sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88
89         /**
90          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
91          * @type Boolean
92          */
93         enableGarbageCollector : true,
94
95         /**
96          * True to automatically purge event listeners after uncaching an element (defaults to false).
97          * Note: this only happens if enableGarbageCollector is true.
98          * @type Boolean
99          */
100         enableListenerCollection:false,
101
102         /**
103          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
104          * the IE insecure content warning (defaults to javascript:false).
105          * @type String
106          */
107         SSL_SECURE_URL : "javascript:false",
108
109         /**
110          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
111          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
112          * @type String
113          */
114         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
115
116         emptyFn : function(){},
117
118         /**
119          * Copies all the properties of config to obj if they don't already exist.
120          * @param {Object} obj The receiver of the properties
121          * @param {Object} config The source of the properties
122          * @return {Object} returns obj
123          */
124         applyIf : function(o, c){
125             if(o && c){
126                 for(var p in c){
127                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
128                 }
129             }
130             return o;
131         },
132
133         /**
134          * Applies event listeners to elements by selectors when the document is ready.
135          * The event name is specified with an @ suffix.
136 <pre><code>
137 Roo.addBehaviors({
138    // add a listener for click on all anchors in element with id foo
139    '#foo a@click' : function(e, t){
140        // do something
141    },
142
143    // add the same listener to multiple selectors (separated by comma BEFORE the @)
144    '#foo a, #bar span.some-class@mouseover' : function(){
145        // do something
146    }
147 });
148 </code></pre>
149          * @param {Object} obj The list of behaviors to apply
150          */
151         addBehaviors : function(o){
152             if(!Roo.isReady){
153                 Roo.onReady(function(){
154                     Roo.addBehaviors(o);
155                 });
156                 return;
157             }
158             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
159             for(var b in o){
160                 var parts = b.split('@');
161                 if(parts[1]){ // for Object prototype breakers
162                     var s = parts[0];
163                     if(!cache[s]){
164                         cache[s] = Roo.select(s);
165                     }
166                     cache[s].on(parts[1], o[b]);
167                 }
168             }
169             cache = null;
170         },
171
172         /**
173          * Generates unique ids. If the element already has an id, it is unchanged
174          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
175          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
176          * @return {String} The generated Id.
177          */
178         id : function(el, prefix){
179             prefix = prefix || "roo-gen";
180             el = Roo.getDom(el);
181             var id = prefix + (++idSeed);
182             return el ? (el.id ? el.id : (el.id = id)) : id;
183         },
184          
185        
186         /**
187          * Extends one class with another class and optionally overrides members with the passed literal. This class
188          * also adds the function "override()" to the class that can be used to override
189          * members on an instance.
190          * @param {Object} subclass The class inheriting the functionality
191          * @param {Object} superclass The class being extended
192          * @param {Object} overrides (optional) A literal with members
193          * @method extend
194          */
195         extend : function(){
196             // inline overrides
197             var io = function(o){
198                 for(var m in o){
199                     this[m] = o[m];
200                 }
201             };
202             return function(sb, sp, overrides){
203                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
204                     overrides = sp;
205                     sp = sb;
206                     sb = function(){sp.apply(this, arguments);};
207                 }
208                 var F = function(){}, sbp, spp = sp.prototype;
209                 F.prototype = spp;
210                 sbp = sb.prototype = new F();
211                 sbp.constructor=sb;
212                 sb.superclass=spp;
213                 
214                 if(spp.constructor == Object.prototype.constructor){
215                     spp.constructor=sp;
216                    
217                 }
218                 
219                 sb.override = function(o){
220                     Roo.override(sb, o);
221                 };
222                 sbp.override = io;
223                 Roo.override(sb, overrides);
224                 return sb;
225             };
226         }(),
227
228         /**
229          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
230          * Usage:<pre><code>
231 Roo.override(MyClass, {
232     newMethod1: function(){
233         // etc.
234     },
235     newMethod2: function(foo){
236         // etc.
237     }
238 });
239  </code></pre>
240          * @param {Object} origclass The class to override
241          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
242          * containing one or more methods.
243          * @method override
244          */
245         override : function(origclass, overrides){
246             if(overrides){
247                 var p = origclass.prototype;
248                 for(var method in overrides){
249                     p[method] = overrides[method];
250                 }
251             }
252         },
253         /**
254          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
255          * <pre><code>
256 Roo.namespace('Company', 'Company.data');
257 Company.Widget = function() { ... }
258 Company.data.CustomStore = function(config) { ... }
259 </code></pre>
260          * @param {String} namespace1
261          * @param {String} namespace2
262          * @param {String} etc
263          * @method namespace
264          */
265         namespace : function(){
266             var a=arguments, o=null, i, j, d, rt;
267             for (i=0; i<a.length; ++i) {
268                 d=a[i].split(".");
269                 rt = d[0];
270                 /** eval:var:o */
271                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
272                 for (j=1; j<d.length; ++j) {
273                     o[d[j]]=o[d[j]] || {};
274                     o=o[d[j]];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
282 Roo.factory(conf, Roo.data);
283 </code></pre>
284          * @param {String} classname
285          * @param {String} namespace (optional)
286          * @method factory
287          */
288          
289         factory : function(c, ns)
290         {
291             // no xtype, no ns or c.xns - or forced off by c.xns
292             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
293                 return c;
294             }
295             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
296             if (c.constructor == ns[c.xtype]) {// already created...
297                 return c;
298             }
299             if (ns[c.xtype]) {
300                 if (Roo.debug) console.log("Roo.Factory(" + c.xtype + ")");
301                 var ret = new ns[c.xtype](c);
302                 ret.xns = false;
303                 return ret;
304             }
305             c.xns = false; // prevent recursion..
306             return c;
307         },
308          
309         /**
310          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
311          * @param {Object} o
312          * @return {String}
313          */
314         urlEncode : function(o){
315             if(!o){
316                 return "";
317             }
318             var buf = [];
319             for(var key in o){
320                 var ov = o[key], k = encodeURIComponent(key);
321                 var type = typeof ov;
322                 if(type == 'undefined'){
323                     buf.push(k, "=&");
324                 }else if(type != "function" && type != "object"){
325                     buf.push(k, "=", encodeURIComponent(ov), "&");
326                 }else if(ov instanceof Array){
327                     if (ov.length) {
328                             for(var i = 0, len = ov.length; i < len; i++) {
329                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
330                             }
331                         } else {
332                             buf.push(k, "=&");
333                         }
334                 }
335             }
336             buf.pop();
337             return buf.join("");
338         },
339
340         /**
341          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
342          * @param {String} string
343          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
344          * @return {Object} A literal with members
345          */
346         urlDecode : function(string, overwrite){
347             if(!string || !string.length){
348                 return {};
349             }
350             var obj = {};
351             var pairs = string.split('&');
352             var pair, name, value;
353             for(var i = 0, len = pairs.length; i < len; i++){
354                 pair = pairs[i].split('=');
355                 name = decodeURIComponent(pair[0]);
356                 value = decodeURIComponent(pair[1]);
357                 if(overwrite !== true){
358                     if(typeof obj[name] == "undefined"){
359                         obj[name] = value;
360                     }else if(typeof obj[name] == "string"){
361                         obj[name] = [obj[name]];
362                         obj[name].push(value);
363                     }else{
364                         obj[name].push(value);
365                     }
366                 }else{
367                     obj[name] = value;
368                 }
369             }
370             return obj;
371         },
372
373         /**
374          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
375          * passed array is not really an array, your function is called once with it.
376          * The supplied function is called with (Object item, Number index, Array allItems).
377          * @param {Array/NodeList/Mixed} array
378          * @param {Function} fn
379          * @param {Object} scope
380          */
381         each : function(array, fn, scope){
382             if(typeof array.length == "undefined" || typeof array == "string"){
383                 array = [array];
384             }
385             for(var i = 0, len = array.length; i < len; i++){
386                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
387             }
388         },
389
390         // deprecated
391         combine : function(){
392             var as = arguments, l = as.length, r = [];
393             for(var i = 0; i < l; i++){
394                 var a = as[i];
395                 if(a instanceof Array){
396                     r = r.concat(a);
397                 }else if(a.length !== undefined && !a.substr){
398                     r = r.concat(Array.prototype.slice.call(a, 0));
399                 }else{
400                     r.push(a);
401                 }
402             }
403             return r;
404         },
405
406         /**
407          * Escapes the passed string for use in a regular expression
408          * @param {String} str
409          * @return {String}
410          */
411         escapeRe : function(s) {
412             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
413         },
414
415         // internal
416         callback : function(cb, scope, args, delay){
417             if(typeof cb == "function"){
418                 if(delay){
419                     cb.defer(delay, scope, args || []);
420                 }else{
421                     cb.apply(scope, args || []);
422                 }
423             }
424         },
425
426         /**
427          * Return the dom node for the passed string (id), dom node, or Roo.Element
428          * @param {String/HTMLElement/Roo.Element} el
429          * @return HTMLElement
430          */
431         getDom : function(el){
432             if(!el){
433                 return null;
434             }
435             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
436         },
437
438         /**
439         * Shorthand for {@link Roo.ComponentMgr#get}
440         * @param {String} id
441         * @return Roo.Component
442         */
443         getCmp : function(id){
444             return Roo.ComponentMgr.get(id);
445         },
446          
447         num : function(v, defaultValue){
448             if(typeof v != 'number'){
449                 return defaultValue;
450             }
451             return v;
452         },
453
454         destroy : function(){
455             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
456                 var as = a[i];
457                 if(as){
458                     if(as.dom){
459                         as.removeAllListeners();
460                         as.remove();
461                         continue;
462                     }
463                     if(typeof as.purgeListeners == 'function'){
464                         as.purgeListeners();
465                     }
466                     if(typeof as.destroy == 'function'){
467                         as.destroy();
468                     }
469                 }
470             }
471         },
472
473         // inpired by a similar function in mootools library
474         /**
475          * Returns the type of object that is passed in. If the object passed in is null or undefined it
476          * return false otherwise it returns one of the following values:<ul>
477          * <li><b>string</b>: If the object passed is a string</li>
478          * <li><b>number</b>: If the object passed is a number</li>
479          * <li><b>boolean</b>: If the object passed is a boolean value</li>
480          * <li><b>function</b>: If the object passed is a function reference</li>
481          * <li><b>object</b>: If the object passed is an object</li>
482          * <li><b>array</b>: If the object passed is an array</li>
483          * <li><b>regexp</b>: If the object passed is a regular expression</li>
484          * <li><b>element</b>: If the object passed is a DOM Element</li>
485          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
486          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
487          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
488          * @param {Mixed} object
489          * @return {String}
490          */
491         type : function(o){
492             if(o === undefined || o === null){
493                 return false;
494             }
495             if(o.htmlElement){
496                 return 'element';
497             }
498             var t = typeof o;
499             if(t == 'object' && o.nodeName) {
500                 switch(o.nodeType) {
501                     case 1: return 'element';
502                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
503                 }
504             }
505             if(t == 'object' || t == 'function') {
506                 switch(o.constructor) {
507                     case Array: return 'array';
508                     case RegExp: return 'regexp';
509                 }
510                 if(typeof o.length == 'number' && typeof o.item == 'function') {
511                     return 'nodelist';
512                 }
513             }
514             return t;
515         },
516
517         /**
518          * Returns true if the passed value is null, undefined or an empty string (optional).
519          * @param {Mixed} value The value to test
520          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
521          * @return {Boolean}
522          */
523         isEmpty : function(v, allowBlank){
524             return v === null || v === undefined || (!allowBlank ? v === '' : false);
525         },
526         
527         /** @type Boolean */
528         isOpera : isOpera,
529         /** @type Boolean */
530         isSafari : isSafari,
531         /** @type Boolean */
532         isIE : isIE,
533         /** @type Boolean */
534         isIE7 : isIE7,
535         /** @type Boolean */
536         isGecko : isGecko,
537         /** @type Boolean */
538         isBorderBox : isBorderBox,
539         /** @type Boolean */
540         isWindows : isWindows,
541         /** @type Boolean */
542         isLinux : isLinux,
543         /** @type Boolean */
544         isMac : isMac,
545
546         /**
547          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
548          * you may want to set this to true.
549          * @type Boolean
550          */
551         useShims : ((isIE && !isIE7) || (isGecko && isMac))
552     });
553
554
555 })();
556
557 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
558                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
559 /*
560  * Based on:
561  * Ext JS Library 1.1.1
562  * Copyright(c) 2006-2007, Ext JS, LLC.
563  *
564  * Originally Released Under LGPL - original licence link has changed is not relivant.
565  *
566  * Fork - LGPL
567  * <script type="text/javascript">
568  */
569
570 (function() {    
571     // wrappedn so fnCleanup is not in global scope...
572     if(Roo.isIE) {
573         function fnCleanUp() {
574             var p = Function.prototype;
575             delete p.createSequence;
576             delete p.defer;
577             delete p.createDelegate;
578             delete p.createCallback;
579             delete p.createInterceptor;
580
581             window.detachEvent("onunload", fnCleanUp);
582         }
583         window.attachEvent("onunload", fnCleanUp);
584     }
585 })();
586
587
588 /**
589  * @class Function
590  * These functions are available on every Function object (any JavaScript function).
591  */
592 Roo.apply(Function.prototype, {
593      /**
594      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
595      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
596      * Will create a function that is bound to those 2 args.
597      * @return {Function} The new function
598     */
599     createCallback : function(/*args...*/){
600         // make args available, in function below
601         var args = arguments;
602         var method = this;
603         return function() {
604             return method.apply(window, args);
605         };
606     },
607
608     /**
609      * Creates a delegate (callback) that sets the scope to obj.
610      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
611      * Will create a function that is automatically scoped to this.
612      * @param {Object} obj (optional) The object for which the scope is set
613      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
614      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
615      *                                             if a number the args are inserted at the specified position
616      * @return {Function} The new function
617      */
618     createDelegate : function(obj, args, appendArgs){
619         var method = this;
620         return function() {
621             var callArgs = args || arguments;
622             if(appendArgs === true){
623                 callArgs = Array.prototype.slice.call(arguments, 0);
624                 callArgs = callArgs.concat(args);
625             }else if(typeof appendArgs == "number"){
626                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
627                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
628                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
629             }
630             return method.apply(obj || window, callArgs);
631         };
632     },
633
634     /**
635      * Calls this function after the number of millseconds specified.
636      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
637      * @param {Object} obj (optional) The object for which the scope is set
638      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
639      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
640      *                                             if a number the args are inserted at the specified position
641      * @return {Number} The timeout id that can be used with clearTimeout
642      */
643     defer : function(millis, obj, args, appendArgs){
644         var fn = this.createDelegate(obj, args, appendArgs);
645         if(millis){
646             return setTimeout(fn, millis);
647         }
648         fn();
649         return 0;
650     },
651     /**
652      * Create a combined function call sequence of the original function + the passed function.
653      * The resulting function returns the results of the original function.
654      * The passed fcn is called with the parameters of the original function
655      * @param {Function} fcn The function to sequence
656      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
657      * @return {Function} The new function
658      */
659     createSequence : function(fcn, scope){
660         if(typeof fcn != "function"){
661             return this;
662         }
663         var method = this;
664         return function() {
665             var retval = method.apply(this || window, arguments);
666             fcn.apply(scope || this || window, arguments);
667             return retval;
668         };
669     },
670
671     /**
672      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
673      * The resulting function returns the results of the original function.
674      * The passed fcn is called with the parameters of the original function.
675      * @addon
676      * @param {Function} fcn The function to call before the original
677      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
678      * @return {Function} The new function
679      */
680     createInterceptor : function(fcn, scope){
681         if(typeof fcn != "function"){
682             return this;
683         }
684         var method = this;
685         return function() {
686             fcn.target = this;
687             fcn.method = method;
688             if(fcn.apply(scope || this || window, arguments) === false){
689                 return;
690             }
691             return method.apply(this || window, arguments);
692         };
693     }
694 });
695 /*
696  * Based on:
697  * Ext JS Library 1.1.1
698  * Copyright(c) 2006-2007, Ext JS, LLC.
699  *
700  * Originally Released Under LGPL - original licence link has changed is not relivant.
701  *
702  * Fork - LGPL
703  * <script type="text/javascript">
704  */
705
706 Roo.applyIf(String, {
707     
708     /** @scope String */
709     
710     /**
711      * Escapes the passed string for ' and \
712      * @param {String} string The string to escape
713      * @return {String} The escaped string
714      * @static
715      */
716     escape : function(string) {
717         return string.replace(/('|\\)/g, "\\$1");
718     },
719
720     /**
721      * Pads the left side of a string with a specified character.  This is especially useful
722      * for normalizing number and date strings.  Example usage:
723      * <pre><code>
724 var s = String.leftPad('123', 5, '0');
725 // s now contains the string: '00123'
726 </code></pre>
727      * @param {String} string The original string
728      * @param {Number} size The total length of the output string
729      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
730      * @return {String} The padded string
731      * @static
732      */
733     leftPad : function (val, size, ch) {
734         var result = new String(val);
735         if(ch === null || ch === undefined || ch === '') {
736             ch = " ";
737         }
738         while (result.length < size) {
739             result = ch + result;
740         }
741         return result;
742     },
743
744     /**
745      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
746      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
747      * <pre><code>
748 var cls = 'my-class', text = 'Some text';
749 var s = String.format('<div class="{0}">{1}</div>', cls, text);
750 // s now contains the string: '<div class="my-class">Some text</div>'
751 </code></pre>
752      * @param {String} string The tokenized string to be formatted
753      * @param {String} value1 The value to replace token {0}
754      * @param {String} value2 Etc...
755      * @return {String} The formatted string
756      * @static
757      */
758     format : function(format){
759         var args = Array.prototype.slice.call(arguments, 1);
760         return format.replace(/\{(\d+)\}/g, function(m, i){
761             return Roo.util.Format.htmlEncode(args[i]);
762         });
763     }
764 });
765
766 /**
767  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
768  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
769  * they are already different, the first value passed in is returned.  Note that this method returns the new value
770  * but does not change the current string.
771  * <pre><code>
772 // alternate sort directions
773 sort = sort.toggle('ASC', 'DESC');
774
775 // instead of conditional logic:
776 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
777 </code></pre>
778  * @param {String} value The value to compare to the current string
779  * @param {String} other The new value to use if the string already equals the first value passed in
780  * @return {String} The new value
781  */
782  
783 String.prototype.toggle = function(value, other){
784     return this == value ? other : value;
785 };/*
786  * Based on:
787  * Ext JS Library 1.1.1
788  * Copyright(c) 2006-2007, Ext JS, LLC.
789  *
790  * Originally Released Under LGPL - original licence link has changed is not relivant.
791  *
792  * Fork - LGPL
793  * <script type="text/javascript">
794  */
795
796  /**
797  * @class Number
798  */
799 Roo.applyIf(Number.prototype, {
800     /**
801      * Checks whether or not the current number is within a desired range.  If the number is already within the
802      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
803      * exceeded.  Note that this method returns the constrained value but does not change the current number.
804      * @param {Number} min The minimum number in the range
805      * @param {Number} max The maximum number in the range
806      * @return {Number} The constrained value if outside the range, otherwise the current value
807      */
808     constrain : function(min, max){
809         return Math.min(Math.max(this, min), max);
810     }
811 });/*
812  * Based on:
813  * Ext JS Library 1.1.1
814  * Copyright(c) 2006-2007, Ext JS, LLC.
815  *
816  * Originally Released Under LGPL - original licence link has changed is not relivant.
817  *
818  * Fork - LGPL
819  * <script type="text/javascript">
820  */
821  /**
822  * @class Array
823  */
824 Roo.applyIf(Array.prototype, {
825     /**
826      * Checks whether or not the specified object exists in the array.
827      * @param {Object} o The object to check for
828      * @return {Number} The index of o in the array (or -1 if it is not found)
829      */
830     indexOf : function(o){
831        for (var i = 0, len = this.length; i < len; i++){
832               if(this[i] == o) return i;
833        }
834            return -1;
835     },
836
837     /**
838      * Removes the specified object from the array.  If the object is not found nothing happens.
839      * @param {Object} o The object to remove
840      */
841     remove : function(o){
842        var index = this.indexOf(o);
843        if(index != -1){
844            this.splice(index, 1);
845        }
846     },
847     /**
848      * Map (JS 1.6 compatibility)
849      * @param {Function} function  to call
850      */
851     map : function(fun )
852     {
853         var len = this.length >>> 0;
854         if (typeof fun != "function")
855             throw new TypeError();
856
857         var res = new Array(len);
858         var thisp = arguments[1];
859         for (var i = 0; i < len; i++)
860         {
861             if (i in this)
862                 res[i] = fun.call(thisp, this[i], i, this);
863         }
864
865         return res;
866     }
867     
868 });
869
870
871  /*
872  * Based on:
873  * Ext JS Library 1.1.1
874  * Copyright(c) 2006-2007, Ext JS, LLC.
875  *
876  * Originally Released Under LGPL - original licence link has changed is not relivant.
877  *
878  * Fork - LGPL
879  * <script type="text/javascript">
880  */
881
882 /**
883  * @class Date
884  *
885  * The date parsing and format syntax is a subset of
886  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
887  * supported will provide results equivalent to their PHP versions.
888  *
889  * Following is the list of all currently supported formats:
890  *<pre>
891 Sample date:
892 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
893
894 Format  Output      Description
895 ------  ----------  --------------------------------------------------------------
896   d      10         Day of the month, 2 digits with leading zeros
897   D      Wed        A textual representation of a day, three letters
898   j      10         Day of the month without leading zeros
899   l      Wednesday  A full textual representation of the day of the week
900   S      th         English ordinal day of month suffix, 2 chars (use with j)
901   w      3          Numeric representation of the day of the week
902   z      9          The julian date, or day of the year (0-365)
903   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
904   F      January    A full textual representation of the month
905   m      01         Numeric representation of a month, with leading zeros
906   M      Jan        Month name abbreviation, three letters
907   n      1          Numeric representation of a month, without leading zeros
908   t      31         Number of days in the given month
909   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
910   Y      2007       A full numeric representation of a year, 4 digits
911   y      07         A two digit representation of a year
912   a      pm         Lowercase Ante meridiem and Post meridiem
913   A      PM         Uppercase Ante meridiem and Post meridiem
914   g      3          12-hour format of an hour without leading zeros
915   G      15         24-hour format of an hour without leading zeros
916   h      03         12-hour format of an hour with leading zeros
917   H      15         24-hour format of an hour with leading zeros
918   i      05         Minutes with leading zeros
919   s      01         Seconds, with leading zeros
920   O      -0600      Difference to Greenwich time (GMT) in hours
921   T      CST        Timezone setting of the machine running the code
922   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
923 </pre>
924  *
925  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
926  * <pre><code>
927 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
928 document.write(dt.format('Y-m-d'));                         //2007-01-10
929 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
930 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
931  </code></pre>
932  *
933  * Here are some standard date/time patterns that you might find helpful.  They
934  * are not part of the source of Date.js, but to use them you can simply copy this
935  * block of code into any script that is included after Date.js and they will also become
936  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
937  * <pre><code>
938 Date.patterns = {
939     ISO8601Long:"Y-m-d H:i:s",
940     ISO8601Short:"Y-m-d",
941     ShortDate: "n/j/Y",
942     LongDate: "l, F d, Y",
943     FullDateTime: "l, F d, Y g:i:s A",
944     MonthDay: "F d",
945     ShortTime: "g:i A",
946     LongTime: "g:i:s A",
947     SortableDateTime: "Y-m-d\\TH:i:s",
948     UniversalSortableDateTime: "Y-m-d H:i:sO",
949     YearMonth: "F, Y"
950 };
951 </code></pre>
952  *
953  * Example usage:
954  * <pre><code>
955 var dt = new Date();
956 document.write(dt.format(Date.patterns.ShortDate));
957  </code></pre>
958  */
959
960 /*
961  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
962  * They generate precompiled functions from date formats instead of parsing and
963  * processing the pattern every time you format a date.  These functions are available
964  * on every Date object (any javascript function).
965  *
966  * The original article and download are here:
967  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
968  *
969  */
970  
971  
972  // was in core
973 /**
974  Returns the number of milliseconds between this date and date
975  @param {Date} date (optional) Defaults to now
976  @return {Number} The diff in milliseconds
977  @member Date getElapsed
978  */
979 Date.prototype.getElapsed = function(date) {
980         return Math.abs((date || new Date()).getTime()-this.getTime());
981 };
982 // was in date file..
983
984
985 // private
986 Date.parseFunctions = {count:0};
987 // private
988 Date.parseRegexes = [];
989 // private
990 Date.formatFunctions = {count:0};
991
992 // private
993 Date.prototype.dateFormat = function(format) {
994     if (Date.formatFunctions[format] == null) {
995         Date.createNewFormat(format);
996     }
997     var func = Date.formatFunctions[format];
998     return this[func]();
999 };
1000
1001
1002 /**
1003  * Formats a date given the supplied format string
1004  * @param {String} format The format string
1005  * @return {String} The formatted date
1006  * @method
1007  */
1008 Date.prototype.format = Date.prototype.dateFormat;
1009
1010 // private
1011 Date.createNewFormat = function(format) {
1012     var funcName = "format" + Date.formatFunctions.count++;
1013     Date.formatFunctions[format] = funcName;
1014     var code = "Date.prototype." + funcName + " = function(){return ";
1015     var special = false;
1016     var ch = '';
1017     for (var i = 0; i < format.length; ++i) {
1018         ch = format.charAt(i);
1019         if (!special && ch == "\\") {
1020             special = true;
1021         }
1022         else if (special) {
1023             special = false;
1024             code += "'" + String.escape(ch) + "' + ";
1025         }
1026         else {
1027             code += Date.getFormatCode(ch);
1028         }
1029     }
1030     /** eval:var:zzzzzzzzzzzzz */
1031     eval(code.substring(0, code.length - 3) + ";}");
1032 };
1033
1034 // private
1035 Date.getFormatCode = function(character) {
1036     switch (character) {
1037     case "d":
1038         return "String.leftPad(this.getDate(), 2, '0') + ";
1039     case "D":
1040         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1041     case "j":
1042         return "this.getDate() + ";
1043     case "l":
1044         return "Date.dayNames[this.getDay()] + ";
1045     case "S":
1046         return "this.getSuffix() + ";
1047     case "w":
1048         return "this.getDay() + ";
1049     case "z":
1050         return "this.getDayOfYear() + ";
1051     case "W":
1052         return "this.getWeekOfYear() + ";
1053     case "F":
1054         return "Date.monthNames[this.getMonth()] + ";
1055     case "m":
1056         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1057     case "M":
1058         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1059     case "n":
1060         return "(this.getMonth() + 1) + ";
1061     case "t":
1062         return "this.getDaysInMonth() + ";
1063     case "L":
1064         return "(this.isLeapYear() ? 1 : 0) + ";
1065     case "Y":
1066         return "this.getFullYear() + ";
1067     case "y":
1068         return "('' + this.getFullYear()).substring(2, 4) + ";
1069     case "a":
1070         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1071     case "A":
1072         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1073     case "g":
1074         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1075     case "G":
1076         return "this.getHours() + ";
1077     case "h":
1078         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1079     case "H":
1080         return "String.leftPad(this.getHours(), 2, '0') + ";
1081     case "i":
1082         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1083     case "s":
1084         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1085     case "O":
1086         return "this.getGMTOffset() + ";
1087     case "T":
1088         return "this.getTimezone() + ";
1089     case "Z":
1090         return "(this.getTimezoneOffset() * -60) + ";
1091     default:
1092         return "'" + String.escape(character) + "' + ";
1093     }
1094 };
1095
1096 /**
1097  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1098  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1099  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1100  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1101  * string or the parse operation will fail.
1102  * Example Usage:
1103 <pre><code>
1104 //dt = Fri May 25 2007 (current date)
1105 var dt = new Date();
1106
1107 //dt = Thu May 25 2006 (today's month/day in 2006)
1108 dt = Date.parseDate("2006", "Y");
1109
1110 //dt = Sun Jan 15 2006 (all date parts specified)
1111 dt = Date.parseDate("2006-1-15", "Y-m-d");
1112
1113 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1114 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1115 </code></pre>
1116  * @param {String} input The unparsed date as a string
1117  * @param {String} format The format the date is in
1118  * @return {Date} The parsed date
1119  * @static
1120  */
1121 Date.parseDate = function(input, format) {
1122     if (Date.parseFunctions[format] == null) {
1123         Date.createParser(format);
1124     }
1125     var func = Date.parseFunctions[format];
1126     return Date[func](input);
1127 };
1128 /**
1129  * @private
1130  */
1131 Date.createParser = function(format) {
1132     var funcName = "parse" + Date.parseFunctions.count++;
1133     var regexNum = Date.parseRegexes.length;
1134     var currentGroup = 1;
1135     Date.parseFunctions[format] = funcName;
1136
1137     var code = "Date." + funcName + " = function(input){\n"
1138         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1139         + "var d = new Date();\n"
1140         + "y = d.getFullYear();\n"
1141         + "m = d.getMonth();\n"
1142         + "d = d.getDate();\n"
1143         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1144         + "if (results && results.length > 0) {";
1145     var regex = "";
1146
1147     var special = false;
1148     var ch = '';
1149     for (var i = 0; i < format.length; ++i) {
1150         ch = format.charAt(i);
1151         if (!special && ch == "\\") {
1152             special = true;
1153         }
1154         else if (special) {
1155             special = false;
1156             regex += String.escape(ch);
1157         }
1158         else {
1159             var obj = Date.formatCodeToRegex(ch, currentGroup);
1160             currentGroup += obj.g;
1161             regex += obj.s;
1162             if (obj.g && obj.c) {
1163                 code += obj.c;
1164             }
1165         }
1166     }
1167
1168     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1169         + "{v = new Date(y, m, d, h, i, s);}\n"
1170         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1171         + "{v = new Date(y, m, d, h, i);}\n"
1172         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1173         + "{v = new Date(y, m, d, h);}\n"
1174         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1175         + "{v = new Date(y, m, d);}\n"
1176         + "else if (y >= 0 && m >= 0)\n"
1177         + "{v = new Date(y, m);}\n"
1178         + "else if (y >= 0)\n"
1179         + "{v = new Date(y);}\n"
1180         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1181         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1182         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1183         + ";}";
1184
1185     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1186     /** eval:var:zzzzzzzzzzzzz */
1187     eval(code);
1188 };
1189
1190 // private
1191 Date.formatCodeToRegex = function(character, currentGroup) {
1192     switch (character) {
1193     case "D":
1194         return {g:0,
1195         c:null,
1196         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1197     case "j":
1198         return {g:1,
1199             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1200             s:"(\\d{1,2})"}; // day of month without leading zeroes
1201     case "d":
1202         return {g:1,
1203             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1204             s:"(\\d{2})"}; // day of month with leading zeroes
1205     case "l":
1206         return {g:0,
1207             c:null,
1208             s:"(?:" + Date.dayNames.join("|") + ")"};
1209     case "S":
1210         return {g:0,
1211             c:null,
1212             s:"(?:st|nd|rd|th)"};
1213     case "w":
1214         return {g:0,
1215             c:null,
1216             s:"\\d"};
1217     case "z":
1218         return {g:0,
1219             c:null,
1220             s:"(?:\\d{1,3})"};
1221     case "W":
1222         return {g:0,
1223             c:null,
1224             s:"(?:\\d{2})"};
1225     case "F":
1226         return {g:1,
1227             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1228             s:"(" + Date.monthNames.join("|") + ")"};
1229     case "M":
1230         return {g:1,
1231             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1232             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1233     case "n":
1234         return {g:1,
1235             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1236             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1237     case "m":
1238         return {g:1,
1239             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1240             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1241     case "t":
1242         return {g:0,
1243             c:null,
1244             s:"\\d{1,2}"};
1245     case "L":
1246         return {g:0,
1247             c:null,
1248             s:"(?:1|0)"};
1249     case "Y":
1250         return {g:1,
1251             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1252             s:"(\\d{4})"};
1253     case "y":
1254         return {g:1,
1255             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1256                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1257             s:"(\\d{1,2})"};
1258     case "a":
1259         return {g:1,
1260             c:"if (results[" + currentGroup + "] == 'am') {\n"
1261                 + "if (h == 12) { h = 0; }\n"
1262                 + "} else { if (h < 12) { h += 12; }}",
1263             s:"(am|pm)"};
1264     case "A":
1265         return {g:1,
1266             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1267                 + "if (h == 12) { h = 0; }\n"
1268                 + "} else { if (h < 12) { h += 12; }}",
1269             s:"(AM|PM)"};
1270     case "g":
1271     case "G":
1272         return {g:1,
1273             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1274             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1275     case "h":
1276     case "H":
1277         return {g:1,
1278             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1279             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1280     case "i":
1281         return {g:1,
1282             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1283             s:"(\\d{2})"};
1284     case "s":
1285         return {g:1,
1286             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{2})"};
1288     case "O":
1289         return {g:1,
1290             c:[
1291                 "o = results[", currentGroup, "];\n",
1292                 "var sn = o.substring(0,1);\n", // get + / - sign
1293                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1294                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1295                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1296                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1297             ].join(""),
1298             s:"([+\-]\\d{4})"};
1299     case "T":
1300         return {g:0,
1301             c:null,
1302             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1303     case "Z":
1304         return {g:1,
1305             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1306                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1307             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1308     default:
1309         return {g:0,
1310             c:null,
1311             s:String.escape(character)};
1312     }
1313 };
1314
1315 /**
1316  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1317  * @return {String} The abbreviated timezone name (e.g. 'CST')
1318  */
1319 Date.prototype.getTimezone = function() {
1320     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1321 };
1322
1323 /**
1324  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1325  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1326  */
1327 Date.prototype.getGMTOffset = function() {
1328     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1329         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1330         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1331 };
1332
1333 /**
1334  * Get the numeric day number of the year, adjusted for leap year.
1335  * @return {Number} 0 through 364 (365 in leap years)
1336  */
1337 Date.prototype.getDayOfYear = function() {
1338     var num = 0;
1339     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1340     for (var i = 0; i < this.getMonth(); ++i) {
1341         num += Date.daysInMonth[i];
1342     }
1343     return num + this.getDate() - 1;
1344 };
1345
1346 /**
1347  * Get the string representation of the numeric week number of the year
1348  * (equivalent to the format specifier 'W').
1349  * @return {String} '00' through '52'
1350  */
1351 Date.prototype.getWeekOfYear = function() {
1352     // Skip to Thursday of this week
1353     var now = this.getDayOfYear() + (4 - this.getDay());
1354     // Find the first Thursday of the year
1355     var jan1 = new Date(this.getFullYear(), 0, 1);
1356     var then = (7 - jan1.getDay() + 4);
1357     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1358 };
1359
1360 /**
1361  * Whether or not the current date is in a leap year.
1362  * @return {Boolean} True if the current date is in a leap year, else false
1363  */
1364 Date.prototype.isLeapYear = function() {
1365     var year = this.getFullYear();
1366     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1367 };
1368
1369 /**
1370  * Get the first day of the current month, adjusted for leap year.  The returned value
1371  * is the numeric day index within the week (0-6) which can be used in conjunction with
1372  * the {@link #monthNames} array to retrieve the textual day name.
1373  * Example:
1374  *<pre><code>
1375 var dt = new Date('1/10/2007');
1376 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1377 </code></pre>
1378  * @return {Number} The day number (0-6)
1379  */
1380 Date.prototype.getFirstDayOfMonth = function() {
1381     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1382     return (day < 0) ? (day + 7) : day;
1383 };
1384
1385 /**
1386  * Get the last day of the current month, adjusted for leap year.  The returned value
1387  * is the numeric day index within the week (0-6) which can be used in conjunction with
1388  * the {@link #monthNames} array to retrieve the textual day name.
1389  * Example:
1390  *<pre><code>
1391 var dt = new Date('1/10/2007');
1392 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1393 </code></pre>
1394  * @return {Number} The day number (0-6)
1395  */
1396 Date.prototype.getLastDayOfMonth = function() {
1397     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1398     return (day < 0) ? (day + 7) : day;
1399 };
1400
1401
1402 /**
1403  * Get the first date of this date's month
1404  * @return {Date}
1405  */
1406 Date.prototype.getFirstDateOfMonth = function() {
1407     return new Date(this.getFullYear(), this.getMonth(), 1);
1408 };
1409
1410 /**
1411  * Get the last date of this date's month
1412  * @return {Date}
1413  */
1414 Date.prototype.getLastDateOfMonth = function() {
1415     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1416 };
1417 /**
1418  * Get the number of days in the current month, adjusted for leap year.
1419  * @return {Number} The number of days in the month
1420  */
1421 Date.prototype.getDaysInMonth = function() {
1422     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1423     return Date.daysInMonth[this.getMonth()];
1424 };
1425
1426 /**
1427  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1428  * @return {String} 'st, 'nd', 'rd' or 'th'
1429  */
1430 Date.prototype.getSuffix = function() {
1431     switch (this.getDate()) {
1432         case 1:
1433         case 21:
1434         case 31:
1435             return "st";
1436         case 2:
1437         case 22:
1438             return "nd";
1439         case 3:
1440         case 23:
1441             return "rd";
1442         default:
1443             return "th";
1444     }
1445 };
1446
1447 // private
1448 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1449
1450 /**
1451  * An array of textual month names.
1452  * Override these values for international dates, for example...
1453  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1454  * @type Array
1455  * @static
1456  */
1457 Date.monthNames =
1458    ["January",
1459     "February",
1460     "March",
1461     "April",
1462     "May",
1463     "June",
1464     "July",
1465     "August",
1466     "September",
1467     "October",
1468     "November",
1469     "December"];
1470
1471 /**
1472  * An array of textual day names.
1473  * Override these values for international dates, for example...
1474  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1475  * @type Array
1476  * @static
1477  */
1478 Date.dayNames =
1479    ["Sunday",
1480     "Monday",
1481     "Tuesday",
1482     "Wednesday",
1483     "Thursday",
1484     "Friday",
1485     "Saturday"];
1486
1487 // private
1488 Date.y2kYear = 50;
1489 // private
1490 Date.monthNumbers = {
1491     Jan:0,
1492     Feb:1,
1493     Mar:2,
1494     Apr:3,
1495     May:4,
1496     Jun:5,
1497     Jul:6,
1498     Aug:7,
1499     Sep:8,
1500     Oct:9,
1501     Nov:10,
1502     Dec:11};
1503
1504 /**
1505  * Creates and returns a new Date instance with the exact same date value as the called instance.
1506  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1507  * variable will also be changed.  When the intention is to create a new variable that will not
1508  * modify the original instance, you should create a clone.
1509  *
1510  * Example of correctly cloning a date:
1511  * <pre><code>
1512 //wrong way:
1513 var orig = new Date('10/1/2006');
1514 var copy = orig;
1515 copy.setDate(5);
1516 document.write(orig);  //returns 'Thu Oct 05 2006'!
1517
1518 //correct way:
1519 var orig = new Date('10/1/2006');
1520 var copy = orig.clone();
1521 copy.setDate(5);
1522 document.write(orig);  //returns 'Thu Oct 01 2006'
1523 </code></pre>
1524  * @return {Date} The new Date instance
1525  */
1526 Date.prototype.clone = function() {
1527         return new Date(this.getTime());
1528 };
1529
1530 /**
1531  * Clears any time information from this date
1532  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1533  @return {Date} this or the clone
1534  */
1535 Date.prototype.clearTime = function(clone){
1536     if(clone){
1537         return this.clone().clearTime();
1538     }
1539     this.setHours(0);
1540     this.setMinutes(0);
1541     this.setSeconds(0);
1542     this.setMilliseconds(0);
1543     return this;
1544 };
1545
1546 // private
1547 // safari setMonth is broken
1548 if(Roo.isSafari){
1549     Date.brokenSetMonth = Date.prototype.setMonth;
1550         Date.prototype.setMonth = function(num){
1551                 if(num <= -1){
1552                         var n = Math.ceil(-num);
1553                         var back_year = Math.ceil(n/12);
1554                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1555                         this.setFullYear(this.getFullYear() - back_year);
1556                         return Date.brokenSetMonth.call(this, month);
1557                 } else {
1558                         return Date.brokenSetMonth.apply(this, arguments);
1559                 }
1560         };
1561 }
1562
1563 /** Date interval constant 
1564 * @static 
1565 * @type String */
1566 Date.MILLI = "ms";
1567 /** Date interval constant 
1568 * @static 
1569 * @type String */
1570 Date.SECOND = "s";
1571 /** Date interval constant 
1572 * @static 
1573 * @type String */
1574 Date.MINUTE = "mi";
1575 /** Date interval constant 
1576 * @static 
1577 * @type String */
1578 Date.HOUR = "h";
1579 /** Date interval constant 
1580 * @static 
1581 * @type String */
1582 Date.DAY = "d";
1583 /** Date interval constant 
1584 * @static 
1585 * @type String */
1586 Date.MONTH = "mo";
1587 /** Date interval constant 
1588 * @static 
1589 * @type String */
1590 Date.YEAR = "y";
1591
1592 /**
1593  * Provides a convenient method of performing basic date arithmetic.  This method
1594  * does not modify the Date instance being called - it creates and returns
1595  * a new Date instance containing the resulting date value.
1596  *
1597  * Examples:
1598  * <pre><code>
1599 //Basic usage:
1600 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1601 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1602
1603 //Negative values will subtract correctly:
1604 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1605 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1606
1607 //You can even chain several calls together in one line!
1608 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1609 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1610  </code></pre>
1611  *
1612  * @param {String} interval   A valid date interval enum value
1613  * @param {Number} value      The amount to add to the current date
1614  * @return {Date} The new Date instance
1615  */
1616 Date.prototype.add = function(interval, value){
1617   var d = this.clone();
1618   if (!interval || value === 0) return d;
1619   switch(interval.toLowerCase()){
1620     case Date.MILLI:
1621       d.setMilliseconds(this.getMilliseconds() + value);
1622       break;
1623     case Date.SECOND:
1624       d.setSeconds(this.getSeconds() + value);
1625       break;
1626     case Date.MINUTE:
1627       d.setMinutes(this.getMinutes() + value);
1628       break;
1629     case Date.HOUR:
1630       d.setHours(this.getHours() + value);
1631       break;
1632     case Date.DAY:
1633       d.setDate(this.getDate() + value);
1634       break;
1635     case Date.MONTH:
1636       var day = this.getDate();
1637       if(day > 28){
1638           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1639       }
1640       d.setDate(day);
1641       d.setMonth(this.getMonth() + value);
1642       break;
1643     case Date.YEAR:
1644       d.setFullYear(this.getFullYear() + value);
1645       break;
1646   }
1647   return d;
1648 };/*
1649  * Based on:
1650  * Ext JS Library 1.1.1
1651  * Copyright(c) 2006-2007, Ext JS, LLC.
1652  *
1653  * Originally Released Under LGPL - original licence link has changed is not relivant.
1654  *
1655  * Fork - LGPL
1656  * <script type="text/javascript">
1657  */
1658
1659 Roo.lib.Dom = {
1660     getViewWidth : function(full) {
1661         return full ? this.getDocumentWidth() : this.getViewportWidth();
1662     },
1663
1664     getViewHeight : function(full) {
1665         return full ? this.getDocumentHeight() : this.getViewportHeight();
1666     },
1667
1668     getDocumentHeight: function() {
1669         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1670         return Math.max(scrollHeight, this.getViewportHeight());
1671     },
1672
1673     getDocumentWidth: function() {
1674         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1675         return Math.max(scrollWidth, this.getViewportWidth());
1676     },
1677
1678     getViewportHeight: function() {
1679         var height = self.innerHeight;
1680         var mode = document.compatMode;
1681
1682         if ((mode || Roo.isIE) && !Roo.isOpera) {
1683             height = (mode == "CSS1Compat") ?
1684                      document.documentElement.clientHeight :
1685                      document.body.clientHeight;
1686         }
1687
1688         return height;
1689     },
1690
1691     getViewportWidth: function() {
1692         var width = self.innerWidth;
1693         var mode = document.compatMode;
1694
1695         if (mode || Roo.isIE) {
1696             width = (mode == "CSS1Compat") ?
1697                     document.documentElement.clientWidth :
1698                     document.body.clientWidth;
1699         }
1700         return width;
1701     },
1702
1703     isAncestor : function(p, c) {
1704         p = Roo.getDom(p);
1705         c = Roo.getDom(c);
1706         if (!p || !c) {
1707             return false;
1708         }
1709
1710         if (p.contains && !Roo.isSafari) {
1711             return p.contains(c);
1712         } else if (p.compareDocumentPosition) {
1713             return !!(p.compareDocumentPosition(c) & 16);
1714         } else {
1715             var parent = c.parentNode;
1716             while (parent) {
1717                 if (parent == p) {
1718                     return true;
1719                 }
1720                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1721                     return false;
1722                 }
1723                 parent = parent.parentNode;
1724             }
1725             return false;
1726         }
1727     },
1728
1729     getRegion : function(el) {
1730         return Roo.lib.Region.getRegion(el);
1731     },
1732
1733     getY : function(el) {
1734         return this.getXY(el)[1];
1735     },
1736
1737     getX : function(el) {
1738         return this.getXY(el)[0];
1739     },
1740
1741     getXY : function(el) {
1742         var p, pe, b, scroll, bd = document.body;
1743         el = Roo.getDom(el);
1744         var fly = Roo.lib.AnimBase.fly;
1745         if (el.getBoundingClientRect) {
1746             b = el.getBoundingClientRect();
1747             scroll = fly(document).getScroll();
1748             return [b.left + scroll.left, b.top + scroll.top];
1749         }
1750         var x = 0, y = 0;
1751
1752         p = el;
1753
1754         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1755
1756         while (p) {
1757
1758             x += p.offsetLeft;
1759             y += p.offsetTop;
1760
1761             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1762                 hasAbsolute = true;
1763             }
1764
1765             if (Roo.isGecko) {
1766                 pe = fly(p);
1767
1768                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1769                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1770
1771
1772                 x += bl;
1773                 y += bt;
1774
1775
1776                 if (p != el && pe.getStyle('overflow') != 'visible') {
1777                     x += bl;
1778                     y += bt;
1779                 }
1780             }
1781             p = p.offsetParent;
1782         }
1783
1784         if (Roo.isSafari && hasAbsolute) {
1785             x -= bd.offsetLeft;
1786             y -= bd.offsetTop;
1787         }
1788
1789         if (Roo.isGecko && !hasAbsolute) {
1790             var dbd = fly(bd);
1791             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1792             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1793         }
1794
1795         p = el.parentNode;
1796         while (p && p != bd) {
1797             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1798                 x -= p.scrollLeft;
1799                 y -= p.scrollTop;
1800             }
1801             p = p.parentNode;
1802         }
1803         return [x, y];
1804     },
1805  
1806   
1807
1808
1809     setXY : function(el, xy) {
1810         el = Roo.fly(el, '_setXY');
1811         el.position();
1812         var pts = el.translatePoints(xy);
1813         if (xy[0] !== false) {
1814             el.dom.style.left = pts.left + "px";
1815         }
1816         if (xy[1] !== false) {
1817             el.dom.style.top = pts.top + "px";
1818         }
1819     },
1820
1821     setX : function(el, x) {
1822         this.setXY(el, [x, false]);
1823     },
1824
1825     setY : function(el, y) {
1826         this.setXY(el, [false, y]);
1827     }
1828 };
1829 /*
1830  * Portions of this file are based on pieces of Yahoo User Interface Library
1831  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1832  * YUI licensed under the BSD License:
1833  * http://developer.yahoo.net/yui/license.txt
1834  * <script type="text/javascript">
1835  *
1836  */
1837
1838 Roo.lib.Event = function() {
1839     var loadComplete = false;
1840     var listeners = [];
1841     var unloadListeners = [];
1842     var retryCount = 0;
1843     var onAvailStack = [];
1844     var counter = 0;
1845     var lastError = null;
1846
1847     return {
1848         POLL_RETRYS: 200,
1849         POLL_INTERVAL: 20,
1850         EL: 0,
1851         TYPE: 1,
1852         FN: 2,
1853         WFN: 3,
1854         OBJ: 3,
1855         ADJ_SCOPE: 4,
1856         _interval: null,
1857
1858         startInterval: function() {
1859             if (!this._interval) {
1860                 var self = this;
1861                 var callback = function() {
1862                     self._tryPreloadAttach();
1863                 };
1864                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1865
1866             }
1867         },
1868
1869         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1870             onAvailStack.push({ id:         p_id,
1871                 fn:         p_fn,
1872                 obj:        p_obj,
1873                 override:   p_override,
1874                 checkReady: false    });
1875
1876             retryCount = this.POLL_RETRYS;
1877             this.startInterval();
1878         },
1879
1880
1881         addListener: function(el, eventName, fn) {
1882             el = Roo.getDom(el);
1883             if (!el || !fn) {
1884                 return false;
1885             }
1886
1887             if ("unload" == eventName) {
1888                 unloadListeners[unloadListeners.length] =
1889                 [el, eventName, fn];
1890                 return true;
1891             }
1892
1893             var wrappedFn = function(e) {
1894                 return fn(Roo.lib.Event.getEvent(e));
1895             };
1896
1897             var li = [el, eventName, fn, wrappedFn];
1898
1899             var index = listeners.length;
1900             listeners[index] = li;
1901
1902             this.doAdd(el, eventName, wrappedFn, false);
1903             return true;
1904
1905         },
1906
1907
1908         removeListener: function(el, eventName, fn) {
1909             var i, len;
1910
1911             el = Roo.getDom(el);
1912
1913             if(!fn) {
1914                 return this.purgeElement(el, false, eventName);
1915             }
1916
1917
1918             if ("unload" == eventName) {
1919
1920                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1921                     var li = unloadListeners[i];
1922                     if (li &&
1923                         li[0] == el &&
1924                         li[1] == eventName &&
1925                         li[2] == fn) {
1926                         unloadListeners.splice(i, 1);
1927                         return true;
1928                     }
1929                 }
1930
1931                 return false;
1932             }
1933
1934             var cacheItem = null;
1935
1936
1937             var index = arguments[3];
1938
1939             if ("undefined" == typeof index) {
1940                 index = this._getCacheIndex(el, eventName, fn);
1941             }
1942
1943             if (index >= 0) {
1944                 cacheItem = listeners[index];
1945             }
1946
1947             if (!el || !cacheItem) {
1948                 return false;
1949             }
1950
1951             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1952
1953             delete listeners[index][this.WFN];
1954             delete listeners[index][this.FN];
1955             listeners.splice(index, 1);
1956
1957             return true;
1958
1959         },
1960
1961
1962         getTarget: function(ev, resolveTextNode) {
1963             ev = ev.browserEvent || ev;
1964             var t = ev.target || ev.srcElement;
1965             return this.resolveTextNode(t);
1966         },
1967
1968
1969         resolveTextNode: function(node) {
1970             if (Roo.isSafari && node && 3 == node.nodeType) {
1971                 return node.parentNode;
1972             } else {
1973                 return node;
1974             }
1975         },
1976
1977
1978         getPageX: function(ev) {
1979             ev = ev.browserEvent || ev;
1980             var x = ev.pageX;
1981             if (!x && 0 !== x) {
1982                 x = ev.clientX || 0;
1983
1984                 if (Roo.isIE) {
1985                     x += this.getScroll()[1];
1986                 }
1987             }
1988
1989             return x;
1990         },
1991
1992
1993         getPageY: function(ev) {
1994             ev = ev.browserEvent || ev;
1995             var y = ev.pageY;
1996             if (!y && 0 !== y) {
1997                 y = ev.clientY || 0;
1998
1999                 if (Roo.isIE) {
2000                     y += this.getScroll()[0];
2001                 }
2002             }
2003
2004
2005             return y;
2006         },
2007
2008
2009         getXY: function(ev) {
2010             ev = ev.browserEvent || ev;
2011             return [this.getPageX(ev), this.getPageY(ev)];
2012         },
2013
2014
2015         getRelatedTarget: function(ev) {
2016             ev = ev.browserEvent || ev;
2017             var t = ev.relatedTarget;
2018             if (!t) {
2019                 if (ev.type == "mouseout") {
2020                     t = ev.toElement;
2021                 } else if (ev.type == "mouseover") {
2022                     t = ev.fromElement;
2023                 }
2024             }
2025
2026             return this.resolveTextNode(t);
2027         },
2028
2029
2030         getTime: function(ev) {
2031             ev = ev.browserEvent || ev;
2032             if (!ev.time) {
2033                 var t = new Date().getTime();
2034                 try {
2035                     ev.time = t;
2036                 } catch(ex) {
2037                     this.lastError = ex;
2038                     return t;
2039                 }
2040             }
2041
2042             return ev.time;
2043         },
2044
2045
2046         stopEvent: function(ev) {
2047             this.stopPropagation(ev);
2048             this.preventDefault(ev);
2049         },
2050
2051
2052         stopPropagation: function(ev) {
2053             ev = ev.browserEvent || ev;
2054             if (ev.stopPropagation) {
2055                 ev.stopPropagation();
2056             } else {
2057                 ev.cancelBubble = true;
2058             }
2059         },
2060
2061
2062         preventDefault: function(ev) {
2063             ev = ev.browserEvent || ev;
2064             if(ev.preventDefault) {
2065                 ev.preventDefault();
2066             } else {
2067                 ev.returnValue = false;
2068             }
2069         },
2070
2071
2072         getEvent: function(e) {
2073             var ev = e || window.event;
2074             if (!ev) {
2075                 var c = this.getEvent.caller;
2076                 while (c) {
2077                     ev = c.arguments[0];
2078                     if (ev && Event == ev.constructor) {
2079                         break;
2080                     }
2081                     c = c.caller;
2082                 }
2083             }
2084             return ev;
2085         },
2086
2087
2088         getCharCode: function(ev) {
2089             ev = ev.browserEvent || ev;
2090             return ev.charCode || ev.keyCode || 0;
2091         },
2092
2093
2094         _getCacheIndex: function(el, eventName, fn) {
2095             for (var i = 0,len = listeners.length; i < len; ++i) {
2096                 var li = listeners[i];
2097                 if (li &&
2098                     li[this.FN] == fn &&
2099                     li[this.EL] == el &&
2100                     li[this.TYPE] == eventName) {
2101                     return i;
2102                 }
2103             }
2104
2105             return -1;
2106         },
2107
2108
2109         elCache: {},
2110
2111
2112         getEl: function(id) {
2113             return document.getElementById(id);
2114         },
2115
2116
2117         clearCache: function() {
2118         },
2119
2120
2121         _load: function(e) {
2122             loadComplete = true;
2123             var EU = Roo.lib.Event;
2124
2125
2126             if (Roo.isIE) {
2127                 EU.doRemove(window, "load", EU._load);
2128             }
2129         },
2130
2131
2132         _tryPreloadAttach: function() {
2133
2134             if (this.locked) {
2135                 return false;
2136             }
2137
2138             this.locked = true;
2139
2140
2141             var tryAgain = !loadComplete;
2142             if (!tryAgain) {
2143                 tryAgain = (retryCount > 0);
2144             }
2145
2146
2147             var notAvail = [];
2148             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2149                 var item = onAvailStack[i];
2150                 if (item) {
2151                     var el = this.getEl(item.id);
2152
2153                     if (el) {
2154                         if (!item.checkReady ||
2155                             loadComplete ||
2156                             el.nextSibling ||
2157                             (document && document.body)) {
2158
2159                             var scope = el;
2160                             if (item.override) {
2161                                 if (item.override === true) {
2162                                     scope = item.obj;
2163                                 } else {
2164                                     scope = item.override;
2165                                 }
2166                             }
2167                             item.fn.call(scope, item.obj);
2168                             onAvailStack[i] = null;
2169                         }
2170                     } else {
2171                         notAvail.push(item);
2172                     }
2173                 }
2174             }
2175
2176             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2177
2178             if (tryAgain) {
2179
2180                 this.startInterval();
2181             } else {
2182                 clearInterval(this._interval);
2183                 this._interval = null;
2184             }
2185
2186             this.locked = false;
2187
2188             return true;
2189
2190         },
2191
2192
2193         purgeElement: function(el, recurse, eventName) {
2194             var elListeners = this.getListeners(el, eventName);
2195             if (elListeners) {
2196                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2197                     var l = elListeners[i];
2198                     this.removeListener(el, l.type, l.fn);
2199                 }
2200             }
2201
2202             if (recurse && el && el.childNodes) {
2203                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2204                     this.purgeElement(el.childNodes[i], recurse, eventName);
2205                 }
2206             }
2207         },
2208
2209
2210         getListeners: function(el, eventName) {
2211             var results = [], searchLists;
2212             if (!eventName) {
2213                 searchLists = [listeners, unloadListeners];
2214             } else if (eventName == "unload") {
2215                 searchLists = [unloadListeners];
2216             } else {
2217                 searchLists = [listeners];
2218             }
2219
2220             for (var j = 0; j < searchLists.length; ++j) {
2221                 var searchList = searchLists[j];
2222                 if (searchList && searchList.length > 0) {
2223                     for (var i = 0,len = searchList.length; i < len; ++i) {
2224                         var l = searchList[i];
2225                         if (l && l[this.EL] === el &&
2226                             (!eventName || eventName === l[this.TYPE])) {
2227                             results.push({
2228                                 type:   l[this.TYPE],
2229                                 fn:     l[this.FN],
2230                                 obj:    l[this.OBJ],
2231                                 adjust: l[this.ADJ_SCOPE],
2232                                 index:  i
2233                             });
2234                         }
2235                     }
2236                 }
2237             }
2238
2239             return (results.length) ? results : null;
2240         },
2241
2242
2243         _unload: function(e) {
2244
2245             var EU = Roo.lib.Event, i, j, l, len, index;
2246
2247             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2248                 l = unloadListeners[i];
2249                 if (l) {
2250                     var scope = window;
2251                     if (l[EU.ADJ_SCOPE]) {
2252                         if (l[EU.ADJ_SCOPE] === true) {
2253                             scope = l[EU.OBJ];
2254                         } else {
2255                             scope = l[EU.ADJ_SCOPE];
2256                         }
2257                     }
2258                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2259                     unloadListeners[i] = null;
2260                     l = null;
2261                     scope = null;
2262                 }
2263             }
2264
2265             unloadListeners = null;
2266
2267             if (listeners && listeners.length > 0) {
2268                 j = listeners.length;
2269                 while (j) {
2270                     index = j - 1;
2271                     l = listeners[index];
2272                     if (l) {
2273                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2274                                 l[EU.FN], index);
2275                     }
2276                     j = j - 1;
2277                 }
2278                 l = null;
2279
2280                 EU.clearCache();
2281             }
2282
2283             EU.doRemove(window, "unload", EU._unload);
2284
2285         },
2286
2287
2288         getScroll: function() {
2289             var dd = document.documentElement, db = document.body;
2290             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2291                 return [dd.scrollTop, dd.scrollLeft];
2292             } else if (db) {
2293                 return [db.scrollTop, db.scrollLeft];
2294             } else {
2295                 return [0, 0];
2296             }
2297         },
2298
2299
2300         doAdd: function () {
2301             if (window.addEventListener) {
2302                 return function(el, eventName, fn, capture) {
2303                     el.addEventListener(eventName, fn, (capture));
2304                 };
2305             } else if (window.attachEvent) {
2306                 return function(el, eventName, fn, capture) {
2307                     el.attachEvent("on" + eventName, fn);
2308                 };
2309             } else {
2310                 return function() {
2311                 };
2312             }
2313         }(),
2314
2315
2316         doRemove: function() {
2317             if (window.removeEventListener) {
2318                 return function (el, eventName, fn, capture) {
2319                     el.removeEventListener(eventName, fn, (capture));
2320                 };
2321             } else if (window.detachEvent) {
2322                 return function (el, eventName, fn) {
2323                     el.detachEvent("on" + eventName, fn);
2324                 };
2325             } else {
2326                 return function() {
2327                 };
2328             }
2329         }()
2330     };
2331     
2332 }();
2333 (function() {     
2334    
2335     var E = Roo.lib.Event;
2336     E.on = E.addListener;
2337     E.un = E.removeListener;
2338
2339     if (document && document.body) {
2340         E._load();
2341     } else {
2342         E.doAdd(window, "load", E._load);
2343     }
2344     E.doAdd(window, "unload", E._unload);
2345     E._tryPreloadAttach();
2346 })();
2347
2348 /*
2349  * Portions of this file are based on pieces of Yahoo User Interface Library
2350  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2351  * YUI licensed under the BSD License:
2352  * http://developer.yahoo.net/yui/license.txt
2353  * <script type="text/javascript">
2354  *
2355  */
2356
2357 (function() {
2358     
2359     Roo.lib.Ajax = {
2360         request : function(method, uri, cb, data, options) {
2361             if(options){
2362                 var hs = options.headers;
2363                 if(hs){
2364                     for(var h in hs){
2365                         if(hs.hasOwnProperty(h)){
2366                             this.initHeader(h, hs[h], false);
2367                         }
2368                     }
2369                 }
2370                 if(options.xmlData){
2371                     this.initHeader('Content-Type', 'text/xml', false);
2372                     method = 'POST';
2373                     data = options.xmlData;
2374                 }
2375             }
2376
2377             return this.asyncRequest(method, uri, cb, data);
2378         },
2379
2380         serializeForm : function(form) {
2381             if(typeof form == 'string') {
2382                 form = (document.getElementById(form) || document.forms[form]);
2383             }
2384
2385             var el, name, val, disabled, data = '', hasSubmit = false;
2386             for (var i = 0; i < form.elements.length; i++) {
2387                 el = form.elements[i];
2388                 disabled = form.elements[i].disabled;
2389                 name = form.elements[i].name;
2390                 val = form.elements[i].value;
2391
2392                 if (!disabled && name){
2393                     switch (el.type)
2394                             {
2395                         case 'select-one':
2396                         case 'select-multiple':
2397                             for (var j = 0; j < el.options.length; j++) {
2398                                 if (el.options[j].selected) {
2399                                     if (Roo.isIE) {
2400                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2401                                     }
2402                                     else {
2403                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2404                                     }
2405                                 }
2406                             }
2407                             break;
2408                         case 'radio':
2409                         case 'checkbox':
2410                             if (el.checked) {
2411                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2412                             }
2413                             break;
2414                         case 'file':
2415
2416                         case undefined:
2417
2418                         case 'reset':
2419
2420                         case 'button':
2421
2422                             break;
2423                         case 'submit':
2424                             if(hasSubmit == false) {
2425                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2426                                 hasSubmit = true;
2427                             }
2428                             break;
2429                         default:
2430                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2431                             break;
2432                     }
2433                 }
2434             }
2435             data = data.substr(0, data.length - 1);
2436             return data;
2437         },
2438
2439         headers:{},
2440
2441         hasHeaders:false,
2442
2443         useDefaultHeader:true,
2444
2445         defaultPostHeader:'application/x-www-form-urlencoded',
2446
2447         useDefaultXhrHeader:true,
2448
2449         defaultXhrHeader:'XMLHttpRequest',
2450
2451         hasDefaultHeaders:true,
2452
2453         defaultHeaders:{},
2454
2455         poll:{},
2456
2457         timeout:{},
2458
2459         pollInterval:50,
2460
2461         transactionId:0,
2462
2463         setProgId:function(id)
2464         {
2465             this.activeX.unshift(id);
2466         },
2467
2468         setDefaultPostHeader:function(b)
2469         {
2470             this.useDefaultHeader = b;
2471         },
2472
2473         setDefaultXhrHeader:function(b)
2474         {
2475             this.useDefaultXhrHeader = b;
2476         },
2477
2478         setPollingInterval:function(i)
2479         {
2480             if (typeof i == 'number' && isFinite(i)) {
2481                 this.pollInterval = i;
2482             }
2483         },
2484
2485         createXhrObject:function(transactionId)
2486         {
2487             var obj,http;
2488             try
2489             {
2490
2491                 http = new XMLHttpRequest();
2492
2493                 obj = { conn:http, tId:transactionId };
2494             }
2495             catch(e)
2496             {
2497                 for (var i = 0; i < this.activeX.length; ++i) {
2498                     try
2499                     {
2500
2501                         http = new ActiveXObject(this.activeX[i]);
2502
2503                         obj = { conn:http, tId:transactionId };
2504                         break;
2505                     }
2506                     catch(e) {
2507                     }
2508                 }
2509             }
2510             finally
2511             {
2512                 return obj;
2513             }
2514         },
2515
2516         getConnectionObject:function()
2517         {
2518             var o;
2519             var tId = this.transactionId;
2520
2521             try
2522             {
2523                 o = this.createXhrObject(tId);
2524                 if (o) {
2525                     this.transactionId++;
2526                 }
2527             }
2528             catch(e) {
2529             }
2530             finally
2531             {
2532                 return o;
2533             }
2534         },
2535
2536         asyncRequest:function(method, uri, callback, postData)
2537         {
2538             var o = this.getConnectionObject();
2539
2540             if (!o) {
2541                 return null;
2542             }
2543             else {
2544                 o.conn.open(method, uri, true);
2545
2546                 if (this.useDefaultXhrHeader) {
2547                     if (!this.defaultHeaders['X-Requested-With']) {
2548                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2549                     }
2550                 }
2551
2552                 if(postData && this.useDefaultHeader){
2553                     this.initHeader('Content-Type', this.defaultPostHeader);
2554                 }
2555
2556                  if (this.hasDefaultHeaders || this.hasHeaders) {
2557                     this.setHeader(o);
2558                 }
2559
2560                 this.handleReadyState(o, callback);
2561                 o.conn.send(postData || null);
2562
2563                 return o;
2564             }
2565         },
2566
2567         handleReadyState:function(o, callback)
2568         {
2569             var oConn = this;
2570
2571             if (callback && callback.timeout) {
2572                 this.timeout[o.tId] = window.setTimeout(function() {
2573                     oConn.abort(o, callback, true);
2574                 }, callback.timeout);
2575             }
2576
2577             this.poll[o.tId] = window.setInterval(
2578                     function() {
2579                         if (o.conn && o.conn.readyState == 4) {
2580                             window.clearInterval(oConn.poll[o.tId]);
2581                             delete oConn.poll[o.tId];
2582
2583                             if(callback && callback.timeout) {
2584                                 window.clearTimeout(oConn.timeout[o.tId]);
2585                                 delete oConn.timeout[o.tId];
2586                             }
2587
2588                             oConn.handleTransactionResponse(o, callback);
2589                         }
2590                     }
2591                     , this.pollInterval);
2592         },
2593
2594         handleTransactionResponse:function(o, callback, isAbort)
2595         {
2596
2597             if (!callback) {
2598                 this.releaseObject(o);
2599                 return;
2600             }
2601
2602             var httpStatus, responseObject;
2603
2604             try
2605             {
2606                 if (o.conn.status !== undefined && o.conn.status != 0) {
2607                     httpStatus = o.conn.status;
2608                 }
2609                 else {
2610                     httpStatus = 13030;
2611                 }
2612             }
2613             catch(e) {
2614
2615
2616                 httpStatus = 13030;
2617             }
2618
2619             if (httpStatus >= 200 && httpStatus < 300) {
2620                 responseObject = this.createResponseObject(o, callback.argument);
2621                 if (callback.success) {
2622                     if (!callback.scope) {
2623                         callback.success(responseObject);
2624                     }
2625                     else {
2626
2627
2628                         callback.success.apply(callback.scope, [responseObject]);
2629                     }
2630                 }
2631             }
2632             else {
2633                 switch (httpStatus) {
2634
2635                     case 12002:
2636                     case 12029:
2637                     case 12030:
2638                     case 12031:
2639                     case 12152:
2640                     case 13030:
2641                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2642                         if (callback.failure) {
2643                             if (!callback.scope) {
2644                                 callback.failure(responseObject);
2645                             }
2646                             else {
2647                                 callback.failure.apply(callback.scope, [responseObject]);
2648                             }
2649                         }
2650                         break;
2651                     default:
2652                         responseObject = this.createResponseObject(o, callback.argument);
2653                         if (callback.failure) {
2654                             if (!callback.scope) {
2655                                 callback.failure(responseObject);
2656                             }
2657                             else {
2658                                 callback.failure.apply(callback.scope, [responseObject]);
2659                             }
2660                         }
2661                 }
2662             }
2663
2664             this.releaseObject(o);
2665             responseObject = null;
2666         },
2667
2668         createResponseObject:function(o, callbackArg)
2669         {
2670             var obj = {};
2671             var headerObj = {};
2672
2673             try
2674             {
2675                 var headerStr = o.conn.getAllResponseHeaders();
2676                 var header = headerStr.split('\n');
2677                 for (var i = 0; i < header.length; i++) {
2678                     var delimitPos = header[i].indexOf(':');
2679                     if (delimitPos != -1) {
2680                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2681                     }
2682                 }
2683             }
2684             catch(e) {
2685             }
2686
2687             obj.tId = o.tId;
2688             obj.status = o.conn.status;
2689             obj.statusText = o.conn.statusText;
2690             obj.getResponseHeader = headerObj;
2691             obj.getAllResponseHeaders = headerStr;
2692             obj.responseText = o.conn.responseText;
2693             obj.responseXML = o.conn.responseXML;
2694
2695             if (typeof callbackArg !== undefined) {
2696                 obj.argument = callbackArg;
2697             }
2698
2699             return obj;
2700         },
2701
2702         createExceptionObject:function(tId, callbackArg, isAbort)
2703         {
2704             var COMM_CODE = 0;
2705             var COMM_ERROR = 'communication failure';
2706             var ABORT_CODE = -1;
2707             var ABORT_ERROR = 'transaction aborted';
2708
2709             var obj = {};
2710
2711             obj.tId = tId;
2712             if (isAbort) {
2713                 obj.status = ABORT_CODE;
2714                 obj.statusText = ABORT_ERROR;
2715             }
2716             else {
2717                 obj.status = COMM_CODE;
2718                 obj.statusText = COMM_ERROR;
2719             }
2720
2721             if (callbackArg) {
2722                 obj.argument = callbackArg;
2723             }
2724
2725             return obj;
2726         },
2727
2728         initHeader:function(label, value, isDefault)
2729         {
2730             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2731
2732             if (headerObj[label] === undefined) {
2733                 headerObj[label] = value;
2734             }
2735             else {
2736
2737
2738                 headerObj[label] = value + "," + headerObj[label];
2739             }
2740
2741             if (isDefault) {
2742                 this.hasDefaultHeaders = true;
2743             }
2744             else {
2745                 this.hasHeaders = true;
2746             }
2747         },
2748
2749
2750         setHeader:function(o)
2751         {
2752             if (this.hasDefaultHeaders) {
2753                 for (var prop in this.defaultHeaders) {
2754                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2755                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2756                     }
2757                 }
2758             }
2759
2760             if (this.hasHeaders) {
2761                 for (var prop in this.headers) {
2762                     if (this.headers.hasOwnProperty(prop)) {
2763                         o.conn.setRequestHeader(prop, this.headers[prop]);
2764                     }
2765                 }
2766                 this.headers = {};
2767                 this.hasHeaders = false;
2768             }
2769         },
2770
2771         resetDefaultHeaders:function() {
2772             delete this.defaultHeaders;
2773             this.defaultHeaders = {};
2774             this.hasDefaultHeaders = false;
2775         },
2776
2777         abort:function(o, callback, isTimeout)
2778         {
2779             if(this.isCallInProgress(o)) {
2780                 o.conn.abort();
2781                 window.clearInterval(this.poll[o.tId]);
2782                 delete this.poll[o.tId];
2783                 if (isTimeout) {
2784                     delete this.timeout[o.tId];
2785                 }
2786
2787                 this.handleTransactionResponse(o, callback, true);
2788
2789                 return true;
2790             }
2791             else {
2792                 return false;
2793             }
2794         },
2795
2796
2797         isCallInProgress:function(o)
2798         {
2799             if (o && o.conn) {
2800                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2801             }
2802             else {
2803
2804                 return false;
2805             }
2806         },
2807
2808
2809         releaseObject:function(o)
2810         {
2811
2812             o.conn = null;
2813
2814             o = null;
2815         },
2816
2817         activeX:[
2818         'MSXML2.XMLHTTP.3.0',
2819         'MSXML2.XMLHTTP',
2820         'Microsoft.XMLHTTP'
2821         ]
2822
2823
2824     };
2825 })();/*
2826  * Portions of this file are based on pieces of Yahoo User Interface Library
2827  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2828  * YUI licensed under the BSD License:
2829  * http://developer.yahoo.net/yui/license.txt
2830  * <script type="text/javascript">
2831  *
2832  */
2833
2834 Roo.lib.Region = function(t, r, b, l) {
2835     this.top = t;
2836     this[1] = t;
2837     this.right = r;
2838     this.bottom = b;
2839     this.left = l;
2840     this[0] = l;
2841 };
2842
2843
2844 Roo.lib.Region.prototype = {
2845     contains : function(region) {
2846         return ( region.left >= this.left &&
2847                  region.right <= this.right &&
2848                  region.top >= this.top &&
2849                  region.bottom <= this.bottom    );
2850
2851     },
2852
2853     getArea : function() {
2854         return ( (this.bottom - this.top) * (this.right - this.left) );
2855     },
2856
2857     intersect : function(region) {
2858         var t = Math.max(this.top, region.top);
2859         var r = Math.min(this.right, region.right);
2860         var b = Math.min(this.bottom, region.bottom);
2861         var l = Math.max(this.left, region.left);
2862
2863         if (b >= t && r >= l) {
2864             return new Roo.lib.Region(t, r, b, l);
2865         } else {
2866             return null;
2867         }
2868     },
2869     union : function(region) {
2870         var t = Math.min(this.top, region.top);
2871         var r = Math.max(this.right, region.right);
2872         var b = Math.max(this.bottom, region.bottom);
2873         var l = Math.min(this.left, region.left);
2874
2875         return new Roo.lib.Region(t, r, b, l);
2876     },
2877
2878     adjust : function(t, l, b, r) {
2879         this.top += t;
2880         this.left += l;
2881         this.right += r;
2882         this.bottom += b;
2883         return this;
2884     }
2885 };
2886
2887 Roo.lib.Region.getRegion = function(el) {
2888     var p = Roo.lib.Dom.getXY(el);
2889
2890     var t = p[1];
2891     var r = p[0] + el.offsetWidth;
2892     var b = p[1] + el.offsetHeight;
2893     var l = p[0];
2894
2895     return new Roo.lib.Region(t, r, b, l);
2896 };
2897 /*
2898  * Portions of this file are based on pieces of Yahoo User Interface Library
2899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2900  * YUI licensed under the BSD License:
2901  * http://developer.yahoo.net/yui/license.txt
2902  * <script type="text/javascript">
2903  *
2904  */
2905 //@@dep Roo.lib.Region
2906
2907
2908 Roo.lib.Point = function(x, y) {
2909     if (x instanceof Array) {
2910         y = x[1];
2911         x = x[0];
2912     }
2913     this.x = this.right = this.left = this[0] = x;
2914     this.y = this.top = this.bottom = this[1] = y;
2915 };
2916
2917 Roo.lib.Point.prototype = new Roo.lib.Region();
2918 /*
2919  * Portions of this file are based on pieces of Yahoo User Interface Library
2920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2921  * YUI licensed under the BSD License:
2922  * http://developer.yahoo.net/yui/license.txt
2923  * <script type="text/javascript">
2924  *
2925  */
2926  
2927 (function() {   
2928
2929     Roo.lib.Anim = {
2930         scroll : function(el, args, duration, easing, cb, scope) {
2931             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2932         },
2933
2934         motion : function(el, args, duration, easing, cb, scope) {
2935             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2936         },
2937
2938         color : function(el, args, duration, easing, cb, scope) {
2939             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2940         },
2941
2942         run : function(el, args, duration, easing, cb, scope, type) {
2943             type = type || Roo.lib.AnimBase;
2944             if (typeof easing == "string") {
2945                 easing = Roo.lib.Easing[easing];
2946             }
2947             var anim = new type(el, args, duration, easing);
2948             anim.animateX(function() {
2949                 Roo.callback(cb, scope);
2950             });
2951             return anim;
2952         }
2953     };
2954 })();/*
2955  * Portions of this file are based on pieces of Yahoo User Interface Library
2956  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2957  * YUI licensed under the BSD License:
2958  * http://developer.yahoo.net/yui/license.txt
2959  * <script type="text/javascript">
2960  *
2961  */
2962
2963 (function() {    
2964     var libFlyweight;
2965     
2966     function fly(el) {
2967         if (!libFlyweight) {
2968             libFlyweight = new Roo.Element.Flyweight();
2969         }
2970         libFlyweight.dom = el;
2971         return libFlyweight;
2972     }
2973
2974     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2975     
2976    
2977     
2978     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2979         if (el) {
2980             this.init(el, attributes, duration, method);
2981         }
2982     };
2983
2984     Roo.lib.AnimBase.fly = fly;
2985     
2986     
2987     
2988     Roo.lib.AnimBase.prototype = {
2989
2990         toString: function() {
2991             var el = this.getEl();
2992             var id = el.id || el.tagName;
2993             return ("Anim " + id);
2994         },
2995
2996         patterns: {
2997             noNegatives:        /width|height|opacity|padding/i,
2998             offsetAttribute:  /^((width|height)|(top|left))$/,
2999             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3000             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3001         },
3002
3003
3004         doMethod: function(attr, start, end) {
3005             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3006         },
3007
3008
3009         setAttribute: function(attr, val, unit) {
3010             if (this.patterns.noNegatives.test(attr)) {
3011                 val = (val > 0) ? val : 0;
3012             }
3013
3014             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3015         },
3016
3017
3018         getAttribute: function(attr) {
3019             var el = this.getEl();
3020             var val = fly(el).getStyle(attr);
3021
3022             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3023                 return parseFloat(val);
3024             }
3025
3026             var a = this.patterns.offsetAttribute.exec(attr) || [];
3027             var pos = !!( a[3] );
3028             var box = !!( a[2] );
3029
3030
3031             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3032                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3033             } else {
3034                 val = 0;
3035             }
3036
3037             return val;
3038         },
3039
3040
3041         getDefaultUnit: function(attr) {
3042             if (this.patterns.defaultUnit.test(attr)) {
3043                 return 'px';
3044             }
3045
3046             return '';
3047         },
3048
3049         animateX : function(callback, scope) {
3050             var f = function() {
3051                 this.onComplete.removeListener(f);
3052                 if (typeof callback == "function") {
3053                     callback.call(scope || this, this);
3054                 }
3055             };
3056             this.onComplete.addListener(f, this);
3057             this.animate();
3058         },
3059
3060
3061         setRuntimeAttribute: function(attr) {
3062             var start;
3063             var end;
3064             var attributes = this.attributes;
3065
3066             this.runtimeAttributes[attr] = {};
3067
3068             var isset = function(prop) {
3069                 return (typeof prop !== 'undefined');
3070             };
3071
3072             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3073                 return false;
3074             }
3075
3076             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3077
3078
3079             if (isset(attributes[attr]['to'])) {
3080                 end = attributes[attr]['to'];
3081             } else if (isset(attributes[attr]['by'])) {
3082                 if (start.constructor == Array) {
3083                     end = [];
3084                     for (var i = 0, len = start.length; i < len; ++i) {
3085                         end[i] = start[i] + attributes[attr]['by'][i];
3086                     }
3087                 } else {
3088                     end = start + attributes[attr]['by'];
3089                 }
3090             }
3091
3092             this.runtimeAttributes[attr].start = start;
3093             this.runtimeAttributes[attr].end = end;
3094
3095
3096             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3097         },
3098
3099
3100         init: function(el, attributes, duration, method) {
3101
3102             var isAnimated = false;
3103
3104
3105             var startTime = null;
3106
3107
3108             var actualFrames = 0;
3109
3110
3111             el = Roo.getDom(el);
3112
3113
3114             this.attributes = attributes || {};
3115
3116
3117             this.duration = duration || 1;
3118
3119
3120             this.method = method || Roo.lib.Easing.easeNone;
3121
3122
3123             this.useSeconds = true;
3124
3125
3126             this.currentFrame = 0;
3127
3128
3129             this.totalFrames = Roo.lib.AnimMgr.fps;
3130
3131
3132             this.getEl = function() {
3133                 return el;
3134             };
3135
3136
3137             this.isAnimated = function() {
3138                 return isAnimated;
3139             };
3140
3141
3142             this.getStartTime = function() {
3143                 return startTime;
3144             };
3145
3146             this.runtimeAttributes = {};
3147
3148
3149             this.animate = function() {
3150                 if (this.isAnimated()) {
3151                     return false;
3152                 }
3153
3154                 this.currentFrame = 0;
3155
3156                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3157
3158                 Roo.lib.AnimMgr.registerElement(this);
3159             };
3160
3161
3162             this.stop = function(finish) {
3163                 if (finish) {
3164                     this.currentFrame = this.totalFrames;
3165                     this._onTween.fire();
3166                 }
3167                 Roo.lib.AnimMgr.stop(this);
3168             };
3169
3170             var onStart = function() {
3171                 this.onStart.fire();
3172
3173                 this.runtimeAttributes = {};
3174                 for (var attr in this.attributes) {
3175                     this.setRuntimeAttribute(attr);
3176                 }
3177
3178                 isAnimated = true;
3179                 actualFrames = 0;
3180                 startTime = new Date();
3181             };
3182
3183
3184             var onTween = function() {
3185                 var data = {
3186                     duration: new Date() - this.getStartTime(),
3187                     currentFrame: this.currentFrame
3188                 };
3189
3190                 data.toString = function() {
3191                     return (
3192                             'duration: ' + data.duration +
3193                             ', currentFrame: ' + data.currentFrame
3194                             );
3195                 };
3196
3197                 this.onTween.fire(data);
3198
3199                 var runtimeAttributes = this.runtimeAttributes;
3200
3201                 for (var attr in runtimeAttributes) {
3202                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3203                 }
3204
3205                 actualFrames += 1;
3206             };
3207
3208             var onComplete = function() {
3209                 var actual_duration = (new Date() - startTime) / 1000 ;
3210
3211                 var data = {
3212                     duration: actual_duration,
3213                     frames: actualFrames,
3214                     fps: actualFrames / actual_duration
3215                 };
3216
3217                 data.toString = function() {
3218                     return (
3219                             'duration: ' + data.duration +
3220                             ', frames: ' + data.frames +
3221                             ', fps: ' + data.fps
3222                             );
3223                 };
3224
3225                 isAnimated = false;
3226                 actualFrames = 0;
3227                 this.onComplete.fire(data);
3228             };
3229
3230
3231             this._onStart = new Roo.util.Event(this);
3232             this.onStart = new Roo.util.Event(this);
3233             this.onTween = new Roo.util.Event(this);
3234             this._onTween = new Roo.util.Event(this);
3235             this.onComplete = new Roo.util.Event(this);
3236             this._onComplete = new Roo.util.Event(this);
3237             this._onStart.addListener(onStart);
3238             this._onTween.addListener(onTween);
3239             this._onComplete.addListener(onComplete);
3240         }
3241     };
3242 })();
3243 /*
3244  * Portions of this file are based on pieces of Yahoo User Interface Library
3245  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3246  * YUI licensed under the BSD License:
3247  * http://developer.yahoo.net/yui/license.txt
3248  * <script type="text/javascript">
3249  *
3250  */
3251
3252 Roo.lib.AnimMgr = new function() {
3253
3254         var thread = null;
3255
3256
3257         var queue = [];
3258
3259
3260         var tweenCount = 0;
3261
3262
3263         this.fps = 1000;
3264
3265
3266         this.delay = 1;
3267
3268
3269         this.registerElement = function(tween) {
3270             queue[queue.length] = tween;
3271             tweenCount += 1;
3272             tween._onStart.fire();
3273             this.start();
3274         };
3275
3276
3277         this.unRegister = function(tween, index) {
3278             tween._onComplete.fire();
3279             index = index || getIndex(tween);
3280             if (index != -1) {
3281                 queue.splice(index, 1);
3282             }
3283
3284             tweenCount -= 1;
3285             if (tweenCount <= 0) {
3286                 this.stop();
3287             }
3288         };
3289
3290
3291         this.start = function() {
3292             if (thread === null) {
3293                 thread = setInterval(this.run, this.delay);
3294             }
3295         };
3296
3297
3298         this.stop = function(tween) {
3299             if (!tween) {
3300                 clearInterval(thread);
3301
3302                 for (var i = 0, len = queue.length; i < len; ++i) {
3303                     if (queue[0].isAnimated()) {
3304                         this.unRegister(queue[0], 0);
3305                     }
3306                 }
3307
3308                 queue = [];
3309                 thread = null;
3310                 tweenCount = 0;
3311             }
3312             else {
3313                 this.unRegister(tween);
3314             }
3315         };
3316
3317
3318         this.run = function() {
3319             for (var i = 0, len = queue.length; i < len; ++i) {
3320                 var tween = queue[i];
3321                 if (!tween || !tween.isAnimated()) {
3322                     continue;
3323                 }
3324
3325                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3326                 {
3327                     tween.currentFrame += 1;
3328
3329                     if (tween.useSeconds) {
3330                         correctFrame(tween);
3331                     }
3332                     tween._onTween.fire();
3333                 }
3334                 else {
3335                     Roo.lib.AnimMgr.stop(tween, i);
3336                 }
3337             }
3338         };
3339
3340         var getIndex = function(anim) {
3341             for (var i = 0, len = queue.length; i < len; ++i) {
3342                 if (queue[i] == anim) {
3343                     return i;
3344                 }
3345             }
3346             return -1;
3347         };
3348
3349
3350         var correctFrame = function(tween) {
3351             var frames = tween.totalFrames;
3352             var frame = tween.currentFrame;
3353             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3354             var elapsed = (new Date() - tween.getStartTime());
3355             var tweak = 0;
3356
3357             if (elapsed < tween.duration * 1000) {
3358                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3359             } else {
3360                 tweak = frames - (frame + 1);
3361             }
3362             if (tweak > 0 && isFinite(tweak)) {
3363                 if (tween.currentFrame + tweak >= frames) {
3364                     tweak = frames - (frame + 1);
3365                 }
3366
3367                 tween.currentFrame += tweak;
3368             }
3369         };
3370     };/*
3371  * Portions of this file are based on pieces of Yahoo User Interface Library
3372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3373  * YUI licensed under the BSD License:
3374  * http://developer.yahoo.net/yui/license.txt
3375  * <script type="text/javascript">
3376  *
3377  */
3378 Roo.lib.Bezier = new function() {
3379
3380         this.getPosition = function(points, t) {
3381             var n = points.length;
3382             var tmp = [];
3383
3384             for (var i = 0; i < n; ++i) {
3385                 tmp[i] = [points[i][0], points[i][1]];
3386             }
3387
3388             for (var j = 1; j < n; ++j) {
3389                 for (i = 0; i < n - j; ++i) {
3390                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3391                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3392                 }
3393             }
3394
3395             return [ tmp[0][0], tmp[0][1] ];
3396
3397         };
3398     };/*
3399  * Portions of this file are based on pieces of Yahoo User Interface Library
3400  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3401  * YUI licensed under the BSD License:
3402  * http://developer.yahoo.net/yui/license.txt
3403  * <script type="text/javascript">
3404  *
3405  */
3406 (function() {
3407
3408     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3409         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3410     };
3411
3412     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3413
3414     var fly = Roo.lib.AnimBase.fly;
3415     var Y = Roo.lib;
3416     var superclass = Y.ColorAnim.superclass;
3417     var proto = Y.ColorAnim.prototype;
3418
3419     proto.toString = function() {
3420         var el = this.getEl();
3421         var id = el.id || el.tagName;
3422         return ("ColorAnim " + id);
3423     };
3424
3425     proto.patterns.color = /color$/i;
3426     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3427     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3428     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3429     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3430
3431
3432     proto.parseColor = function(s) {
3433         if (s.length == 3) {
3434             return s;
3435         }
3436
3437         var c = this.patterns.hex.exec(s);
3438         if (c && c.length == 4) {
3439             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3440         }
3441
3442         c = this.patterns.rgb.exec(s);
3443         if (c && c.length == 4) {
3444             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3445         }
3446
3447         c = this.patterns.hex3.exec(s);
3448         if (c && c.length == 4) {
3449             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3450         }
3451
3452         return null;
3453     };
3454     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3455     proto.getAttribute = function(attr) {
3456         var el = this.getEl();
3457         if (this.patterns.color.test(attr)) {
3458             var val = fly(el).getStyle(attr);
3459
3460             if (this.patterns.transparent.test(val)) {
3461                 var parent = el.parentNode;
3462                 val = fly(parent).getStyle(attr);
3463
3464                 while (parent && this.patterns.transparent.test(val)) {
3465                     parent = parent.parentNode;
3466                     val = fly(parent).getStyle(attr);
3467                     if (parent.tagName.toUpperCase() == 'HTML') {
3468                         val = '#fff';
3469                     }
3470                 }
3471             }
3472         } else {
3473             val = superclass.getAttribute.call(this, attr);
3474         }
3475
3476         return val;
3477     };
3478     proto.getAttribute = function(attr) {
3479         var el = this.getEl();
3480         if (this.patterns.color.test(attr)) {
3481             var val = fly(el).getStyle(attr);
3482
3483             if (this.patterns.transparent.test(val)) {
3484                 var parent = el.parentNode;
3485                 val = fly(parent).getStyle(attr);
3486
3487                 while (parent && this.patterns.transparent.test(val)) {
3488                     parent = parent.parentNode;
3489                     val = fly(parent).getStyle(attr);
3490                     if (parent.tagName.toUpperCase() == 'HTML') {
3491                         val = '#fff';
3492                     }
3493                 }
3494             }
3495         } else {
3496             val = superclass.getAttribute.call(this, attr);
3497         }
3498
3499         return val;
3500     };
3501
3502     proto.doMethod = function(attr, start, end) {
3503         var val;
3504
3505         if (this.patterns.color.test(attr)) {
3506             val = [];
3507             for (var i = 0, len = start.length; i < len; ++i) {
3508                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3509             }
3510
3511             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3512         }
3513         else {
3514             val = superclass.doMethod.call(this, attr, start, end);
3515         }
3516
3517         return val;
3518     };
3519
3520     proto.setRuntimeAttribute = function(attr) {
3521         superclass.setRuntimeAttribute.call(this, attr);
3522
3523         if (this.patterns.color.test(attr)) {
3524             var attributes = this.attributes;
3525             var start = this.parseColor(this.runtimeAttributes[attr].start);
3526             var end = this.parseColor(this.runtimeAttributes[attr].end);
3527
3528             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3529                 end = this.parseColor(attributes[attr].by);
3530
3531                 for (var i = 0, len = start.length; i < len; ++i) {
3532                     end[i] = start[i] + end[i];
3533                 }
3534             }
3535
3536             this.runtimeAttributes[attr].start = start;
3537             this.runtimeAttributes[attr].end = end;
3538         }
3539     };
3540 })();
3541
3542 /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Easing = {
3551
3552
3553     easeNone: function (t, b, c, d) {
3554         return c * t / d + b;
3555     },
3556
3557
3558     easeIn: function (t, b, c, d) {
3559         return c * (t /= d) * t + b;
3560     },
3561
3562
3563     easeOut: function (t, b, c, d) {
3564         return -c * (t /= d) * (t - 2) + b;
3565     },
3566
3567
3568     easeBoth: function (t, b, c, d) {
3569         if ((t /= d / 2) < 1) {
3570             return c / 2 * t * t + b;
3571         }
3572
3573         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3574     },
3575
3576
3577     easeInStrong: function (t, b, c, d) {
3578         return c * (t /= d) * t * t * t + b;
3579     },
3580
3581
3582     easeOutStrong: function (t, b, c, d) {
3583         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3584     },
3585
3586
3587     easeBothStrong: function (t, b, c, d) {
3588         if ((t /= d / 2) < 1) {
3589             return c / 2 * t * t * t * t + b;
3590         }
3591
3592         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3593     },
3594
3595
3596
3597     elasticIn: function (t, b, c, d, a, p) {
3598         if (t == 0) {
3599             return b;
3600         }
3601         if ((t /= d) == 1) {
3602             return b + c;
3603         }
3604         if (!p) {
3605             p = d * .3;
3606         }
3607
3608         if (!a || a < Math.abs(c)) {
3609             a = c;
3610             var s = p / 4;
3611         }
3612         else {
3613             var s = p / (2 * Math.PI) * Math.asin(c / a);
3614         }
3615
3616         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3617     },
3618
3619
3620     elasticOut: function (t, b, c, d, a, p) {
3621         if (t == 0) {
3622             return b;
3623         }
3624         if ((t /= d) == 1) {
3625             return b + c;
3626         }
3627         if (!p) {
3628             p = d * .3;
3629         }
3630
3631         if (!a || a < Math.abs(c)) {
3632             a = c;
3633             var s = p / 4;
3634         }
3635         else {
3636             var s = p / (2 * Math.PI) * Math.asin(c / a);
3637         }
3638
3639         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3640     },
3641
3642
3643     elasticBoth: function (t, b, c, d, a, p) {
3644         if (t == 0) {
3645             return b;
3646         }
3647
3648         if ((t /= d / 2) == 2) {
3649             return b + c;
3650         }
3651
3652         if (!p) {
3653             p = d * (.3 * 1.5);
3654         }
3655
3656         if (!a || a < Math.abs(c)) {
3657             a = c;
3658             var s = p / 4;
3659         }
3660         else {
3661             var s = p / (2 * Math.PI) * Math.asin(c / a);
3662         }
3663
3664         if (t < 1) {
3665             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3666                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3667         }
3668         return a * Math.pow(2, -10 * (t -= 1)) *
3669                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3670     },
3671
3672
3673
3674     backIn: function (t, b, c, d, s) {
3675         if (typeof s == 'undefined') {
3676             s = 1.70158;
3677         }
3678         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3679     },
3680
3681
3682     backOut: function (t, b, c, d, s) {
3683         if (typeof s == 'undefined') {
3684             s = 1.70158;
3685         }
3686         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3687     },
3688
3689
3690     backBoth: function (t, b, c, d, s) {
3691         if (typeof s == 'undefined') {
3692             s = 1.70158;
3693         }
3694
3695         if ((t /= d / 2 ) < 1) {
3696             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3697         }
3698         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3699     },
3700
3701
3702     bounceIn: function (t, b, c, d) {
3703         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3704     },
3705
3706
3707     bounceOut: function (t, b, c, d) {
3708         if ((t /= d) < (1 / 2.75)) {
3709             return c * (7.5625 * t * t) + b;
3710         } else if (t < (2 / 2.75)) {
3711             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3712         } else if (t < (2.5 / 2.75)) {
3713             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3714         }
3715         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3716     },
3717
3718
3719     bounceBoth: function (t, b, c, d) {
3720         if (t < d / 2) {
3721             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3722         }
3723         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3724     }
3725 };/*
3726  * Portions of this file are based on pieces of Yahoo User Interface Library
3727  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3728  * YUI licensed under the BSD License:
3729  * http://developer.yahoo.net/yui/license.txt
3730  * <script type="text/javascript">
3731  *
3732  */
3733     (function() {
3734         Roo.lib.Motion = function(el, attributes, duration, method) {
3735             if (el) {
3736                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3737             }
3738         };
3739
3740         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3741
3742
3743         var Y = Roo.lib;
3744         var superclass = Y.Motion.superclass;
3745         var proto = Y.Motion.prototype;
3746
3747         proto.toString = function() {
3748             var el = this.getEl();
3749             var id = el.id || el.tagName;
3750             return ("Motion " + id);
3751         };
3752
3753         proto.patterns.points = /^points$/i;
3754
3755         proto.setAttribute = function(attr, val, unit) {
3756             if (this.patterns.points.test(attr)) {
3757                 unit = unit || 'px';
3758                 superclass.setAttribute.call(this, 'left', val[0], unit);
3759                 superclass.setAttribute.call(this, 'top', val[1], unit);
3760             } else {
3761                 superclass.setAttribute.call(this, attr, val, unit);
3762             }
3763         };
3764
3765         proto.getAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var val = [
3768                         superclass.getAttribute.call(this, 'left'),
3769                         superclass.getAttribute.call(this, 'top')
3770                         ];
3771             } else {
3772                 val = superclass.getAttribute.call(this, attr);
3773             }
3774
3775             return val;
3776         };
3777
3778         proto.doMethod = function(attr, start, end) {
3779             var val = null;
3780
3781             if (this.patterns.points.test(attr)) {
3782                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3783                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3784             } else {
3785                 val = superclass.doMethod.call(this, attr, start, end);
3786             }
3787             return val;
3788         };
3789
3790         proto.setRuntimeAttribute = function(attr) {
3791             if (this.patterns.points.test(attr)) {
3792                 var el = this.getEl();
3793                 var attributes = this.attributes;
3794                 var start;
3795                 var control = attributes['points']['control'] || [];
3796                 var end;
3797                 var i, len;
3798
3799                 if (control.length > 0 && !(control[0] instanceof Array)) {
3800                     control = [control];
3801                 } else {
3802                     var tmp = [];
3803                     for (i = 0,len = control.length; i < len; ++i) {
3804                         tmp[i] = control[i];
3805                     }
3806                     control = tmp;
3807                 }
3808
3809                 Roo.fly(el).position();
3810
3811                 if (isset(attributes['points']['from'])) {
3812                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3813                 }
3814                 else {
3815                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3816                 }
3817
3818                 start = this.getAttribute('points');
3819
3820
3821                 if (isset(attributes['points']['to'])) {
3822                     end = translateValues.call(this, attributes['points']['to'], start);
3823
3824                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3825                     for (i = 0,len = control.length; i < len; ++i) {
3826                         control[i] = translateValues.call(this, control[i], start);
3827                     }
3828
3829
3830                 } else if (isset(attributes['points']['by'])) {
3831                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3832
3833                     for (i = 0,len = control.length; i < len; ++i) {
3834                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3835                     }
3836                 }
3837
3838                 this.runtimeAttributes[attr] = [start];
3839
3840                 if (control.length > 0) {
3841                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3842                 }
3843
3844                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3845             }
3846             else {
3847                 superclass.setRuntimeAttribute.call(this, attr);
3848             }
3849         };
3850
3851         var translateValues = function(val, start) {
3852             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3853             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3854
3855             return val;
3856         };
3857
3858         var isset = function(prop) {
3859             return (typeof prop !== 'undefined');
3860         };
3861     })();
3862 /*
3863  * Portions of this file are based on pieces of Yahoo User Interface Library
3864  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3865  * YUI licensed under the BSD License:
3866  * http://developer.yahoo.net/yui/license.txt
3867  * <script type="text/javascript">
3868  *
3869  */
3870     (function() {
3871         Roo.lib.Scroll = function(el, attributes, duration, method) {
3872             if (el) {
3873                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3874             }
3875         };
3876
3877         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3878
3879
3880         var Y = Roo.lib;
3881         var superclass = Y.Scroll.superclass;
3882         var proto = Y.Scroll.prototype;
3883
3884         proto.toString = function() {
3885             var el = this.getEl();
3886             var id = el.id || el.tagName;
3887             return ("Scroll " + id);
3888         };
3889
3890         proto.doMethod = function(attr, start, end) {
3891             var val = null;
3892
3893             if (attr == 'scroll') {
3894                 val = [
3895                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3896                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3897                         ];
3898
3899             } else {
3900                 val = superclass.doMethod.call(this, attr, start, end);
3901             }
3902             return val;
3903         };
3904
3905         proto.getAttribute = function(attr) {
3906             var val = null;
3907             var el = this.getEl();
3908
3909             if (attr == 'scroll') {
3910                 val = [ el.scrollLeft, el.scrollTop ];
3911             } else {
3912                 val = superclass.getAttribute.call(this, attr);
3913             }
3914
3915             return val;
3916         };
3917
3918         proto.setAttribute = function(attr, val, unit) {
3919             var el = this.getEl();
3920
3921             if (attr == 'scroll') {
3922                 el.scrollLeft = val[0];
3923                 el.scrollTop = val[1];
3924             } else {
3925                 superclass.setAttribute.call(this, attr, val, unit);
3926             }
3927         };
3928     })();
3929 /*
3930  * Based on:
3931  * Ext JS Library 1.1.1
3932  * Copyright(c) 2006-2007, Ext JS, LLC.
3933  *
3934  * Originally Released Under LGPL - original licence link has changed is not relivant.
3935  *
3936  * Fork - LGPL
3937  * <script type="text/javascript">
3938  */
3939  
3940
3941 /**
3942  * @class Roo.DomHelper
3943  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3944  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3945  * @singleton
3946  */
3947 Roo.DomHelper = function(){
3948     var tempTableEl = null;
3949     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3950     var tableRe = /^table|tbody|tr|td$/i;
3951     var xmlns = {};
3952     // build as innerHTML where available
3953     /** @ignore */
3954     var createHtml = function(o){
3955         if(typeof o == 'string'){
3956             return o;
3957         }
3958         var b = "";
3959         if(!o.tag){
3960             o.tag = "div";
3961         }
3962         b += "<" + o.tag;
3963         for(var attr in o){
3964             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3965             if(attr == "style"){
3966                 var s = o["style"];
3967                 if(typeof s == "function"){
3968                     s = s.call();
3969                 }
3970                 if(typeof s == "string"){
3971                     b += ' style="' + s + '"';
3972                 }else if(typeof s == "object"){
3973                     b += ' style="';
3974                     for(var key in s){
3975                         if(typeof s[key] != "function"){
3976                             b += key + ":" + s[key] + ";";
3977                         }
3978                     }
3979                     b += '"';
3980                 }
3981             }else{
3982                 if(attr == "cls"){
3983                     b += ' class="' + o["cls"] + '"';
3984                 }else if(attr == "htmlFor"){
3985                     b += ' for="' + o["htmlFor"] + '"';
3986                 }else{
3987                     b += " " + attr + '="' + o[attr] + '"';
3988                 }
3989             }
3990         }
3991         if(emptyTags.test(o.tag)){
3992             b += "/>";
3993         }else{
3994             b += ">";
3995             var cn = o.children || o.cn;
3996             if(cn){
3997                 //http://bugs.kde.org/show_bug.cgi?id=71506
3998                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3999                     for(var i = 0, len = cn.length; i < len; i++) {
4000                         b += createHtml(cn[i], b);
4001                     }
4002                 }else{
4003                     b += createHtml(cn, b);
4004                 }
4005             }
4006             if(o.html){
4007                 b += o.html;
4008             }
4009             b += "</" + o.tag + ">";
4010         }
4011         return b;
4012     };
4013
4014     // build as dom
4015     /** @ignore */
4016     var createDom = function(o, parentNode){
4017          
4018         // defininition craeted..
4019         var ns = false;
4020         if (o.ns && o.ns != 'html') {
4021                
4022             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4023                 xmlns[o.ns] = o.xmlns;
4024                 ns = o.xmlns;
4025             }
4026             if (typeof(xmlns[o.ns]) == 'undefined') {
4027                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4028             }
4029             ns = xmlns[o.ns];
4030         }
4031         
4032         
4033         if (typeof(o) == 'string') {
4034             return parentNode.appendChild(document.createTextNode(o));
4035         }
4036         o.tag = o.tag || div;
4037         if (o.ns && Roo.isIE) {
4038             ns = false;
4039             o.tag = o.ns + ':' + o.tag;
4040             
4041         }
4042         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4043         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4044         for(var attr in o){
4045             
4046             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4047                     attr == "style" || typeof o[attr] == "function") continue;
4048                     
4049             if(attr=="cls" && Roo.isIE){
4050                 el.className = o["cls"];
4051             }else{
4052                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4053                 else el[attr] = o[attr];
4054             }
4055         }
4056         Roo.DomHelper.applyStyles(el, o.style);
4057         var cn = o.children || o.cn;
4058         if(cn){
4059             //http://bugs.kde.org/show_bug.cgi?id=71506
4060              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4061                 for(var i = 0, len = cn.length; i < len; i++) {
4062                     createDom(cn[i], el);
4063                 }
4064             }else{
4065                 createDom(cn, el);
4066             }
4067         }
4068         if(o.html){
4069             el.innerHTML = o.html;
4070         }
4071         if(parentNode){
4072            parentNode.appendChild(el);
4073         }
4074         return el;
4075     };
4076
4077     var ieTable = function(depth, s, h, e){
4078         tempTableEl.innerHTML = [s, h, e].join('');
4079         var i = -1, el = tempTableEl;
4080         while(++i < depth){
4081             el = el.firstChild;
4082         }
4083         return el;
4084     };
4085
4086     // kill repeat to save bytes
4087     var ts = '<table>',
4088         te = '</table>',
4089         tbs = ts+'<tbody>',
4090         tbe = '</tbody>'+te,
4091         trs = tbs + '<tr>',
4092         tre = '</tr>'+tbe;
4093
4094     /**
4095      * @ignore
4096      * Nasty code for IE's broken table implementation
4097      */
4098     var insertIntoTable = function(tag, where, el, html){
4099         if(!tempTableEl){
4100             tempTableEl = document.createElement('div');
4101         }
4102         var node;
4103         var before = null;
4104         if(tag == 'td'){
4105             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4106                 return;
4107             }
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111             } else{
4112                 before = el.nextSibling;
4113                 el = el.parentNode;
4114             }
4115             node = ieTable(4, trs, html, tre);
4116         }
4117         else if(tag == 'tr'){
4118             if(where == 'beforebegin'){
4119                 before = el;
4120                 el = el.parentNode;
4121                 node = ieTable(3, tbs, html, tbe);
4122             } else if(where == 'afterend'){
4123                 before = el.nextSibling;
4124                 el = el.parentNode;
4125                 node = ieTable(3, tbs, html, tbe);
4126             } else{ // INTO a TR
4127                 if(where == 'afterbegin'){
4128                     before = el.firstChild;
4129                 }
4130                 node = ieTable(4, trs, html, tre);
4131             }
4132         } else if(tag == 'tbody'){
4133             if(where == 'beforebegin'){
4134                 before = el;
4135                 el = el.parentNode;
4136                 node = ieTable(2, ts, html, te);
4137             } else if(where == 'afterend'){
4138                 before = el.nextSibling;
4139                 el = el.parentNode;
4140                 node = ieTable(2, ts, html, te);
4141             } else{
4142                 if(where == 'afterbegin'){
4143                     before = el.firstChild;
4144                 }
4145                 node = ieTable(3, tbs, html, tbe);
4146             }
4147         } else{ // TABLE
4148             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4149                 return;
4150             }
4151             if(where == 'afterbegin'){
4152                 before = el.firstChild;
4153             }
4154             node = ieTable(2, ts, html, te);
4155         }
4156         el.insertBefore(node, before);
4157         return node;
4158     };
4159
4160     return {
4161     /** True to force the use of DOM instead of html fragments @type Boolean */
4162     useDom : false,
4163
4164     /**
4165      * Returns the markup for the passed Element(s) config
4166      * @param {Object} o The Dom object spec (and children)
4167      * @return {String}
4168      */
4169     markup : function(o){
4170         return createHtml(o);
4171     },
4172
4173     /**
4174      * Applies a style specification to an element
4175      * @param {String/HTMLElement} el The element to apply styles to
4176      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4177      * a function which returns such a specification.
4178      */
4179     applyStyles : function(el, styles){
4180         if(styles){
4181            el = Roo.fly(el);
4182            if(typeof styles == "string"){
4183                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4184                var matches;
4185                while ((matches = re.exec(styles)) != null){
4186                    el.setStyle(matches[1], matches[2]);
4187                }
4188            }else if (typeof styles == "object"){
4189                for (var style in styles){
4190                   el.setStyle(style, styles[style]);
4191                }
4192            }else if (typeof styles == "function"){
4193                 Roo.DomHelper.applyStyles(el, styles.call());
4194            }
4195         }
4196     },
4197
4198     /**
4199      * Inserts an HTML fragment into the Dom
4200      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4201      * @param {HTMLElement} el The context element
4202      * @param {String} html The HTML fragmenet
4203      * @return {HTMLElement} The new node
4204      */
4205     insertHtml : function(where, el, html){
4206         where = where.toLowerCase();
4207         if(el.insertAdjacentHTML){
4208             if(tableRe.test(el.tagName)){
4209                 var rs;
4210                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4211                     return rs;
4212                 }
4213             }
4214             switch(where){
4215                 case "beforebegin":
4216                     el.insertAdjacentHTML('BeforeBegin', html);
4217                     return el.previousSibling;
4218                 case "afterbegin":
4219                     el.insertAdjacentHTML('AfterBegin', html);
4220                     return el.firstChild;
4221                 case "beforeend":
4222                     el.insertAdjacentHTML('BeforeEnd', html);
4223                     return el.lastChild;
4224                 case "afterend":
4225                     el.insertAdjacentHTML('AfterEnd', html);
4226                     return el.nextSibling;
4227             }
4228             throw 'Illegal insertion point -> "' + where + '"';
4229         }
4230         var range = el.ownerDocument.createRange();
4231         var frag;
4232         switch(where){
4233              case "beforebegin":
4234                 range.setStartBefore(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el);
4237                 return el.previousSibling;
4238              case "afterbegin":
4239                 if(el.firstChild){
4240                     range.setStartBefore(el.firstChild);
4241                     frag = range.createContextualFragment(html);
4242                     el.insertBefore(frag, el.firstChild);
4243                     return el.firstChild;
4244                 }else{
4245                     el.innerHTML = html;
4246                     return el.firstChild;
4247                 }
4248             case "beforeend":
4249                 if(el.lastChild){
4250                     range.setStartAfter(el.lastChild);
4251                     frag = range.createContextualFragment(html);
4252                     el.appendChild(frag);
4253                     return el.lastChild;
4254                 }else{
4255                     el.innerHTML = html;
4256                     return el.lastChild;
4257                 }
4258             case "afterend":
4259                 range.setStartAfter(el);
4260                 frag = range.createContextualFragment(html);
4261                 el.parentNode.insertBefore(frag, el.nextSibling);
4262                 return el.nextSibling;
4263             }
4264             throw 'Illegal insertion point -> "' + where + '"';
4265     },
4266
4267     /**
4268      * Creates new Dom element(s) and inserts them before el
4269      * @param {String/HTMLElement/Element} el The context element
4270      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4271      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4272      * @return {HTMLElement/Roo.Element} The new node
4273      */
4274     insertBefore : function(el, o, returnElement){
4275         return this.doInsert(el, o, returnElement, "beforeBegin");
4276     },
4277
4278     /**
4279      * Creates new Dom element(s) and inserts them after el
4280      * @param {String/HTMLElement/Element} el The context element
4281      * @param {Object} o The Dom object spec (and children)
4282      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4283      * @return {HTMLElement/Roo.Element} The new node
4284      */
4285     insertAfter : function(el, o, returnElement){
4286         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and inserts them as the first child of el
4291      * @param {String/HTMLElement/Element} el The context element
4292      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4293      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4294      * @return {HTMLElement/Roo.Element} The new node
4295      */
4296     insertFirst : function(el, o, returnElement){
4297         return this.doInsert(el, o, returnElement, "afterBegin");
4298     },
4299
4300     // private
4301     doInsert : function(el, o, returnElement, pos, sibling){
4302         el = Roo.getDom(el);
4303         var newNode;
4304         if(this.useDom || o.ns){
4305             newNode = createDom(o, null);
4306             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4307         }else{
4308             var html = createHtml(o);
4309             newNode = this.insertHtml(pos, el, html);
4310         }
4311         return returnElement ? Roo.get(newNode, true) : newNode;
4312     },
4313
4314     /**
4315      * Creates new Dom element(s) and appends them to el
4316      * @param {String/HTMLElement/Element} el The context element
4317      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4318      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4319      * @return {HTMLElement/Roo.Element} The new node
4320      */
4321     append : function(el, o, returnElement){
4322         el = Roo.getDom(el);
4323         var newNode;
4324         if(this.useDom || o.ns){
4325             newNode = createDom(o, null);
4326             el.appendChild(newNode);
4327         }else{
4328             var html = createHtml(o);
4329             newNode = this.insertHtml("beforeEnd", el, html);
4330         }
4331         return returnElement ? Roo.get(newNode, true) : newNode;
4332     },
4333
4334     /**
4335      * Creates new Dom element(s) and overwrites the contents of el with them
4336      * @param {String/HTMLElement/Element} el The context element
4337      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4338      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4339      * @return {HTMLElement/Roo.Element} The new node
4340      */
4341     overwrite : function(el, o, returnElement){
4342         el = Roo.getDom(el);
4343         if (o.ns) {
4344           
4345             while (el.childNodes.length) {
4346                 el.removeChild(el.firstChild);
4347             }
4348             createDom(o, el);
4349         } else {
4350             el.innerHTML = createHtml(o);   
4351         }
4352         
4353         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4354     },
4355
4356     /**
4357      * Creates a new Roo.DomHelper.Template from the Dom object spec
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {Roo.DomHelper.Template} The new template
4360      */
4361     createTemplate : function(o){
4362         var html = createHtml(o);
4363         return new Roo.Template(html);
4364     }
4365     };
4366 }();
4367 /*
4368  * Based on:
4369  * Ext JS Library 1.1.1
4370  * Copyright(c) 2006-2007, Ext JS, LLC.
4371  *
4372  * Originally Released Under LGPL - original licence link has changed is not relivant.
4373  *
4374  * Fork - LGPL
4375  * <script type="text/javascript">
4376  */
4377  
4378 /**
4379 * @class Roo.Template
4380 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4381 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4382 * Usage:
4383 <pre><code>
4384 var t = new Roo.Template(
4385     '&lt;div name="{id}"&gt;',
4386         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4387     '&lt;/div&gt;'
4388 );
4389 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4390 </code></pre>
4391 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4392 * @constructor
4393 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4394 */
4395 Roo.Template = function(html){
4396     if(html instanceof Array){
4397         html = html.join("");
4398     }else if(arguments.length > 1){
4399         html = Array.prototype.join.call(arguments, "");
4400     }
4401     /**@private*/
4402     this.html = html;
4403     
4404 };
4405 Roo.Template.prototype = {
4406     /**
4407      * Returns an HTML fragment of this template with the specified values applied.
4408      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4409      * @return {String} The HTML fragment
4410      */
4411     applyTemplate : function(values){
4412         if(this.compiled){
4413             return this.compiled(values);
4414         }
4415         var useF = this.disableFormats !== true;
4416         var fm = Roo.util.Format, tpl = this;
4417         var fn = function(m, name, format, args){
4418             if(format && useF){
4419                 if(format.substr(0, 5) == "this."){
4420                     return tpl.call(format.substr(5), values[name], values);
4421                 }else{
4422                     if(args){
4423                         // quoted values are required for strings in compiled templates, 
4424                         // but for non compiled we need to strip them
4425                         // quoted reversed for jsmin
4426                         var re = /^\s*['"](.*)["']\s*$/;
4427                         args = args.split(',');
4428                         for(var i = 0, len = args.length; i < len; i++){
4429                             args[i] = args[i].replace(re, "$1");
4430                         }
4431                         args = [values[name]].concat(args);
4432                     }else{
4433                         args = [values[name]];
4434                     }
4435                     return fm[format].apply(fm, args);
4436                 }
4437             }else{
4438                 return values[name] !== undefined ? values[name] : "";
4439             }
4440         };
4441         return this.html.replace(this.re, fn);
4442     },
4443     
4444     /**
4445      * Sets the HTML used as the template and optionally compiles it.
4446      * @param {String} html
4447      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4448      * @return {Roo.Template} this
4449      */
4450     set : function(html, compile){
4451         this.html = html;
4452         this.compiled = null;
4453         if(compile){
4454             this.compile();
4455         }
4456         return this;
4457     },
4458     
4459     /**
4460      * True to disable format functions (defaults to false)
4461      * @type Boolean
4462      */
4463     disableFormats : false,
4464     
4465     /**
4466     * The regular expression used to match template variables 
4467     * @type RegExp
4468     * @property 
4469     */
4470     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4471     
4472     /**
4473      * Compiles the template into an internal function, eliminating the RegEx overhead.
4474      * @return {Roo.Template} this
4475      */
4476     compile : function(){
4477         var fm = Roo.util.Format;
4478         var useF = this.disableFormats !== true;
4479         var sep = Roo.isGecko ? "+" : ",";
4480         var fn = function(m, name, format, args){
4481             if(format && useF){
4482                 args = args ? ',' + args : "";
4483                 if(format.substr(0, 5) != "this."){
4484                     format = "fm." + format + '(';
4485                 }else{
4486                     format = 'this.call("'+ format.substr(5) + '", ';
4487                     args = ", values";
4488                 }
4489             }else{
4490                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4491             }
4492             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4493         };
4494         var body;
4495         // branched to use + in gecko and [].join() in others
4496         if(Roo.isGecko){
4497             body = "this.compiled = function(values){ return '" +
4498                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4499                     "';};";
4500         }else{
4501             body = ["this.compiled = function(values){ return ['"];
4502             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4503             body.push("'].join('');};");
4504             body = body.join('');
4505         }
4506         /**
4507          * eval:var:values
4508          * eval:var:fm
4509          */
4510         eval(body);
4511         return this;
4512     },
4513     
4514     // private function used to call members
4515     call : function(fnName, value, allValues){
4516         return this[fnName](value, allValues);
4517     },
4518     
4519     /**
4520      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4521      * @param {String/HTMLElement/Roo.Element} el The context element
4522      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4523      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4524      * @return {HTMLElement/Roo.Element} The new node or Element
4525      */
4526     insertFirst: function(el, values, returnElement){
4527         return this.doInsert('afterBegin', el, values, returnElement);
4528     },
4529
4530     /**
4531      * Applies the supplied values to the template and inserts the new node(s) before el.
4532      * @param {String/HTMLElement/Roo.Element} el The context element
4533      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4534      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4535      * @return {HTMLElement/Roo.Element} The new node or Element
4536      */
4537     insertBefore: function(el, values, returnElement){
4538         return this.doInsert('beforeBegin', el, values, returnElement);
4539     },
4540
4541     /**
4542      * Applies the supplied values to the template and inserts the new node(s) after el.
4543      * @param {String/HTMLElement/Roo.Element} el The context element
4544      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4545      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4546      * @return {HTMLElement/Roo.Element} The new node or Element
4547      */
4548     insertAfter : function(el, values, returnElement){
4549         return this.doInsert('afterEnd', el, values, returnElement);
4550     },
4551     
4552     /**
4553      * Applies the supplied values to the template and appends the new node(s) to el.
4554      * @param {String/HTMLElement/Roo.Element} el The context element
4555      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4556      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4557      * @return {HTMLElement/Roo.Element} The new node or Element
4558      */
4559     append : function(el, values, returnElement){
4560         return this.doInsert('beforeEnd', el, values, returnElement);
4561     },
4562
4563     doInsert : function(where, el, values, returnEl){
4564         el = Roo.getDom(el);
4565         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4566         return returnEl ? Roo.get(newNode, true) : newNode;
4567     },
4568
4569     /**
4570      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4571      * @param {String/HTMLElement/Roo.Element} el The context element
4572      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4573      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4574      * @return {HTMLElement/Roo.Element} The new node or Element
4575      */
4576     overwrite : function(el, values, returnElement){
4577         el = Roo.getDom(el);
4578         el.innerHTML = this.applyTemplate(values);
4579         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4580     }
4581 };
4582 /**
4583  * Alias for {@link #applyTemplate}
4584  * @method
4585  */
4586 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4587
4588 // backwards compat
4589 Roo.DomHelper.Template = Roo.Template;
4590
4591 /**
4592  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4593  * @param {String/HTMLElement} el A DOM element or its id
4594  * @returns {Roo.Template} The created template
4595  * @static
4596  */
4597 Roo.Template.from = function(el){
4598     el = Roo.getDom(el);
4599     return new Roo.Template(el.value || el.innerHTML);
4600 };/*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611
4612 /*
4613  * This is code is also distributed under MIT license for use
4614  * with jQuery and prototype JavaScript libraries.
4615  */
4616 /**
4617  * @class Roo.DomQuery
4618 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4619 <p>
4620 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4621
4622 <p>
4623 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4624 </p>
4625 <h4>Element Selectors:</h4>
4626 <ul class="list">
4627     <li> <b>*</b> any element</li>
4628     <li> <b>E</b> an element with the tag E</li>
4629     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4630     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4631     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4632     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4633 </ul>
4634 <h4>Attribute Selectors:</h4>
4635 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4636 <ul class="list">
4637     <li> <b>E[foo]</b> has an attribute "foo"</li>
4638     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4639     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4640     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4641     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4642     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4643     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4644 </ul>
4645 <h4>Pseudo Classes:</h4>
4646 <ul class="list">
4647     <li> <b>E:first-child</b> E is the first child of its parent</li>
4648     <li> <b>E:last-child</b> E is the last child of its parent</li>
4649     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4650     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4651     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4652     <li> <b>E:only-child</b> E is the only child of its parent</li>
4653     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4654     <li> <b>E:first</b> the first E in the resultset</li>
4655     <li> <b>E:last</b> the last E in the resultset</li>
4656     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4657     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4658     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4659     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4660     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4661     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4662     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4663     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4664     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4665 </ul>
4666 <h4>CSS Value Selectors:</h4>
4667 <ul class="list">
4668     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4669     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4670     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4671     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4672     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4673     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4674 </ul>
4675  * @singleton
4676  */
4677 Roo.DomQuery = function(){
4678     var cache = {}, simpleCache = {}, valueCache = {};
4679     var nonSpace = /\S/;
4680     var trimRe = /^\s+|\s+$/g;
4681     var tplRe = /\{(\d+)\}/g;
4682     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4683     var tagTokenRe = /^(#)?([\w-\*]+)/;
4684     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4685
4686     function child(p, index){
4687         var i = 0;
4688         var n = p.firstChild;
4689         while(n){
4690             if(n.nodeType == 1){
4691                if(++i == index){
4692                    return n;
4693                }
4694             }
4695             n = n.nextSibling;
4696         }
4697         return null;
4698     };
4699
4700     function next(n){
4701         while((n = n.nextSibling) && n.nodeType != 1);
4702         return n;
4703     };
4704
4705     function prev(n){
4706         while((n = n.previousSibling) && n.nodeType != 1);
4707         return n;
4708     };
4709
4710     function children(d){
4711         var n = d.firstChild, ni = -1;
4712             while(n){
4713                 var nx = n.nextSibling;
4714                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4715                     d.removeChild(n);
4716                 }else{
4717                     n.nodeIndex = ++ni;
4718                 }
4719                 n = nx;
4720             }
4721             return this;
4722         };
4723
4724     function byClassName(c, a, v){
4725         if(!v){
4726             return c;
4727         }
4728         var r = [], ri = -1, cn;
4729         for(var i = 0, ci; ci = c[i]; i++){
4730             if((' '+ci.className+' ').indexOf(v) != -1){
4731                 r[++ri] = ci;
4732             }
4733         }
4734         return r;
4735     };
4736
4737     function attrValue(n, attr){
4738         if(!n.tagName && typeof n.length != "undefined"){
4739             n = n[0];
4740         }
4741         if(!n){
4742             return null;
4743         }
4744         if(attr == "for"){
4745             return n.htmlFor;
4746         }
4747         if(attr == "class" || attr == "className"){
4748             return n.className;
4749         }
4750         return n.getAttribute(attr) || n[attr];
4751
4752     };
4753
4754     function getNodes(ns, mode, tagName){
4755         var result = [], ri = -1, cs;
4756         if(!ns){
4757             return result;
4758         }
4759         tagName = tagName || "*";
4760         if(typeof ns.getElementsByTagName != "undefined"){
4761             ns = [ns];
4762         }
4763         if(!mode){
4764             for(var i = 0, ni; ni = ns[i]; i++){
4765                 cs = ni.getElementsByTagName(tagName);
4766                 for(var j = 0, ci; ci = cs[j]; j++){
4767                     result[++ri] = ci;
4768                 }
4769             }
4770         }else if(mode == "/" || mode == ">"){
4771             var utag = tagName.toUpperCase();
4772             for(var i = 0, ni, cn; ni = ns[i]; i++){
4773                 cn = ni.children || ni.childNodes;
4774                 for(var j = 0, cj; cj = cn[j]; j++){
4775                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4776                         result[++ri] = cj;
4777                     }
4778                 }
4779             }
4780         }else if(mode == "+"){
4781             var utag = tagName.toUpperCase();
4782             for(var i = 0, n; n = ns[i]; i++){
4783                 while((n = n.nextSibling) && n.nodeType != 1);
4784                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4785                     result[++ri] = n;
4786                 }
4787             }
4788         }else if(mode == "~"){
4789             for(var i = 0, n; n = ns[i]; i++){
4790                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4791                 if(n){
4792                     result[++ri] = n;
4793                 }
4794             }
4795         }
4796         return result;
4797     };
4798
4799     function concat(a, b){
4800         if(b.slice){
4801             return a.concat(b);
4802         }
4803         for(var i = 0, l = b.length; i < l; i++){
4804             a[a.length] = b[i];
4805         }
4806         return a;
4807     }
4808
4809     function byTag(cs, tagName){
4810         if(cs.tagName || cs == document){
4811             cs = [cs];
4812         }
4813         if(!tagName){
4814             return cs;
4815         }
4816         var r = [], ri = -1;
4817         tagName = tagName.toLowerCase();
4818         for(var i = 0, ci; ci = cs[i]; i++){
4819             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4820                 r[++ri] = ci;
4821             }
4822         }
4823         return r;
4824     };
4825
4826     function byId(cs, attr, id){
4827         if(cs.tagName || cs == document){
4828             cs = [cs];
4829         }
4830         if(!id){
4831             return cs;
4832         }
4833         var r = [], ri = -1;
4834         for(var i = 0,ci; ci = cs[i]; i++){
4835             if(ci && ci.id == id){
4836                 r[++ri] = ci;
4837                 return r;
4838             }
4839         }
4840         return r;
4841     };
4842
4843     function byAttribute(cs, attr, value, op, custom){
4844         var r = [], ri = -1, st = custom=="{";
4845         var f = Roo.DomQuery.operators[op];
4846         for(var i = 0, ci; ci = cs[i]; i++){
4847             var a;
4848             if(st){
4849                 a = Roo.DomQuery.getStyle(ci, attr);
4850             }
4851             else if(attr == "class" || attr == "className"){
4852                 a = ci.className;
4853             }else if(attr == "for"){
4854                 a = ci.htmlFor;
4855             }else if(attr == "href"){
4856                 a = ci.getAttribute("href", 2);
4857             }else{
4858                 a = ci.getAttribute(attr);
4859             }
4860             if((f && f(a, value)) || (!f && a)){
4861                 r[++ri] = ci;
4862             }
4863         }
4864         return r;
4865     };
4866
4867     function byPseudo(cs, name, value){
4868         return Roo.DomQuery.pseudos[name](cs, value);
4869     };
4870
4871     // This is for IE MSXML which does not support expandos.
4872     // IE runs the same speed using setAttribute, however FF slows way down
4873     // and Safari completely fails so they need to continue to use expandos.
4874     var isIE = window.ActiveXObject ? true : false;
4875
4876     // this eval is stop the compressor from
4877     // renaming the variable to something shorter
4878     
4879     /** eval:var:batch */
4880     var batch = 30803; 
4881
4882     var key = 30803;
4883
4884     function nodupIEXml(cs){
4885         var d = ++key;
4886         cs[0].setAttribute("_nodup", d);
4887         var r = [cs[0]];
4888         for(var i = 1, len = cs.length; i < len; i++){
4889             var c = cs[i];
4890             if(!c.getAttribute("_nodup") != d){
4891                 c.setAttribute("_nodup", d);
4892                 r[r.length] = c;
4893             }
4894         }
4895         for(var i = 0, len = cs.length; i < len; i++){
4896             cs[i].removeAttribute("_nodup");
4897         }
4898         return r;
4899     }
4900
4901     function nodup(cs){
4902         if(!cs){
4903             return [];
4904         }
4905         var len = cs.length, c, i, r = cs, cj, ri = -1;
4906         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4907             return cs;
4908         }
4909         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4910             return nodupIEXml(cs);
4911         }
4912         var d = ++key;
4913         cs[0]._nodup = d;
4914         for(i = 1; c = cs[i]; i++){
4915             if(c._nodup != d){
4916                 c._nodup = d;
4917             }else{
4918                 r = [];
4919                 for(var j = 0; j < i; j++){
4920                     r[++ri] = cs[j];
4921                 }
4922                 for(j = i+1; cj = cs[j]; j++){
4923                     if(cj._nodup != d){
4924                         cj._nodup = d;
4925                         r[++ri] = cj;
4926                     }
4927                 }
4928                 return r;
4929             }
4930         }
4931         return r;
4932     }
4933
4934     function quickDiffIEXml(c1, c2){
4935         var d = ++key;
4936         for(var i = 0, len = c1.length; i < len; i++){
4937             c1[i].setAttribute("_qdiff", d);
4938         }
4939         var r = [];
4940         for(var i = 0, len = c2.length; i < len; i++){
4941             if(c2[i].getAttribute("_qdiff") != d){
4942                 r[r.length] = c2[i];
4943             }
4944         }
4945         for(var i = 0, len = c1.length; i < len; i++){
4946            c1[i].removeAttribute("_qdiff");
4947         }
4948         return r;
4949     }
4950
4951     function quickDiff(c1, c2){
4952         var len1 = c1.length;
4953         if(!len1){
4954             return c2;
4955         }
4956         if(isIE && c1[0].selectSingleNode){
4957             return quickDiffIEXml(c1, c2);
4958         }
4959         var d = ++key;
4960         for(var i = 0; i < len1; i++){
4961             c1[i]._qdiff = d;
4962         }
4963         var r = [];
4964         for(var i = 0, len = c2.length; i < len; i++){
4965             if(c2[i]._qdiff != d){
4966                 r[r.length] = c2[i];
4967             }
4968         }
4969         return r;
4970     }
4971
4972     function quickId(ns, mode, root, id){
4973         if(ns == root){
4974            var d = root.ownerDocument || root;
4975            return d.getElementById(id);
4976         }
4977         ns = getNodes(ns, mode, "*");
4978         return byId(ns, null, id);
4979     }
4980
4981     return {
4982         getStyle : function(el, name){
4983             return Roo.fly(el).getStyle(name);
4984         },
4985         /**
4986          * Compiles a selector/xpath query into a reusable function. The returned function
4987          * takes one parameter "root" (optional), which is the context node from where the query should start.
4988          * @param {String} selector The selector/xpath query
4989          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4990          * @return {Function}
4991          */
4992         compile : function(path, type){
4993             type = type || "select";
4994             
4995             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4996             var q = path, mode, lq;
4997             var tk = Roo.DomQuery.matchers;
4998             var tklen = tk.length;
4999             var mm;
5000
5001             // accept leading mode switch
5002             var lmode = q.match(modeRe);
5003             if(lmode && lmode[1]){
5004                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5005                 q = q.replace(lmode[1], "");
5006             }
5007             // strip leading slashes
5008             while(path.substr(0, 1)=="/"){
5009                 path = path.substr(1);
5010             }
5011
5012             while(q && lq != q){
5013                 lq = q;
5014                 var tm = q.match(tagTokenRe);
5015                 if(type == "select"){
5016                     if(tm){
5017                         if(tm[1] == "#"){
5018                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5019                         }else{
5020                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5021                         }
5022                         q = q.replace(tm[0], "");
5023                     }else if(q.substr(0, 1) != '@'){
5024                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5025                     }
5026                 }else{
5027                     if(tm){
5028                         if(tm[1] == "#"){
5029                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5030                         }else{
5031                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5032                         }
5033                         q = q.replace(tm[0], "");
5034                     }
5035                 }
5036                 while(!(mm = q.match(modeRe))){
5037                     var matched = false;
5038                     for(var j = 0; j < tklen; j++){
5039                         var t = tk[j];
5040                         var m = q.match(t.re);
5041                         if(m){
5042                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5043                                                     return m[i];
5044                                                 });
5045                             q = q.replace(m[0], "");
5046                             matched = true;
5047                             break;
5048                         }
5049                     }
5050                     // prevent infinite loop on bad selector
5051                     if(!matched){
5052                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5053                     }
5054                 }
5055                 if(mm[1]){
5056                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5057                     q = q.replace(mm[1], "");
5058                 }
5059             }
5060             fn[fn.length] = "return nodup(n);\n}";
5061             
5062              /** 
5063               * list of variables that need from compression as they are used by eval.
5064              *  eval:var:batch 
5065              *  eval:var:nodup
5066              *  eval:var:byTag
5067              *  eval:var:ById
5068              *  eval:var:getNodes
5069              *  eval:var:quickId
5070              *  eval:var:mode
5071              *  eval:var:root
5072              *  eval:var:n
5073              *  eval:var:byClassName
5074              *  eval:var:byPseudo
5075              *  eval:var:byAttribute
5076              *  eval:var:attrValue
5077              * 
5078              **/ 
5079             eval(fn.join(""));
5080             return f;
5081         },
5082
5083         /**
5084          * Selects a group of elements.
5085          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5086          * @param {Node} root (optional) The start of the query (defaults to document).
5087          * @return {Array}
5088          */
5089         select : function(path, root, type){
5090             if(!root || root == document){
5091                 root = document;
5092             }
5093             if(typeof root == "string"){
5094                 root = document.getElementById(root);
5095             }
5096             var paths = path.split(",");
5097             var results = [];
5098             for(var i = 0, len = paths.length; i < len; i++){
5099                 var p = paths[i].replace(trimRe, "");
5100                 if(!cache[p]){
5101                     cache[p] = Roo.DomQuery.compile(p);
5102                     if(!cache[p]){
5103                         throw p + " is not a valid selector";
5104                     }
5105                 }
5106                 var result = cache[p](root);
5107                 if(result && result != document){
5108                     results = results.concat(result);
5109                 }
5110             }
5111             if(paths.length > 1){
5112                 return nodup(results);
5113             }
5114             return results;
5115         },
5116
5117         /**
5118          * Selects a single element.
5119          * @param {String} selector The selector/xpath query
5120          * @param {Node} root (optional) The start of the query (defaults to document).
5121          * @return {Element}
5122          */
5123         selectNode : function(path, root){
5124             return Roo.DomQuery.select(path, root)[0];
5125         },
5126
5127         /**
5128          * Selects the value of a node, optionally replacing null with the defaultValue.
5129          * @param {String} selector The selector/xpath query
5130          * @param {Node} root (optional) The start of the query (defaults to document).
5131          * @param {String} defaultValue
5132          */
5133         selectValue : function(path, root, defaultValue){
5134             path = path.replace(trimRe, "");
5135             if(!valueCache[path]){
5136                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5137             }
5138             var n = valueCache[path](root);
5139             n = n[0] ? n[0] : n;
5140             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5141             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5142         },
5143
5144         /**
5145          * Selects the value of a node, parsing integers and floats.
5146          * @param {String} selector The selector/xpath query
5147          * @param {Node} root (optional) The start of the query (defaults to document).
5148          * @param {Number} defaultValue
5149          * @return {Number}
5150          */
5151         selectNumber : function(path, root, defaultValue){
5152             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5153             return parseFloat(v);
5154         },
5155
5156         /**
5157          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5158          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5159          * @param {String} selector The simple selector to test
5160          * @return {Boolean}
5161          */
5162         is : function(el, ss){
5163             if(typeof el == "string"){
5164                 el = document.getElementById(el);
5165             }
5166             var isArray = (el instanceof Array);
5167             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5168             return isArray ? (result.length == el.length) : (result.length > 0);
5169         },
5170
5171         /**
5172          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5173          * @param {Array} el An array of elements to filter
5174          * @param {String} selector The simple selector to test
5175          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5176          * the selector instead of the ones that match
5177          * @return {Array}
5178          */
5179         filter : function(els, ss, nonMatches){
5180             ss = ss.replace(trimRe, "");
5181             if(!simpleCache[ss]){
5182                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5183             }
5184             var result = simpleCache[ss](els);
5185             return nonMatches ? quickDiff(result, els) : result;
5186         },
5187
5188         /**
5189          * Collection of matching regular expressions and code snippets.
5190          */
5191         matchers : [{
5192                 re: /^\.([\w-]+)/,
5193                 select: 'n = byClassName(n, null, " {1} ");'
5194             }, {
5195                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5196                 select: 'n = byPseudo(n, "{1}", "{2}");'
5197             },{
5198                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5199                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5200             }, {
5201                 re: /^#([\w-]+)/,
5202                 select: 'n = byId(n, null, "{1}");'
5203             },{
5204                 re: /^@([\w-]+)/,
5205                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5206             }
5207         ],
5208
5209         /**
5210          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5211          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5212          */
5213         operators : {
5214             "=" : function(a, v){
5215                 return a == v;
5216             },
5217             "!=" : function(a, v){
5218                 return a != v;
5219             },
5220             "^=" : function(a, v){
5221                 return a && a.substr(0, v.length) == v;
5222             },
5223             "$=" : function(a, v){
5224                 return a && a.substr(a.length-v.length) == v;
5225             },
5226             "*=" : function(a, v){
5227                 return a && a.indexOf(v) !== -1;
5228             },
5229             "%=" : function(a, v){
5230                 return (a % v) == 0;
5231             },
5232             "|=" : function(a, v){
5233                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5234             },
5235             "~=" : function(a, v){
5236                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5237             }
5238         },
5239
5240         /**
5241          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5242          * and the argument (if any) supplied in the selector.
5243          */
5244         pseudos : {
5245             "first-child" : function(c){
5246                 var r = [], ri = -1, n;
5247                 for(var i = 0, ci; ci = n = c[i]; i++){
5248                     while((n = n.previousSibling) && n.nodeType != 1);
5249                     if(!n){
5250                         r[++ri] = ci;
5251                     }
5252                 }
5253                 return r;
5254             },
5255
5256             "last-child" : function(c){
5257                 var r = [], ri = -1, n;
5258                 for(var i = 0, ci; ci = n = c[i]; i++){
5259                     while((n = n.nextSibling) && n.nodeType != 1);
5260                     if(!n){
5261                         r[++ri] = ci;
5262                     }
5263                 }
5264                 return r;
5265             },
5266
5267             "nth-child" : function(c, a) {
5268                 var r = [], ri = -1;
5269                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5270                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5271                 for(var i = 0, n; n = c[i]; i++){
5272                     var pn = n.parentNode;
5273                     if (batch != pn._batch) {
5274                         var j = 0;
5275                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5276                             if(cn.nodeType == 1){
5277                                cn.nodeIndex = ++j;
5278                             }
5279                         }
5280                         pn._batch = batch;
5281                     }
5282                     if (f == 1) {
5283                         if (l == 0 || n.nodeIndex == l){
5284                             r[++ri] = n;
5285                         }
5286                     } else if ((n.nodeIndex + l) % f == 0){
5287                         r[++ri] = n;
5288                     }
5289                 }
5290
5291                 return r;
5292             },
5293
5294             "only-child" : function(c){
5295                 var r = [], ri = -1;;
5296                 for(var i = 0, ci; ci = c[i]; i++){
5297                     if(!prev(ci) && !next(ci)){
5298                         r[++ri] = ci;
5299                     }
5300                 }
5301                 return r;
5302             },
5303
5304             "empty" : function(c){
5305                 var r = [], ri = -1;
5306                 for(var i = 0, ci; ci = c[i]; i++){
5307                     var cns = ci.childNodes, j = 0, cn, empty = true;
5308                     while(cn = cns[j]){
5309                         ++j;
5310                         if(cn.nodeType == 1 || cn.nodeType == 3){
5311                             empty = false;
5312                             break;
5313                         }
5314                     }
5315                     if(empty){
5316                         r[++ri] = ci;
5317                     }
5318                 }
5319                 return r;
5320             },
5321
5322             "contains" : function(c, v){
5323                 var r = [], ri = -1;
5324                 for(var i = 0, ci; ci = c[i]; i++){
5325                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5326                         r[++ri] = ci;
5327                     }
5328                 }
5329                 return r;
5330             },
5331
5332             "nodeValue" : function(c, v){
5333                 var r = [], ri = -1;
5334                 for(var i = 0, ci; ci = c[i]; i++){
5335                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5336                         r[++ri] = ci;
5337                     }
5338                 }
5339                 return r;
5340             },
5341
5342             "checked" : function(c){
5343                 var r = [], ri = -1;
5344                 for(var i = 0, ci; ci = c[i]; i++){
5345                     if(ci.checked == true){
5346                         r[++ri] = ci;
5347                     }
5348                 }
5349                 return r;
5350             },
5351
5352             "not" : function(c, ss){
5353                 return Roo.DomQuery.filter(c, ss, true);
5354             },
5355
5356             "odd" : function(c){
5357                 return this["nth-child"](c, "odd");
5358             },
5359
5360             "even" : function(c){
5361                 return this["nth-child"](c, "even");
5362             },
5363
5364             "nth" : function(c, a){
5365                 return c[a-1] || [];
5366             },
5367
5368             "first" : function(c){
5369                 return c[0] || [];
5370             },
5371
5372             "last" : function(c){
5373                 return c[c.length-1] || [];
5374             },
5375
5376             "has" : function(c, ss){
5377                 var s = Roo.DomQuery.select;
5378                 var r = [], ri = -1;
5379                 for(var i = 0, ci; ci = c[i]; i++){
5380                     if(s(ss, ci).length > 0){
5381                         r[++ri] = ci;
5382                     }
5383                 }
5384                 return r;
5385             },
5386
5387             "next" : function(c, ss){
5388                 var is = Roo.DomQuery.is;
5389                 var r = [], ri = -1;
5390                 for(var i = 0, ci; ci = c[i]; i++){
5391                     var n = next(ci);
5392                     if(n && is(n, ss)){
5393                         r[++ri] = ci;
5394                     }
5395                 }
5396                 return r;
5397             },
5398
5399             "prev" : function(c, ss){
5400                 var is = Roo.DomQuery.is;
5401                 var r = [], ri = -1;
5402                 for(var i = 0, ci; ci = c[i]; i++){
5403                     var n = prev(ci);
5404                     if(n && is(n, ss)){
5405                         r[++ri] = ci;
5406                     }
5407                 }
5408                 return r;
5409             }
5410         }
5411     };
5412 }();
5413
5414 /**
5415  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5416  * @param {String} path The selector/xpath query
5417  * @param {Node} root (optional) The start of the query (defaults to document).
5418  * @return {Array}
5419  * @member Roo
5420  * @method query
5421  */
5422 Roo.query = Roo.DomQuery.select;
5423 /*
5424  * Based on:
5425  * Ext JS Library 1.1.1
5426  * Copyright(c) 2006-2007, Ext JS, LLC.
5427  *
5428  * Originally Released Under LGPL - original licence link has changed is not relivant.
5429  *
5430  * Fork - LGPL
5431  * <script type="text/javascript">
5432  */
5433
5434 /**
5435  * @class Roo.util.Observable
5436  * Base class that provides a common interface for publishing events. Subclasses are expected to
5437  * to have a property "events" with all the events defined.<br>
5438  * For example:
5439  * <pre><code>
5440  Employee = function(name){
5441     this.name = name;
5442     this.addEvents({
5443         "fired" : true,
5444         "quit" : true
5445     });
5446  }
5447  Roo.extend(Employee, Roo.util.Observable);
5448 </code></pre>
5449  * @param {Object} config properties to use (incuding events / listeners)
5450  */
5451
5452 Roo.util.Observable = function(cfg){
5453     
5454     cfg = cfg|| {};
5455     this.addEvents(cfg.events || {});
5456     if (cfg.events) {
5457         delete cfg.events; // make sure
5458     }
5459      
5460     Roo.apply(this, cfg);
5461     
5462     if(this.listeners){
5463         this.on(this.listeners);
5464         delete this.listeners;
5465     }
5466 };
5467 Roo.util.Observable.prototype = {
5468     /** 
5469  * @cfg {Object} listeners  list of events and functions to call for this object, 
5470  * For example :
5471  * <pre><code>
5472     listeners :  { 
5473        'click' : function(e) {
5474            ..... 
5475         } ,
5476         .... 
5477     } 
5478   </code></pre>
5479  */
5480     
5481     
5482     /**
5483      * Fires the specified event with the passed parameters (minus the event name).
5484      * @param {String} eventName
5485      * @param {Object...} args Variable number of parameters are passed to handlers
5486      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5487      */
5488     fireEvent : function(){
5489         var ce = this.events[arguments[0].toLowerCase()];
5490         if(typeof ce == "object"){
5491             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5492         }else{
5493             return true;
5494         }
5495     },
5496
5497     // private
5498     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5499
5500     /**
5501      * Appends an event handler to this component
5502      * @param {String}   eventName The type of event to listen for
5503      * @param {Function} handler The method the event invokes
5504      * @param {Object}   scope (optional) The scope in which to execute the handler
5505      * function. The handler function's "this" context.
5506      * @param {Object}   options (optional) An object containing handler configuration
5507      * properties. This may contain any of the following properties:<ul>
5508      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5509      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5510      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5511      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5512      * by the specified number of milliseconds. If the event fires again within that time, the original
5513      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5514      * </ul><br>
5515      * <p>
5516      * <b>Combining Options</b><br>
5517      * Using the options argument, it is possible to combine different types of listeners:<br>
5518      * <br>
5519      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5520                 <pre><code>
5521                 el.on('click', this.onClick, this, {
5522                         single: true,
5523                 delay: 100,
5524                 forumId: 4
5525                 });
5526                 </code></pre>
5527      * <p>
5528      * <b>Attaching multiple handlers in 1 call</b><br>
5529      * The method also allows for a single argument to be passed which is a config object containing properties
5530      * which specify multiple handlers.
5531      * <pre><code>
5532                 el.on({
5533                         'click': {
5534                         fn: this.onClick,
5535                         scope: this,
5536                         delay: 100
5537                 }, 
5538                 'mouseover': {
5539                         fn: this.onMouseOver,
5540                         scope: this
5541                 },
5542                 'mouseout': {
5543                         fn: this.onMouseOut,
5544                         scope: this
5545                 }
5546                 });
5547                 </code></pre>
5548      * <p>
5549      * Or a shorthand syntax which passes the same scope object to all handlers:
5550         <pre><code>
5551                 el.on({
5552                         'click': this.onClick,
5553                 'mouseover': this.onMouseOver,
5554                 'mouseout': this.onMouseOut,
5555                 scope: this
5556                 });
5557                 </code></pre>
5558      */
5559     addListener : function(eventName, fn, scope, o){
5560         if(typeof eventName == "object"){
5561             o = eventName;
5562             for(var e in o){
5563                 if(this.filterOptRe.test(e)){
5564                     continue;
5565                 }
5566                 if(typeof o[e] == "function"){
5567                     // shared options
5568                     this.addListener(e, o[e], o.scope,  o);
5569                 }else{
5570                     // individual options
5571                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5572                 }
5573             }
5574             return;
5575         }
5576         o = (!o || typeof o == "boolean") ? {} : o;
5577         eventName = eventName.toLowerCase();
5578         var ce = this.events[eventName] || true;
5579         if(typeof ce == "boolean"){
5580             ce = new Roo.util.Event(this, eventName);
5581             this.events[eventName] = ce;
5582         }
5583         ce.addListener(fn, scope, o);
5584     },
5585
5586     /**
5587      * Removes a listener
5588      * @param {String}   eventName     The type of event to listen for
5589      * @param {Function} handler        The handler to remove
5590      * @param {Object}   scope  (optional) The scope (this object) for the handler
5591      */
5592     removeListener : function(eventName, fn, scope){
5593         var ce = this.events[eventName.toLowerCase()];
5594         if(typeof ce == "object"){
5595             ce.removeListener(fn, scope);
5596         }
5597     },
5598
5599     /**
5600      * Removes all listeners for this object
5601      */
5602     purgeListeners : function(){
5603         for(var evt in this.events){
5604             if(typeof this.events[evt] == "object"){
5605                  this.events[evt].clearListeners();
5606             }
5607         }
5608     },
5609
5610     relayEvents : function(o, events){
5611         var createHandler = function(ename){
5612             return function(){
5613                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5614             };
5615         };
5616         for(var i = 0, len = events.length; i < len; i++){
5617             var ename = events[i];
5618             if(!this.events[ename]){ this.events[ename] = true; };
5619             o.on(ename, createHandler(ename), this);
5620         }
5621     },
5622
5623     /**
5624      * Used to define events on this Observable
5625      * @param {Object} object The object with the events defined
5626      */
5627     addEvents : function(o){
5628         if(!this.events){
5629             this.events = {};
5630         }
5631         Roo.applyIf(this.events, o);
5632     },
5633
5634     /**
5635      * Checks to see if this object has any listeners for a specified event
5636      * @param {String} eventName The name of the event to check for
5637      * @return {Boolean} True if the event is being listened for, else false
5638      */
5639     hasListener : function(eventName){
5640         var e = this.events[eventName];
5641         return typeof e == "object" && e.listeners.length > 0;
5642     }
5643 };
5644 /**
5645  * Appends an event handler to this element (shorthand for addListener)
5646  * @param {String}   eventName     The type of event to listen for
5647  * @param {Function} handler        The method the event invokes
5648  * @param {Object}   scope (optional) The scope in which to execute the handler
5649  * function. The handler function's "this" context.
5650  * @param {Object}   options  (optional)
5651  * @method
5652  */
5653 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5654 /**
5655  * Removes a listener (shorthand for removeListener)
5656  * @param {String}   eventName     The type of event to listen for
5657  * @param {Function} handler        The handler to remove
5658  * @param {Object}   scope  (optional) The scope (this object) for the handler
5659  * @method
5660  */
5661 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5662
5663 /**
5664  * Starts capture on the specified Observable. All events will be passed
5665  * to the supplied function with the event name + standard signature of the event
5666  * <b>before</b> the event is fired. If the supplied function returns false,
5667  * the event will not fire.
5668  * @param {Observable} o The Observable to capture
5669  * @param {Function} fn The function to call
5670  * @param {Object} scope (optional) The scope (this object) for the fn
5671  * @static
5672  */
5673 Roo.util.Observable.capture = function(o, fn, scope){
5674     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5675 };
5676
5677 /**
5678  * Removes <b>all</b> added captures from the Observable.
5679  * @param {Observable} o The Observable to release
5680  * @static
5681  */
5682 Roo.util.Observable.releaseCapture = function(o){
5683     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5684 };
5685
5686 (function(){
5687
5688     var createBuffered = function(h, o, scope){
5689         var task = new Roo.util.DelayedTask();
5690         return function(){
5691             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5692         };
5693     };
5694
5695     var createSingle = function(h, e, fn, scope){
5696         return function(){
5697             e.removeListener(fn, scope);
5698             return h.apply(scope, arguments);
5699         };
5700     };
5701
5702     var createDelayed = function(h, o, scope){
5703         return function(){
5704             var args = Array.prototype.slice.call(arguments, 0);
5705             setTimeout(function(){
5706                 h.apply(scope, args);
5707             }, o.delay || 10);
5708         };
5709     };
5710
5711     Roo.util.Event = function(obj, name){
5712         this.name = name;
5713         this.obj = obj;
5714         this.listeners = [];
5715     };
5716
5717     Roo.util.Event.prototype = {
5718         addListener : function(fn, scope, options){
5719             var o = options || {};
5720             scope = scope || this.obj;
5721             if(!this.isListening(fn, scope)){
5722                 var l = {fn: fn, scope: scope, options: o};
5723                 var h = fn;
5724                 if(o.delay){
5725                     h = createDelayed(h, o, scope);
5726                 }
5727                 if(o.single){
5728                     h = createSingle(h, this, fn, scope);
5729                 }
5730                 if(o.buffer){
5731                     h = createBuffered(h, o, scope);
5732                 }
5733                 l.fireFn = h;
5734                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5735                     this.listeners.push(l);
5736                 }else{
5737                     this.listeners = this.listeners.slice(0);
5738                     this.listeners.push(l);
5739                 }
5740             }
5741         },
5742
5743         findListener : function(fn, scope){
5744             scope = scope || this.obj;
5745             var ls = this.listeners;
5746             for(var i = 0, len = ls.length; i < len; i++){
5747                 var l = ls[i];
5748                 if(l.fn == fn && l.scope == scope){
5749                     return i;
5750                 }
5751             }
5752             return -1;
5753         },
5754
5755         isListening : function(fn, scope){
5756             return this.findListener(fn, scope) != -1;
5757         },
5758
5759         removeListener : function(fn, scope){
5760             var index;
5761             if((index = this.findListener(fn, scope)) != -1){
5762                 if(!this.firing){
5763                     this.listeners.splice(index, 1);
5764                 }else{
5765                     this.listeners = this.listeners.slice(0);
5766                     this.listeners.splice(index, 1);
5767                 }
5768                 return true;
5769             }
5770             return false;
5771         },
5772
5773         clearListeners : function(){
5774             this.listeners = [];
5775         },
5776
5777         fire : function(){
5778             var ls = this.listeners, scope, len = ls.length;
5779             if(len > 0){
5780                 this.firing = true;
5781                 var args = Array.prototype.slice.call(arguments, 0);
5782                 for(var i = 0; i < len; i++){
5783                     var l = ls[i];
5784                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5785                         this.firing = false;
5786                         return false;
5787                     }
5788                 }
5789                 this.firing = false;
5790             }
5791             return true;
5792         }
5793     };
5794 })();/*
5795  * Based on:
5796  * Ext JS Library 1.1.1
5797  * Copyright(c) 2006-2007, Ext JS, LLC.
5798  *
5799  * Originally Released Under LGPL - original licence link has changed is not relivant.
5800  *
5801  * Fork - LGPL
5802  * <script type="text/javascript">
5803  */
5804
5805 /**
5806  * @class Roo.EventManager
5807  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5808  * several useful events directly.
5809  * See {@link Roo.EventObject} for more details on normalized event objects.
5810  * @singleton
5811  */
5812 Roo.EventManager = function(){
5813     var docReadyEvent, docReadyProcId, docReadyState = false;
5814     var resizeEvent, resizeTask, textEvent, textSize;
5815     var E = Roo.lib.Event;
5816     var D = Roo.lib.Dom;
5817
5818
5819     var fireDocReady = function(){
5820         if(!docReadyState){
5821             docReadyState = true;
5822             Roo.isReady = true;
5823             if(docReadyProcId){
5824                 clearInterval(docReadyProcId);
5825             }
5826             if(Roo.isGecko || Roo.isOpera) {
5827                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5828             }
5829             if(Roo.isIE){
5830                 var defer = document.getElementById("ie-deferred-loader");
5831                 if(defer){
5832                     defer.onreadystatechange = null;
5833                     defer.parentNode.removeChild(defer);
5834                 }
5835             }
5836             if(docReadyEvent){
5837                 docReadyEvent.fire();
5838                 docReadyEvent.clearListeners();
5839             }
5840         }
5841     };
5842     
5843     var initDocReady = function(){
5844         docReadyEvent = new Roo.util.Event();
5845         if(Roo.isGecko || Roo.isOpera) {
5846             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5847         }else if(Roo.isIE){
5848             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5849             var defer = document.getElementById("ie-deferred-loader");
5850             defer.onreadystatechange = function(){
5851                 if(this.readyState == "complete"){
5852                     fireDocReady();
5853                 }
5854             };
5855         }else if(Roo.isSafari){ 
5856             docReadyProcId = setInterval(function(){
5857                 var rs = document.readyState;
5858                 if(rs == "complete") {
5859                     fireDocReady();     
5860                  }
5861             }, 10);
5862         }
5863         // no matter what, make sure it fires on load
5864         E.on(window, "load", fireDocReady);
5865     };
5866
5867     var createBuffered = function(h, o){
5868         var task = new Roo.util.DelayedTask(h);
5869         return function(e){
5870             // create new event object impl so new events don't wipe out properties
5871             e = new Roo.EventObjectImpl(e);
5872             task.delay(o.buffer, h, null, [e]);
5873         };
5874     };
5875
5876     var createSingle = function(h, el, ename, fn){
5877         return function(e){
5878             Roo.EventManager.removeListener(el, ename, fn);
5879             h(e);
5880         };
5881     };
5882
5883     var createDelayed = function(h, o){
5884         return function(e){
5885             // create new event object impl so new events don't wipe out properties
5886             e = new Roo.EventObjectImpl(e);
5887             setTimeout(function(){
5888                 h(e);
5889             }, o.delay || 10);
5890         };
5891     };
5892
5893     var listen = function(element, ename, opt, fn, scope){
5894         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5895         fn = fn || o.fn; scope = scope || o.scope;
5896         var el = Roo.getDom(element);
5897         if(!el){
5898             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5899         }
5900         var h = function(e){
5901             e = Roo.EventObject.setEvent(e);
5902             var t;
5903             if(o.delegate){
5904                 t = e.getTarget(o.delegate, el);
5905                 if(!t){
5906                     return;
5907                 }
5908             }else{
5909                 t = e.target;
5910             }
5911             if(o.stopEvent === true){
5912                 e.stopEvent();
5913             }
5914             if(o.preventDefault === true){
5915                e.preventDefault();
5916             }
5917             if(o.stopPropagation === true){
5918                 e.stopPropagation();
5919             }
5920
5921             if(o.normalized === false){
5922                 e = e.browserEvent;
5923             }
5924
5925             fn.call(scope || el, e, t, o);
5926         };
5927         if(o.delay){
5928             h = createDelayed(h, o);
5929         }
5930         if(o.single){
5931             h = createSingle(h, el, ename, fn);
5932         }
5933         if(o.buffer){
5934             h = createBuffered(h, o);
5935         }
5936         fn._handlers = fn._handlers || [];
5937         fn._handlers.push([Roo.id(el), ename, h]);
5938
5939         E.on(el, ename, h);
5940         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5941             el.addEventListener("DOMMouseScroll", h, false);
5942             E.on(window, 'unload', function(){
5943                 el.removeEventListener("DOMMouseScroll", h, false);
5944             });
5945         }
5946         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5947             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5948         }
5949         return h;
5950     };
5951
5952     var stopListening = function(el, ename, fn){
5953         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5954         if(hds){
5955             for(var i = 0, len = hds.length; i < len; i++){
5956                 var h = hds[i];
5957                 if(h[0] == id && h[1] == ename){
5958                     hd = h[2];
5959                     hds.splice(i, 1);
5960                     break;
5961                 }
5962             }
5963         }
5964         E.un(el, ename, hd);
5965         el = Roo.getDom(el);
5966         if(ename == "mousewheel" && el.addEventListener){
5967             el.removeEventListener("DOMMouseScroll", hd, false);
5968         }
5969         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5970             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5971         }
5972     };
5973
5974     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5975     
5976     var pub = {
5977         
5978         
5979         /** 
5980          * Fix for doc tools
5981          * @scope Roo.EventManager
5982          */
5983         
5984         
5985         /** 
5986          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5987          * object with a Roo.EventObject
5988          * @param {Function} fn        The method the event invokes
5989          * @param {Object}   scope    An object that becomes the scope of the handler
5990          * @param {boolean}  override If true, the obj passed in becomes
5991          *                             the execution scope of the listener
5992          * @return {Function} The wrapped function
5993          * @deprecated
5994          */
5995         wrap : function(fn, scope, override){
5996             return function(e){
5997                 Roo.EventObject.setEvent(e);
5998                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5999             };
6000         },
6001         
6002         /**
6003      * Appends an event handler to an element (shorthand for addListener)
6004      * @param {String/HTMLElement}   element        The html element or id to assign the
6005      * @param {String}   eventName The type of event to listen for
6006      * @param {Function} handler The method the event invokes
6007      * @param {Object}   scope (optional) The scope in which to execute the handler
6008      * function. The handler function's "this" context.
6009      * @param {Object}   options (optional) An object containing handler configuration
6010      * properties. This may contain any of the following properties:<ul>
6011      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6012      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6013      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6014      * <li>preventDefault {Boolean} True to prevent the default action</li>
6015      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6016      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6017      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6018      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6019      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6020      * by the specified number of milliseconds. If the event fires again within that time, the original
6021      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6022      * </ul><br>
6023      * <p>
6024      * <b>Combining Options</b><br>
6025      * Using the options argument, it is possible to combine different types of listeners:<br>
6026      * <br>
6027      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6028      * Code:<pre><code>
6029 el.on('click', this.onClick, this, {
6030     single: true,
6031     delay: 100,
6032     stopEvent : true,
6033     forumId: 4
6034 });</code></pre>
6035      * <p>
6036      * <b>Attaching multiple handlers in 1 call</b><br>
6037       * The method also allows for a single argument to be passed which is a config object containing properties
6038      * which specify multiple handlers.
6039      * <p>
6040      * Code:<pre><code>
6041 el.on({
6042     'click' : {
6043         fn: this.onClick
6044         scope: this,
6045         delay: 100
6046     },
6047     'mouseover' : {
6048         fn: this.onMouseOver
6049         scope: this
6050     },
6051     'mouseout' : {
6052         fn: this.onMouseOut
6053         scope: this
6054     }
6055 });</code></pre>
6056      * <p>
6057      * Or a shorthand syntax:<br>
6058      * Code:<pre><code>
6059 el.on({
6060     'click' : this.onClick,
6061     'mouseover' : this.onMouseOver,
6062     'mouseout' : this.onMouseOut
6063     scope: this
6064 });</code></pre>
6065      */
6066         addListener : function(element, eventName, fn, scope, options){
6067             if(typeof eventName == "object"){
6068                 var o = eventName;
6069                 for(var e in o){
6070                     if(propRe.test(e)){
6071                         continue;
6072                     }
6073                     if(typeof o[e] == "function"){
6074                         // shared options
6075                         listen(element, e, o, o[e], o.scope);
6076                     }else{
6077                         // individual options
6078                         listen(element, e, o[e]);
6079                     }
6080                 }
6081                 return;
6082             }
6083             return listen(element, eventName, options, fn, scope);
6084         },
6085         
6086         /**
6087          * Removes an event handler
6088          *
6089          * @param {String/HTMLElement}   element        The id or html element to remove the 
6090          *                             event from
6091          * @param {String}   eventName     The type of event
6092          * @param {Function} fn
6093          * @return {Boolean} True if a listener was actually removed
6094          */
6095         removeListener : function(element, eventName, fn){
6096             return stopListening(element, eventName, fn);
6097         },
6098         
6099         /**
6100          * Fires when the document is ready (before onload and before images are loaded). Can be 
6101          * accessed shorthanded Roo.onReady().
6102          * @param {Function} fn        The method the event invokes
6103          * @param {Object}   scope    An  object that becomes the scope of the handler
6104          * @param {boolean}  options
6105          */
6106         onDocumentReady : function(fn, scope, options){
6107             if(docReadyState){ // if it already fired
6108                 docReadyEvent.addListener(fn, scope, options);
6109                 docReadyEvent.fire();
6110                 docReadyEvent.clearListeners();
6111                 return;
6112             }
6113             if(!docReadyEvent){
6114                 initDocReady();
6115             }
6116             docReadyEvent.addListener(fn, scope, options);
6117         },
6118         
6119         /**
6120          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6121          * @param {Function} fn        The method the event invokes
6122          * @param {Object}   scope    An object that becomes the scope of the handler
6123          * @param {boolean}  options
6124          */
6125         onWindowResize : function(fn, scope, options){
6126             if(!resizeEvent){
6127                 resizeEvent = new Roo.util.Event();
6128                 resizeTask = new Roo.util.DelayedTask(function(){
6129                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6130                 });
6131                 E.on(window, "resize", function(){
6132                     if(Roo.isIE){
6133                         resizeTask.delay(50);
6134                     }else{
6135                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6136                     }
6137                 });
6138             }
6139             resizeEvent.addListener(fn, scope, options);
6140         },
6141
6142         /**
6143          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onTextResize : function(fn, scope, options){
6149             if(!textEvent){
6150                 textEvent = new Roo.util.Event();
6151                 var textEl = new Roo.Element(document.createElement('div'));
6152                 textEl.dom.className = 'x-text-resize';
6153                 textEl.dom.innerHTML = 'X';
6154                 textEl.appendTo(document.body);
6155                 textSize = textEl.dom.offsetHeight;
6156                 setInterval(function(){
6157                     if(textEl.dom.offsetHeight != textSize){
6158                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6159                     }
6160                 }, this.textResizeInterval);
6161             }
6162             textEvent.addListener(fn, scope, options);
6163         },
6164
6165         /**
6166          * Removes the passed window resize listener.
6167          * @param {Function} fn        The method the event invokes
6168          * @param {Object}   scope    The scope of handler
6169          */
6170         removeResizeListener : function(fn, scope){
6171             if(resizeEvent){
6172                 resizeEvent.removeListener(fn, scope);
6173             }
6174         },
6175
6176         // private
6177         fireResize : function(){
6178             if(resizeEvent){
6179                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6180             }   
6181         },
6182         /**
6183          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6184          */
6185         ieDeferSrc : false,
6186         /**
6187          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6188          */
6189         textResizeInterval : 50
6190     };
6191     
6192     /**
6193      * Fix for doc tools
6194      * @scopeAlias pub=Roo.EventManager
6195      */
6196     
6197      /**
6198      * Appends an event handler to an element (shorthand for addListener)
6199      * @param {String/HTMLElement}   element        The html element or id to assign the
6200      * @param {String}   eventName The type of event to listen for
6201      * @param {Function} handler The method the event invokes
6202      * @param {Object}   scope (optional) The scope in which to execute the handler
6203      * function. The handler function's "this" context.
6204      * @param {Object}   options (optional) An object containing handler configuration
6205      * properties. This may contain any of the following properties:<ul>
6206      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6207      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6208      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6209      * <li>preventDefault {Boolean} True to prevent the default action</li>
6210      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6211      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6212      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6213      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6214      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6215      * by the specified number of milliseconds. If the event fires again within that time, the original
6216      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6217      * </ul><br>
6218      * <p>
6219      * <b>Combining Options</b><br>
6220      * Using the options argument, it is possible to combine different types of listeners:<br>
6221      * <br>
6222      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6223      * Code:<pre><code>
6224 el.on('click', this.onClick, this, {
6225     single: true,
6226     delay: 100,
6227     stopEvent : true,
6228     forumId: 4
6229 });</code></pre>
6230      * <p>
6231      * <b>Attaching multiple handlers in 1 call</b><br>
6232       * The method also allows for a single argument to be passed which is a config object containing properties
6233      * which specify multiple handlers.
6234      * <p>
6235      * Code:<pre><code>
6236 el.on({
6237     'click' : {
6238         fn: this.onClick
6239         scope: this,
6240         delay: 100
6241     },
6242     'mouseover' : {
6243         fn: this.onMouseOver
6244         scope: this
6245     },
6246     'mouseout' : {
6247         fn: this.onMouseOut
6248         scope: this
6249     }
6250 });</code></pre>
6251      * <p>
6252      * Or a shorthand syntax:<br>
6253      * Code:<pre><code>
6254 el.on({
6255     'click' : this.onClick,
6256     'mouseover' : this.onMouseOver,
6257     'mouseout' : this.onMouseOut
6258     scope: this
6259 });</code></pre>
6260      */
6261     pub.on = pub.addListener;
6262     pub.un = pub.removeListener;
6263
6264     pub.stoppedMouseDownEvent = new Roo.util.Event();
6265     return pub;
6266 }();
6267 /**
6268   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6269   * @param {Function} fn        The method the event invokes
6270   * @param {Object}   scope    An  object that becomes the scope of the handler
6271   * @param {boolean}  override If true, the obj passed in becomes
6272   *                             the execution scope of the listener
6273   * @member Roo
6274   * @method onReady
6275  */
6276 Roo.onReady = Roo.EventManager.onDocumentReady;
6277
6278 Roo.onReady(function(){
6279     var bd = Roo.get(document.body);
6280     if(!bd){ return; }
6281
6282     var cls = [
6283             Roo.isIE ? "roo-ie"
6284             : Roo.isGecko ? "roo-gecko"
6285             : Roo.isOpera ? "roo-opera"
6286             : Roo.isSafari ? "roo-safari" : ""];
6287
6288     if(Roo.isMac){
6289         cls.push("roo-mac");
6290     }
6291     if(Roo.isLinux){
6292         cls.push("roo-linux");
6293     }
6294     if(Roo.isBorderBox){
6295         cls.push('roo-border-box');
6296     }
6297     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6298         var p = bd.dom.parentNode;
6299         if(p){
6300             p.className += ' roo-strict';
6301         }
6302     }
6303     bd.addClass(cls.join(' '));
6304 });
6305
6306 /**
6307  * @class Roo.EventObject
6308  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6309  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6310  * Example:
6311  * <pre><code>
6312  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6313     e.preventDefault();
6314     var target = e.getTarget();
6315     ...
6316  }
6317  var myDiv = Roo.get("myDiv");
6318  myDiv.on("click", handleClick);
6319  //or
6320  Roo.EventManager.on("myDiv", 'click', handleClick);
6321  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6322  </code></pre>
6323  * @singleton
6324  */
6325 Roo.EventObject = function(){
6326     
6327     var E = Roo.lib.Event;
6328     
6329     // safari keypress events for special keys return bad keycodes
6330     var safariKeys = {
6331         63234 : 37, // left
6332         63235 : 39, // right
6333         63232 : 38, // up
6334         63233 : 40, // down
6335         63276 : 33, // page up
6336         63277 : 34, // page down
6337         63272 : 46, // delete
6338         63273 : 36, // home
6339         63275 : 35  // end
6340     };
6341
6342     // normalize button clicks
6343     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6344                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6345
6346     Roo.EventObjectImpl = function(e){
6347         if(e){
6348             this.setEvent(e.browserEvent || e);
6349         }
6350     };
6351     Roo.EventObjectImpl.prototype = {
6352         /**
6353          * Used to fix doc tools.
6354          * @scope Roo.EventObject.prototype
6355          */
6356             
6357
6358         
6359         
6360         /** The normal browser event */
6361         browserEvent : null,
6362         /** The button pressed in a mouse event */
6363         button : -1,
6364         /** True if the shift key was down during the event */
6365         shiftKey : false,
6366         /** True if the control key was down during the event */
6367         ctrlKey : false,
6368         /** True if the alt key was down during the event */
6369         altKey : false,
6370
6371         /** Key constant 
6372         * @type Number */
6373         BACKSPACE : 8,
6374         /** Key constant 
6375         * @type Number */
6376         TAB : 9,
6377         /** Key constant 
6378         * @type Number */
6379         RETURN : 13,
6380         /** Key constant 
6381         * @type Number */
6382         ENTER : 13,
6383         /** Key constant 
6384         * @type Number */
6385         SHIFT : 16,
6386         /** Key constant 
6387         * @type Number */
6388         CONTROL : 17,
6389         /** Key constant 
6390         * @type Number */
6391         ESC : 27,
6392         /** Key constant 
6393         * @type Number */
6394         SPACE : 32,
6395         /** Key constant 
6396         * @type Number */
6397         PAGEUP : 33,
6398         /** Key constant 
6399         * @type Number */
6400         PAGEDOWN : 34,
6401         /** Key constant 
6402         * @type Number */
6403         END : 35,
6404         /** Key constant 
6405         * @type Number */
6406         HOME : 36,
6407         /** Key constant 
6408         * @type Number */
6409         LEFT : 37,
6410         /** Key constant 
6411         * @type Number */
6412         UP : 38,
6413         /** Key constant 
6414         * @type Number */
6415         RIGHT : 39,
6416         /** Key constant 
6417         * @type Number */
6418         DOWN : 40,
6419         /** Key constant 
6420         * @type Number */
6421         DELETE : 46,
6422         /** Key constant 
6423         * @type Number */
6424         F5 : 116,
6425
6426            /** @private */
6427         setEvent : function(e){
6428             if(e == this || (e && e.browserEvent)){ // already wrapped
6429                 return e;
6430             }
6431             this.browserEvent = e;
6432             if(e){
6433                 // normalize buttons
6434                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6435                 if(e.type == 'click' && this.button == -1){
6436                     this.button = 0;
6437                 }
6438                 this.type = e.type;
6439                 this.shiftKey = e.shiftKey;
6440                 // mac metaKey behaves like ctrlKey
6441                 this.ctrlKey = e.ctrlKey || e.metaKey;
6442                 this.altKey = e.altKey;
6443                 // in getKey these will be normalized for the mac
6444                 this.keyCode = e.keyCode;
6445                 // keyup warnings on firefox.
6446                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6447                 // cache the target for the delayed and or buffered events
6448                 this.target = E.getTarget(e);
6449                 // same for XY
6450                 this.xy = E.getXY(e);
6451             }else{
6452                 this.button = -1;
6453                 this.shiftKey = false;
6454                 this.ctrlKey = false;
6455                 this.altKey = false;
6456                 this.keyCode = 0;
6457                 this.charCode =0;
6458                 this.target = null;
6459                 this.xy = [0, 0];
6460             }
6461             return this;
6462         },
6463
6464         /**
6465          * Stop the event (preventDefault and stopPropagation)
6466          */
6467         stopEvent : function(){
6468             if(this.browserEvent){
6469                 if(this.browserEvent.type == 'mousedown'){
6470                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6471                 }
6472                 E.stopEvent(this.browserEvent);
6473             }
6474         },
6475
6476         /**
6477          * Prevents the browsers default handling of the event.
6478          */
6479         preventDefault : function(){
6480             if(this.browserEvent){
6481                 E.preventDefault(this.browserEvent);
6482             }
6483         },
6484
6485         /** @private */
6486         isNavKeyPress : function(){
6487             var k = this.keyCode;
6488             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6489             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6490         },
6491
6492         isSpecialKey : function(){
6493             var k = this.keyCode;
6494             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6495             (k == 16) || (k == 17) ||
6496             (k >= 18 && k <= 20) ||
6497             (k >= 33 && k <= 35) ||
6498             (k >= 36 && k <= 39) ||
6499             (k >= 44 && k <= 45);
6500         },
6501         /**
6502          * Cancels bubbling of the event.
6503          */
6504         stopPropagation : function(){
6505             if(this.browserEvent){
6506                 if(this.type == 'mousedown'){
6507                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6508                 }
6509                 E.stopPropagation(this.browserEvent);
6510             }
6511         },
6512
6513         /**
6514          * Gets the key code for the event.
6515          * @return {Number}
6516          */
6517         getCharCode : function(){
6518             return this.charCode || this.keyCode;
6519         },
6520
6521         /**
6522          * Returns a normalized keyCode for the event.
6523          * @return {Number} The key code
6524          */
6525         getKey : function(){
6526             var k = this.keyCode || this.charCode;
6527             return Roo.isSafari ? (safariKeys[k] || k) : k;
6528         },
6529
6530         /**
6531          * Gets the x coordinate of the event.
6532          * @return {Number}
6533          */
6534         getPageX : function(){
6535             return this.xy[0];
6536         },
6537
6538         /**
6539          * Gets the y coordinate of the event.
6540          * @return {Number}
6541          */
6542         getPageY : function(){
6543             return this.xy[1];
6544         },
6545
6546         /**
6547          * Gets the time of the event.
6548          * @return {Number}
6549          */
6550         getTime : function(){
6551             if(this.browserEvent){
6552                 return E.getTime(this.browserEvent);
6553             }
6554             return null;
6555         },
6556
6557         /**
6558          * Gets the page coordinates of the event.
6559          * @return {Array} The xy values like [x, y]
6560          */
6561         getXY : function(){
6562             return this.xy;
6563         },
6564
6565         /**
6566          * Gets the target for the event.
6567          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6568          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6569                 search as a number or element (defaults to 10 || document.body)
6570          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6571          * @return {HTMLelement}
6572          */
6573         getTarget : function(selector, maxDepth, returnEl){
6574             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6575         },
6576         /**
6577          * Gets the related target.
6578          * @return {HTMLElement}
6579          */
6580         getRelatedTarget : function(){
6581             if(this.browserEvent){
6582                 return E.getRelatedTarget(this.browserEvent);
6583             }
6584             return null;
6585         },
6586
6587         /**
6588          * Normalizes mouse wheel delta across browsers
6589          * @return {Number} The delta
6590          */
6591         getWheelDelta : function(){
6592             var e = this.browserEvent;
6593             var delta = 0;
6594             if(e.wheelDelta){ /* IE/Opera. */
6595                 delta = e.wheelDelta/120;
6596             }else if(e.detail){ /* Mozilla case. */
6597                 delta = -e.detail/3;
6598             }
6599             return delta;
6600         },
6601
6602         /**
6603          * Returns true if the control, meta, shift or alt key was pressed during this event.
6604          * @return {Boolean}
6605          */
6606         hasModifier : function(){
6607             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6608         },
6609
6610         /**
6611          * Returns true if the target of this event equals el or is a child of el
6612          * @param {String/HTMLElement/Element} el
6613          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6614          * @return {Boolean}
6615          */
6616         within : function(el, related){
6617             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6618             return t && Roo.fly(el).contains(t);
6619         },
6620
6621         getPoint : function(){
6622             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6623         }
6624     };
6625
6626     return new Roo.EventObjectImpl();
6627 }();
6628             
6629     /*
6630  * Based on:
6631  * Ext JS Library 1.1.1
6632  * Copyright(c) 2006-2007, Ext JS, LLC.
6633  *
6634  * Originally Released Under LGPL - original licence link has changed is not relivant.
6635  *
6636  * Fork - LGPL
6637  * <script type="text/javascript">
6638  */
6639
6640  
6641 // was in Composite Element!??!?!
6642  
6643 (function(){
6644     var D = Roo.lib.Dom;
6645     var E = Roo.lib.Event;
6646     var A = Roo.lib.Anim;
6647
6648     // local style camelizing for speed
6649     var propCache = {};
6650     var camelRe = /(-[a-z])/gi;
6651     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6652     var view = document.defaultView;
6653
6654 /**
6655  * @class Roo.Element
6656  * Represents an Element in the DOM.<br><br>
6657  * Usage:<br>
6658 <pre><code>
6659 var el = Roo.get("my-div");
6660
6661 // or with getEl
6662 var el = getEl("my-div");
6663
6664 // or with a DOM element
6665 var el = Roo.get(myDivElement);
6666 </code></pre>
6667  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6668  * each call instead of constructing a new one.<br><br>
6669  * <b>Animations</b><br />
6670  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6671  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6672 <pre>
6673 Option    Default   Description
6674 --------- --------  ---------------------------------------------
6675 duration  .35       The duration of the animation in seconds
6676 easing    easeOut   The YUI easing method
6677 callback  none      A function to execute when the anim completes
6678 scope     this      The scope (this) of the callback function
6679 </pre>
6680 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6681 * manipulate the animation. Here's an example:
6682 <pre><code>
6683 var el = Roo.get("my-div");
6684
6685 // no animation
6686 el.setWidth(100);
6687
6688 // default animation
6689 el.setWidth(100, true);
6690
6691 // animation with some options set
6692 el.setWidth(100, {
6693     duration: 1,
6694     callback: this.foo,
6695     scope: this
6696 });
6697
6698 // using the "anim" property to get the Anim object
6699 var opt = {
6700     duration: 1,
6701     callback: this.foo,
6702     scope: this
6703 };
6704 el.setWidth(100, opt);
6705 ...
6706 if(opt.anim.isAnimated()){
6707     opt.anim.stop();
6708 }
6709 </code></pre>
6710 * <b> Composite (Collections of) Elements</b><br />
6711  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6712  * @constructor Create a new Element directly.
6713  * @param {String/HTMLElement} element
6714  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6715  */
6716     Roo.Element = function(element, forceNew){
6717         var dom = typeof element == "string" ?
6718                 document.getElementById(element) : element;
6719         if(!dom){ // invalid id/element
6720             return null;
6721         }
6722         var id = dom.id;
6723         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6724             return Roo.Element.cache[id];
6725         }
6726
6727         /**
6728          * The DOM element
6729          * @type HTMLElement
6730          */
6731         this.dom = dom;
6732
6733         /**
6734          * The DOM element ID
6735          * @type String
6736          */
6737         this.id = id || Roo.id(dom);
6738     };
6739
6740     var El = Roo.Element;
6741
6742     El.prototype = {
6743         /**
6744          * The element's default display mode  (defaults to "")
6745          * @type String
6746          */
6747         originalDisplay : "",
6748
6749         visibilityMode : 1,
6750         /**
6751          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6752          * @type String
6753          */
6754         defaultUnit : "px",
6755         /**
6756          * Sets the element's visibility mode. When setVisible() is called it
6757          * will use this to determine whether to set the visibility or the display property.
6758          * @param visMode Element.VISIBILITY or Element.DISPLAY
6759          * @return {Roo.Element} this
6760          */
6761         setVisibilityMode : function(visMode){
6762             this.visibilityMode = visMode;
6763             return this;
6764         },
6765         /**
6766          * Convenience method for setVisibilityMode(Element.DISPLAY)
6767          * @param {String} display (optional) What to set display to when visible
6768          * @return {Roo.Element} this
6769          */
6770         enableDisplayMode : function(display){
6771             this.setVisibilityMode(El.DISPLAY);
6772             if(typeof display != "undefined") this.originalDisplay = display;
6773             return this;
6774         },
6775
6776         /**
6777          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6778          * @param {String} selector The simple selector to test
6779          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6780                 search as a number or element (defaults to 10 || document.body)
6781          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6782          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6783          */
6784         findParent : function(simpleSelector, maxDepth, returnEl){
6785             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6786             maxDepth = maxDepth || 50;
6787             if(typeof maxDepth != "number"){
6788                 stopEl = Roo.getDom(maxDepth);
6789                 maxDepth = 10;
6790             }
6791             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6792                 if(dq.is(p, simpleSelector)){
6793                     return returnEl ? Roo.get(p) : p;
6794                 }
6795                 depth++;
6796                 p = p.parentNode;
6797             }
6798             return null;
6799         },
6800
6801
6802         /**
6803          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6804          * @param {String} selector The simple selector to test
6805          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6806                 search as a number or element (defaults to 10 || document.body)
6807          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6808          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6809          */
6810         findParentNode : function(simpleSelector, maxDepth, returnEl){
6811             var p = Roo.fly(this.dom.parentNode, '_internal');
6812             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6813         },
6814
6815         /**
6816          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6817          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6818          * @param {String} selector The simple selector to test
6819          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6820                 search as a number or element (defaults to 10 || document.body)
6821          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6822          */
6823         up : function(simpleSelector, maxDepth){
6824             return this.findParentNode(simpleSelector, maxDepth, true);
6825         },
6826
6827
6828
6829         /**
6830          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6831          * @param {String} selector The simple selector to test
6832          * @return {Boolean} True if this element matches the selector, else false
6833          */
6834         is : function(simpleSelector){
6835             return Roo.DomQuery.is(this.dom, simpleSelector);
6836         },
6837
6838         /**
6839          * Perform animation on this element.
6840          * @param {Object} args The YUI animation control args
6841          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6842          * @param {Function} onComplete (optional) Function to call when animation completes
6843          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6844          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6845          * @return {Roo.Element} this
6846          */
6847         animate : function(args, duration, onComplete, easing, animType){
6848             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6849             return this;
6850         },
6851
6852         /*
6853          * @private Internal animation call
6854          */
6855         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6856             animType = animType || 'run';
6857             opt = opt || {};
6858             var anim = Roo.lib.Anim[animType](
6859                 this.dom, args,
6860                 (opt.duration || defaultDur) || .35,
6861                 (opt.easing || defaultEase) || 'easeOut',
6862                 function(){
6863                     Roo.callback(cb, this);
6864                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6865                 },
6866                 this
6867             );
6868             opt.anim = anim;
6869             return anim;
6870         },
6871
6872         // private legacy anim prep
6873         preanim : function(a, i){
6874             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6875         },
6876
6877         /**
6878          * Removes worthless text nodes
6879          * @param {Boolean} forceReclean (optional) By default the element
6880          * keeps track if it has been cleaned already so
6881          * you can call this over and over. However, if you update the element and
6882          * need to force a reclean, you can pass true.
6883          */
6884         clean : function(forceReclean){
6885             if(this.isCleaned && forceReclean !== true){
6886                 return this;
6887             }
6888             var ns = /\S/;
6889             var d = this.dom, n = d.firstChild, ni = -1;
6890             while(n){
6891                 var nx = n.nextSibling;
6892                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6893                     d.removeChild(n);
6894                 }else{
6895                     n.nodeIndex = ++ni;
6896                 }
6897                 n = nx;
6898             }
6899             this.isCleaned = true;
6900             return this;
6901         },
6902
6903         // private
6904         calcOffsetsTo : function(el){
6905             el = Roo.get(el);
6906             var d = el.dom;
6907             var restorePos = false;
6908             if(el.getStyle('position') == 'static'){
6909                 el.position('relative');
6910                 restorePos = true;
6911             }
6912             var x = 0, y =0;
6913             var op = this.dom;
6914             while(op && op != d && op.tagName != 'HTML'){
6915                 x+= op.offsetLeft;
6916                 y+= op.offsetTop;
6917                 op = op.offsetParent;
6918             }
6919             if(restorePos){
6920                 el.position('static');
6921             }
6922             return [x, y];
6923         },
6924
6925         /**
6926          * Scrolls this element into view within the passed container.
6927          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6928          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6929          * @return {Roo.Element} this
6930          */
6931         scrollIntoView : function(container, hscroll){
6932             var c = Roo.getDom(container) || document.body;
6933             var el = this.dom;
6934
6935             var o = this.calcOffsetsTo(c),
6936                 l = o[0],
6937                 t = o[1],
6938                 b = t+el.offsetHeight,
6939                 r = l+el.offsetWidth;
6940
6941             var ch = c.clientHeight;
6942             var ct = parseInt(c.scrollTop, 10);
6943             var cl = parseInt(c.scrollLeft, 10);
6944             var cb = ct + ch;
6945             var cr = cl + c.clientWidth;
6946
6947             if(t < ct){
6948                 c.scrollTop = t;
6949             }else if(b > cb){
6950                 c.scrollTop = b-ch;
6951             }
6952
6953             if(hscroll !== false){
6954                 if(l < cl){
6955                     c.scrollLeft = l;
6956                 }else if(r > cr){
6957                     c.scrollLeft = r-c.clientWidth;
6958                 }
6959             }
6960             return this;
6961         },
6962
6963         // private
6964         scrollChildIntoView : function(child, hscroll){
6965             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6966         },
6967
6968         /**
6969          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6970          * the new height may not be available immediately.
6971          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6972          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6973          * @param {Function} onComplete (optional) Function to call when animation completes
6974          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6975          * @return {Roo.Element} this
6976          */
6977         autoHeight : function(animate, duration, onComplete, easing){
6978             var oldHeight = this.getHeight();
6979             this.clip();
6980             this.setHeight(1); // force clipping
6981             setTimeout(function(){
6982                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6983                 if(!animate){
6984                     this.setHeight(height);
6985                     this.unclip();
6986                     if(typeof onComplete == "function"){
6987                         onComplete();
6988                     }
6989                 }else{
6990                     this.setHeight(oldHeight); // restore original height
6991                     this.setHeight(height, animate, duration, function(){
6992                         this.unclip();
6993                         if(typeof onComplete == "function") onComplete();
6994                     }.createDelegate(this), easing);
6995                 }
6996             }.createDelegate(this), 0);
6997             return this;
6998         },
6999
7000         /**
7001          * Returns true if this element is an ancestor of the passed element
7002          * @param {HTMLElement/String} el The element to check
7003          * @return {Boolean} True if this element is an ancestor of el, else false
7004          */
7005         contains : function(el){
7006             if(!el){return false;}
7007             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7008         },
7009
7010         /**
7011          * Checks whether the element is currently visible using both visibility and display properties.
7012          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7013          * @return {Boolean} True if the element is currently visible, else false
7014          */
7015         isVisible : function(deep) {
7016             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7017             if(deep !== true || !vis){
7018                 return vis;
7019             }
7020             var p = this.dom.parentNode;
7021             while(p && p.tagName.toLowerCase() != "body"){
7022                 if(!Roo.fly(p, '_isVisible').isVisible()){
7023                     return false;
7024                 }
7025                 p = p.parentNode;
7026             }
7027             return true;
7028         },
7029
7030         /**
7031          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7032          * @param {String} selector The CSS selector
7033          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7034          * @return {CompositeElement/CompositeElementLite} The composite element
7035          */
7036         select : function(selector, unique){
7037             return El.select(selector, unique, this.dom);
7038         },
7039
7040         /**
7041          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7042          * @param {String} selector The CSS selector
7043          * @return {Array} An array of the matched nodes
7044          */
7045         query : function(selector, unique){
7046             return Roo.DomQuery.select(selector, this.dom);
7047         },
7048
7049         /**
7050          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7051          * @param {String} selector The CSS selector
7052          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7053          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7054          */
7055         child : function(selector, returnDom){
7056             var n = Roo.DomQuery.selectNode(selector, this.dom);
7057             return returnDom ? n : Roo.get(n);
7058         },
7059
7060         /**
7061          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7062          * @param {String} selector The CSS selector
7063          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7064          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7065          */
7066         down : function(selector, returnDom){
7067             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7068             return returnDom ? n : Roo.get(n);
7069         },
7070
7071         /**
7072          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7073          * @param {String} group The group the DD object is member of
7074          * @param {Object} config The DD config object
7075          * @param {Object} overrides An object containing methods to override/implement on the DD object
7076          * @return {Roo.dd.DD} The DD object
7077          */
7078         initDD : function(group, config, overrides){
7079             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7080             return Roo.apply(dd, overrides);
7081         },
7082
7083         /**
7084          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7085          * @param {String} group The group the DDProxy object is member of
7086          * @param {Object} config The DDProxy config object
7087          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7088          * @return {Roo.dd.DDProxy} The DDProxy object
7089          */
7090         initDDProxy : function(group, config, overrides){
7091             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7092             return Roo.apply(dd, overrides);
7093         },
7094
7095         /**
7096          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7097          * @param {String} group The group the DDTarget object is member of
7098          * @param {Object} config The DDTarget config object
7099          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7100          * @return {Roo.dd.DDTarget} The DDTarget object
7101          */
7102         initDDTarget : function(group, config, overrides){
7103             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7104             return Roo.apply(dd, overrides);
7105         },
7106
7107         /**
7108          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7109          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7110          * @param {Boolean} visible Whether the element is visible
7111          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7112          * @return {Roo.Element} this
7113          */
7114          setVisible : function(visible, animate){
7115             if(!animate || !A){
7116                 if(this.visibilityMode == El.DISPLAY){
7117                     this.setDisplayed(visible);
7118                 }else{
7119                     this.fixDisplay();
7120                     this.dom.style.visibility = visible ? "visible" : "hidden";
7121                 }
7122             }else{
7123                 // closure for composites
7124                 var dom = this.dom;
7125                 var visMode = this.visibilityMode;
7126                 if(visible){
7127                     this.setOpacity(.01);
7128                     this.setVisible(true);
7129                 }
7130                 this.anim({opacity: { to: (visible?1:0) }},
7131                       this.preanim(arguments, 1),
7132                       null, .35, 'easeIn', function(){
7133                          if(!visible){
7134                              if(visMode == El.DISPLAY){
7135                                  dom.style.display = "none";
7136                              }else{
7137                                  dom.style.visibility = "hidden";
7138                              }
7139                              Roo.get(dom).setOpacity(1);
7140                          }
7141                      });
7142             }
7143             return this;
7144         },
7145
7146         /**
7147          * Returns true if display is not "none"
7148          * @return {Boolean}
7149          */
7150         isDisplayed : function() {
7151             return this.getStyle("display") != "none";
7152         },
7153
7154         /**
7155          * Toggles the element's visibility or display, depending on visibility mode.
7156          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7157          * @return {Roo.Element} this
7158          */
7159         toggle : function(animate){
7160             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7161             return this;
7162         },
7163
7164         /**
7165          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7166          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7167          * @return {Roo.Element} this
7168          */
7169         setDisplayed : function(value) {
7170             if(typeof value == "boolean"){
7171                value = value ? this.originalDisplay : "none";
7172             }
7173             this.setStyle("display", value);
7174             return this;
7175         },
7176
7177         /**
7178          * Tries to focus the element. Any exceptions are caught and ignored.
7179          * @return {Roo.Element} this
7180          */
7181         focus : function() {
7182             try{
7183                 this.dom.focus();
7184             }catch(e){}
7185             return this;
7186         },
7187
7188         /**
7189          * Tries to blur the element. Any exceptions are caught and ignored.
7190          * @return {Roo.Element} this
7191          */
7192         blur : function() {
7193             try{
7194                 this.dom.blur();
7195             }catch(e){}
7196             return this;
7197         },
7198
7199         /**
7200          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7201          * @param {String/Array} className The CSS class to add, or an array of classes
7202          * @return {Roo.Element} this
7203          */
7204         addClass : function(className){
7205             if(className instanceof Array){
7206                 for(var i = 0, len = className.length; i < len; i++) {
7207                     this.addClass(className[i]);
7208                 }
7209             }else{
7210                 if(className && !this.hasClass(className)){
7211                     this.dom.className = this.dom.className + " " + className;
7212                 }
7213             }
7214             return this;
7215         },
7216
7217         /**
7218          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7219          * @param {String/Array} className The CSS class to add, or an array of classes
7220          * @return {Roo.Element} this
7221          */
7222         radioClass : function(className){
7223             var siblings = this.dom.parentNode.childNodes;
7224             for(var i = 0; i < siblings.length; i++) {
7225                 var s = siblings[i];
7226                 if(s.nodeType == 1){
7227                     Roo.get(s).removeClass(className);
7228                 }
7229             }
7230             this.addClass(className);
7231             return this;
7232         },
7233
7234         /**
7235          * Removes one or more CSS classes from the element.
7236          * @param {String/Array} className The CSS class to remove, or an array of classes
7237          * @return {Roo.Element} this
7238          */
7239         removeClass : function(className){
7240             if(!className || !this.dom.className){
7241                 return this;
7242             }
7243             if(className instanceof Array){
7244                 for(var i = 0, len = className.length; i < len; i++) {
7245                     this.removeClass(className[i]);
7246                 }
7247             }else{
7248                 if(this.hasClass(className)){
7249                     var re = this.classReCache[className];
7250                     if (!re) {
7251                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7252                        this.classReCache[className] = re;
7253                     }
7254                     this.dom.className =
7255                         this.dom.className.replace(re, " ");
7256                 }
7257             }
7258             return this;
7259         },
7260
7261         // private
7262         classReCache: {},
7263
7264         /**
7265          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7266          * @param {String} className The CSS class to toggle
7267          * @return {Roo.Element} this
7268          */
7269         toggleClass : function(className){
7270             if(this.hasClass(className)){
7271                 this.removeClass(className);
7272             }else{
7273                 this.addClass(className);
7274             }
7275             return this;
7276         },
7277
7278         /**
7279          * Checks if the specified CSS class exists on this element's DOM node.
7280          * @param {String} className The CSS class to check for
7281          * @return {Boolean} True if the class exists, else false
7282          */
7283         hasClass : function(className){
7284             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7285         },
7286
7287         /**
7288          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7289          * @param {String} oldClassName The CSS class to replace
7290          * @param {String} newClassName The replacement CSS class
7291          * @return {Roo.Element} this
7292          */
7293         replaceClass : function(oldClassName, newClassName){
7294             this.removeClass(oldClassName);
7295             this.addClass(newClassName);
7296             return this;
7297         },
7298
7299         /**
7300          * Returns an object with properties matching the styles requested.
7301          * For example, el.getStyles('color', 'font-size', 'width') might return
7302          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7303          * @param {String} style1 A style name
7304          * @param {String} style2 A style name
7305          * @param {String} etc.
7306          * @return {Object} The style object
7307          */
7308         getStyles : function(){
7309             var a = arguments, len = a.length, r = {};
7310             for(var i = 0; i < len; i++){
7311                 r[a[i]] = this.getStyle(a[i]);
7312             }
7313             return r;
7314         },
7315
7316         /**
7317          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7318          * @param {String} property The style property whose value is returned.
7319          * @return {String} The current value of the style property for this element.
7320          */
7321         getStyle : function(){
7322             return view && view.getComputedStyle ?
7323                 function(prop){
7324                     var el = this.dom, v, cs, camel;
7325                     if(prop == 'float'){
7326                         prop = "cssFloat";
7327                     }
7328                     if(el.style && (v = el.style[prop])){
7329                         return v;
7330                     }
7331                     if(cs = view.getComputedStyle(el, "")){
7332                         if(!(camel = propCache[prop])){
7333                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7334                         }
7335                         return cs[camel];
7336                     }
7337                     return null;
7338                 } :
7339                 function(prop){
7340                     var el = this.dom, v, cs, camel;
7341                     if(prop == 'opacity'){
7342                         if(typeof el.style.filter == 'string'){
7343                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7344                             if(m){
7345                                 var fv = parseFloat(m[1]);
7346                                 if(!isNaN(fv)){
7347                                     return fv ? fv / 100 : 0;
7348                                 }
7349                             }
7350                         }
7351                         return 1;
7352                     }else if(prop == 'float'){
7353                         prop = "styleFloat";
7354                     }
7355                     if(!(camel = propCache[prop])){
7356                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7357                     }
7358                     if(v = el.style[camel]){
7359                         return v;
7360                     }
7361                     if(cs = el.currentStyle){
7362                         return cs[camel];
7363                     }
7364                     return null;
7365                 };
7366         }(),
7367
7368         /**
7369          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7370          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7371          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7372          * @return {Roo.Element} this
7373          */
7374         setStyle : function(prop, value){
7375             if(typeof prop == "string"){
7376                 var camel;
7377                 if(!(camel = propCache[prop])){
7378                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7379                 }
7380                 if(camel == 'opacity') {
7381                     this.setOpacity(value);
7382                 }else{
7383                     this.dom.style[camel] = value;
7384                 }
7385             }else{
7386                 for(var style in prop){
7387                     if(typeof prop[style] != "function"){
7388                        this.setStyle(style, prop[style]);
7389                     }
7390                 }
7391             }
7392             return this;
7393         },
7394
7395         /**
7396          * More flexible version of {@link #setStyle} for setting style properties.
7397          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7398          * a function which returns such a specification.
7399          * @return {Roo.Element} this
7400          */
7401         applyStyles : function(style){
7402             Roo.DomHelper.applyStyles(this.dom, style);
7403             return this;
7404         },
7405
7406         /**
7407           * 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).
7408           * @return {Number} The X position of the element
7409           */
7410         getX : function(){
7411             return D.getX(this.dom);
7412         },
7413
7414         /**
7415           * 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).
7416           * @return {Number} The Y position of the element
7417           */
7418         getY : function(){
7419             return D.getY(this.dom);
7420         },
7421
7422         /**
7423           * 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).
7424           * @return {Array} The XY position of the element
7425           */
7426         getXY : function(){
7427             return D.getXY(this.dom);
7428         },
7429
7430         /**
7431          * 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).
7432          * @param {Number} The X position of the element
7433          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7434          * @return {Roo.Element} this
7435          */
7436         setX : function(x, animate){
7437             if(!animate || !A){
7438                 D.setX(this.dom, x);
7439             }else{
7440                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7441             }
7442             return this;
7443         },
7444
7445         /**
7446          * 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).
7447          * @param {Number} The Y position of the element
7448          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7449          * @return {Roo.Element} this
7450          */
7451         setY : function(y, animate){
7452             if(!animate || !A){
7453                 D.setY(this.dom, y);
7454             }else{
7455                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7456             }
7457             return this;
7458         },
7459
7460         /**
7461          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7462          * @param {String} left The left CSS property value
7463          * @return {Roo.Element} this
7464          */
7465         setLeft : function(left){
7466             this.setStyle("left", this.addUnits(left));
7467             return this;
7468         },
7469
7470         /**
7471          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7472          * @param {String} top The top CSS property value
7473          * @return {Roo.Element} this
7474          */
7475         setTop : function(top){
7476             this.setStyle("top", this.addUnits(top));
7477             return this;
7478         },
7479
7480         /**
7481          * Sets the element's CSS right style.
7482          * @param {String} right The right CSS property value
7483          * @return {Roo.Element} this
7484          */
7485         setRight : function(right){
7486             this.setStyle("right", this.addUnits(right));
7487             return this;
7488         },
7489
7490         /**
7491          * Sets the element's CSS bottom style.
7492          * @param {String} bottom The bottom CSS property value
7493          * @return {Roo.Element} this
7494          */
7495         setBottom : function(bottom){
7496             this.setStyle("bottom", this.addUnits(bottom));
7497             return this;
7498         },
7499
7500         /**
7501          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7502          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7503          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7504          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7505          * @return {Roo.Element} this
7506          */
7507         setXY : function(pos, animate){
7508             if(!animate || !A){
7509                 D.setXY(this.dom, pos);
7510             }else{
7511                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7512             }
7513             return this;
7514         },
7515
7516         /**
7517          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7518          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7519          * @param {Number} x X value for new position (coordinates are page-based)
7520          * @param {Number} y Y value for new position (coordinates are page-based)
7521          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7522          * @return {Roo.Element} this
7523          */
7524         setLocation : function(x, y, animate){
7525             this.setXY([x, y], this.preanim(arguments, 2));
7526             return this;
7527         },
7528
7529         /**
7530          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7531          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7532          * @param {Number} x X value for new position (coordinates are page-based)
7533          * @param {Number} y Y value for new position (coordinates are page-based)
7534          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7535          * @return {Roo.Element} this
7536          */
7537         moveTo : function(x, y, animate){
7538             this.setXY([x, y], this.preanim(arguments, 2));
7539             return this;
7540         },
7541
7542         /**
7543          * Returns the region of the given element.
7544          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7545          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7546          */
7547         getRegion : function(){
7548             return D.getRegion(this.dom);
7549         },
7550
7551         /**
7552          * Returns the offset height of the element
7553          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7554          * @return {Number} The element's height
7555          */
7556         getHeight : function(contentHeight){
7557             var h = this.dom.offsetHeight || 0;
7558             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7559         },
7560
7561         /**
7562          * Returns the offset width of the element
7563          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7564          * @return {Number} The element's width
7565          */
7566         getWidth : function(contentWidth){
7567             var w = this.dom.offsetWidth || 0;
7568             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7569         },
7570
7571         /**
7572          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7573          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7574          * if a height has not been set using CSS.
7575          * @return {Number}
7576          */
7577         getComputedHeight : function(){
7578             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7579             if(!h){
7580                 h = parseInt(this.getStyle('height'), 10) || 0;
7581                 if(!this.isBorderBox()){
7582                     h += this.getFrameWidth('tb');
7583                 }
7584             }
7585             return h;
7586         },
7587
7588         /**
7589          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7590          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7591          * if a width has not been set using CSS.
7592          * @return {Number}
7593          */
7594         getComputedWidth : function(){
7595             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7596             if(!w){
7597                 w = parseInt(this.getStyle('width'), 10) || 0;
7598                 if(!this.isBorderBox()){
7599                     w += this.getFrameWidth('lr');
7600                 }
7601             }
7602             return w;
7603         },
7604
7605         /**
7606          * Returns the size of the element.
7607          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7608          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7609          */
7610         getSize : function(contentSize){
7611             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7612         },
7613
7614         /**
7615          * Returns the width and height of the viewport.
7616          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7617          */
7618         getViewSize : function(){
7619             var d = this.dom, doc = document, aw = 0, ah = 0;
7620             if(d == doc || d == doc.body){
7621                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7622             }else{
7623                 return {
7624                     width : d.clientWidth,
7625                     height: d.clientHeight
7626                 };
7627             }
7628         },
7629
7630         /**
7631          * Returns the value of the "value" attribute
7632          * @param {Boolean} asNumber true to parse the value as a number
7633          * @return {String/Number}
7634          */
7635         getValue : function(asNumber){
7636             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7637         },
7638
7639         // private
7640         adjustWidth : function(width){
7641             if(typeof width == "number"){
7642                 if(this.autoBoxAdjust && !this.isBorderBox()){
7643                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7644                 }
7645                 if(width < 0){
7646                     width = 0;
7647                 }
7648             }
7649             return width;
7650         },
7651
7652         // private
7653         adjustHeight : function(height){
7654             if(typeof height == "number"){
7655                if(this.autoBoxAdjust && !this.isBorderBox()){
7656                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7657                }
7658                if(height < 0){
7659                    height = 0;
7660                }
7661             }
7662             return height;
7663         },
7664
7665         /**
7666          * Set the width of the element
7667          * @param {Number} width The new width
7668          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7669          * @return {Roo.Element} this
7670          */
7671         setWidth : function(width, animate){
7672             width = this.adjustWidth(width);
7673             if(!animate || !A){
7674                 this.dom.style.width = this.addUnits(width);
7675             }else{
7676                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Set the height of the element
7683          * @param {Number} height The new height
7684          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687          setHeight : function(height, animate){
7688             height = this.adjustHeight(height);
7689             if(!animate || !A){
7690                 this.dom.style.height = this.addUnits(height);
7691             }else{
7692                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7693             }
7694             return this;
7695         },
7696
7697         /**
7698          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7699          * @param {Number} width The new width
7700          * @param {Number} height The new height
7701          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704          setSize : function(width, height, animate){
7705             if(typeof width == "object"){ // in case of object from getSize()
7706                 height = width.height; width = width.width;
7707             }
7708             width = this.adjustWidth(width); height = this.adjustHeight(height);
7709             if(!animate || !A){
7710                 this.dom.style.width = this.addUnits(width);
7711                 this.dom.style.height = this.addUnits(height);
7712             }else{
7713                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7714             }
7715             return this;
7716         },
7717
7718         /**
7719          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7720          * @param {Number} x X value for new position (coordinates are page-based)
7721          * @param {Number} y Y value for new position (coordinates are page-based)
7722          * @param {Number} width The new width
7723          * @param {Number} height The new height
7724          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7725          * @return {Roo.Element} this
7726          */
7727         setBounds : function(x, y, width, height, animate){
7728             if(!animate || !A){
7729                 this.setSize(width, height);
7730                 this.setLocation(x, y);
7731             }else{
7732                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7733                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7734                               this.preanim(arguments, 4), 'motion');
7735             }
7736             return this;
7737         },
7738
7739         /**
7740          * 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.
7741          * @param {Roo.lib.Region} region The region to fill
7742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7743          * @return {Roo.Element} this
7744          */
7745         setRegion : function(region, animate){
7746             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7747             return this;
7748         },
7749
7750         /**
7751          * Appends an event handler
7752          *
7753          * @param {String}   eventName     The type of event to append
7754          * @param {Function} fn        The method the event invokes
7755          * @param {Object} scope       (optional) The scope (this object) of the fn
7756          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7757          */
7758         addListener : function(eventName, fn, scope, options){
7759             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7760         },
7761
7762         /**
7763          * Removes an event handler from this element
7764          * @param {String} eventName the type of event to remove
7765          * @param {Function} fn the method the event invokes
7766          * @return {Roo.Element} this
7767          */
7768         removeListener : function(eventName, fn){
7769             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7770             return this;
7771         },
7772
7773         /**
7774          * Removes all previous added listeners from this element
7775          * @return {Roo.Element} this
7776          */
7777         removeAllListeners : function(){
7778             E.purgeElement(this.dom);
7779             return this;
7780         },
7781
7782         relayEvent : function(eventName, observable){
7783             this.on(eventName, function(e){
7784                 observable.fireEvent(eventName, e);
7785             });
7786         },
7787
7788         /**
7789          * Set the opacity of the element
7790          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7791          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794          setOpacity : function(opacity, animate){
7795             if(!animate || !A){
7796                 var s = this.dom.style;
7797                 if(Roo.isIE){
7798                     s.zoom = 1;
7799                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7800                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7801                 }else{
7802                     s.opacity = opacity;
7803                 }
7804             }else{
7805                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7806             }
7807             return this;
7808         },
7809
7810         /**
7811          * Gets the left X coordinate
7812          * @param {Boolean} local True to get the local css position instead of page coordinate
7813          * @return {Number}
7814          */
7815         getLeft : function(local){
7816             if(!local){
7817                 return this.getX();
7818             }else{
7819                 return parseInt(this.getStyle("left"), 10) || 0;
7820             }
7821         },
7822
7823         /**
7824          * Gets the right X coordinate of the element (element X position + element width)
7825          * @param {Boolean} local True to get the local css position instead of page coordinate
7826          * @return {Number}
7827          */
7828         getRight : function(local){
7829             if(!local){
7830                 return this.getX() + this.getWidth();
7831             }else{
7832                 return (this.getLeft(true) + this.getWidth()) || 0;
7833             }
7834         },
7835
7836         /**
7837          * Gets the top Y coordinate
7838          * @param {Boolean} local True to get the local css position instead of page coordinate
7839          * @return {Number}
7840          */
7841         getTop : function(local) {
7842             if(!local){
7843                 return this.getY();
7844             }else{
7845                 return parseInt(this.getStyle("top"), 10) || 0;
7846             }
7847         },
7848
7849         /**
7850          * Gets the bottom Y coordinate of the element (element Y position + element height)
7851          * @param {Boolean} local True to get the local css position instead of page coordinate
7852          * @return {Number}
7853          */
7854         getBottom : function(local){
7855             if(!local){
7856                 return this.getY() + this.getHeight();
7857             }else{
7858                 return (this.getTop(true) + this.getHeight()) || 0;
7859             }
7860         },
7861
7862         /**
7863         * Initializes positioning on this element. If a desired position is not passed, it will make the
7864         * the element positioned relative IF it is not already positioned.
7865         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7866         * @param {Number} zIndex (optional) The zIndex to apply
7867         * @param {Number} x (optional) Set the page X position
7868         * @param {Number} y (optional) Set the page Y position
7869         */
7870         position : function(pos, zIndex, x, y){
7871             if(!pos){
7872                if(this.getStyle('position') == 'static'){
7873                    this.setStyle('position', 'relative');
7874                }
7875             }else{
7876                 this.setStyle("position", pos);
7877             }
7878             if(zIndex){
7879                 this.setStyle("z-index", zIndex);
7880             }
7881             if(x !== undefined && y !== undefined){
7882                 this.setXY([x, y]);
7883             }else if(x !== undefined){
7884                 this.setX(x);
7885             }else if(y !== undefined){
7886                 this.setY(y);
7887             }
7888         },
7889
7890         /**
7891         * Clear positioning back to the default when the document was loaded
7892         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7893         * @return {Roo.Element} this
7894          */
7895         clearPositioning : function(value){
7896             value = value ||'';
7897             this.setStyle({
7898                 "left": value,
7899                 "right": value,
7900                 "top": value,
7901                 "bottom": value,
7902                 "z-index": "",
7903                 "position" : "static"
7904             });
7905             return this;
7906         },
7907
7908         /**
7909         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7910         * snapshot before performing an update and then restoring the element.
7911         * @return {Object}
7912         */
7913         getPositioning : function(){
7914             var l = this.getStyle("left");
7915             var t = this.getStyle("top");
7916             return {
7917                 "position" : this.getStyle("position"),
7918                 "left" : l,
7919                 "right" : l ? "" : this.getStyle("right"),
7920                 "top" : t,
7921                 "bottom" : t ? "" : this.getStyle("bottom"),
7922                 "z-index" : this.getStyle("z-index")
7923             };
7924         },
7925
7926         /**
7927          * Gets the width of the border(s) for the specified side(s)
7928          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7929          * passing lr would get the border (l)eft width + the border (r)ight width.
7930          * @return {Number} The width of the sides passed added together
7931          */
7932         getBorderWidth : function(side){
7933             return this.addStyles(side, El.borders);
7934         },
7935
7936         /**
7937          * Gets the width of the padding(s) for the specified side(s)
7938          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7939          * passing lr would get the padding (l)eft + the padding (r)ight.
7940          * @return {Number} The padding of the sides passed added together
7941          */
7942         getPadding : function(side){
7943             return this.addStyles(side, El.paddings);
7944         },
7945
7946         /**
7947         * Set positioning with an object returned by getPositioning().
7948         * @param {Object} posCfg
7949         * @return {Roo.Element} this
7950          */
7951         setPositioning : function(pc){
7952             this.applyStyles(pc);
7953             if(pc.right == "auto"){
7954                 this.dom.style.right = "";
7955             }
7956             if(pc.bottom == "auto"){
7957                 this.dom.style.bottom = "";
7958             }
7959             return this;
7960         },
7961
7962         // private
7963         fixDisplay : function(){
7964             if(this.getStyle("display") == "none"){
7965                 this.setStyle("visibility", "hidden");
7966                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7967                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7968                     this.setStyle("display", "block");
7969                 }
7970             }
7971         },
7972
7973         /**
7974          * Quick set left and top adding default units
7975          * @param {String} left The left CSS property value
7976          * @param {String} top The top CSS property value
7977          * @return {Roo.Element} this
7978          */
7979          setLeftTop : function(left, top){
7980             this.dom.style.left = this.addUnits(left);
7981             this.dom.style.top = this.addUnits(top);
7982             return this;
7983         },
7984
7985         /**
7986          * Move this element relative to its current position.
7987          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7988          * @param {Number} distance How far to move the element in pixels
7989          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7990          * @return {Roo.Element} this
7991          */
7992          move : function(direction, distance, animate){
7993             var xy = this.getXY();
7994             direction = direction.toLowerCase();
7995             switch(direction){
7996                 case "l":
7997                 case "left":
7998                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7999                     break;
8000                case "r":
8001                case "right":
8002                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8003                     break;
8004                case "t":
8005                case "top":
8006                case "up":
8007                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8008                     break;
8009                case "b":
8010                case "bottom":
8011                case "down":
8012                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8013                     break;
8014             }
8015             return this;
8016         },
8017
8018         /**
8019          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8020          * @return {Roo.Element} this
8021          */
8022         clip : function(){
8023             if(!this.isClipped){
8024                this.isClipped = true;
8025                this.originalClip = {
8026                    "o": this.getStyle("overflow"),
8027                    "x": this.getStyle("overflow-x"),
8028                    "y": this.getStyle("overflow-y")
8029                };
8030                this.setStyle("overflow", "hidden");
8031                this.setStyle("overflow-x", "hidden");
8032                this.setStyle("overflow-y", "hidden");
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          *  Return clipping (overflow) to original clipping before clip() was called
8039          * @return {Roo.Element} this
8040          */
8041         unclip : function(){
8042             if(this.isClipped){
8043                 this.isClipped = false;
8044                 var o = this.originalClip;
8045                 if(o.o){this.setStyle("overflow", o.o);}
8046                 if(o.x){this.setStyle("overflow-x", o.x);}
8047                 if(o.y){this.setStyle("overflow-y", o.y);}
8048             }
8049             return this;
8050         },
8051
8052
8053         /**
8054          * Gets the x,y coordinates specified by the anchor position on the element.
8055          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8056          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8057          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8058          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8059          * @return {Array} [x, y] An array containing the element's x and y coordinates
8060          */
8061         getAnchorXY : function(anchor, local, s){
8062             //Passing a different size is useful for pre-calculating anchors,
8063             //especially for anchored animations that change the el size.
8064
8065             var w, h, vp = false;
8066             if(!s){
8067                 var d = this.dom;
8068                 if(d == document.body || d == document){
8069                     vp = true;
8070                     w = D.getViewWidth(); h = D.getViewHeight();
8071                 }else{
8072                     w = this.getWidth(); h = this.getHeight();
8073                 }
8074             }else{
8075                 w = s.width;  h = s.height;
8076             }
8077             var x = 0, y = 0, r = Math.round;
8078             switch((anchor || "tl").toLowerCase()){
8079                 case "c":
8080                     x = r(w*.5);
8081                     y = r(h*.5);
8082                 break;
8083                 case "t":
8084                     x = r(w*.5);
8085                     y = 0;
8086                 break;
8087                 case "l":
8088                     x = 0;
8089                     y = r(h*.5);
8090                 break;
8091                 case "r":
8092                     x = w;
8093                     y = r(h*.5);
8094                 break;
8095                 case "b":
8096                     x = r(w*.5);
8097                     y = h;
8098                 break;
8099                 case "tl":
8100                     x = 0;
8101                     y = 0;
8102                 break;
8103                 case "bl":
8104                     x = 0;
8105                     y = h;
8106                 break;
8107                 case "br":
8108                     x = w;
8109                     y = h;
8110                 break;
8111                 case "tr":
8112                     x = w;
8113                     y = 0;
8114                 break;
8115             }
8116             if(local === true){
8117                 return [x, y];
8118             }
8119             if(vp){
8120                 var sc = this.getScroll();
8121                 return [x + sc.left, y + sc.top];
8122             }
8123             //Add the element's offset xy
8124             var o = this.getXY();
8125             return [x+o[0], y+o[1]];
8126         },
8127
8128         /**
8129          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8130          * supported position values.
8131          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8132          * @param {String} position The position to align to.
8133          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8134          * @return {Array} [x, y]
8135          */
8136         getAlignToXY : function(el, p, o){
8137             el = Roo.get(el);
8138             var d = this.dom;
8139             if(!el.dom){
8140                 throw "Element.alignTo with an element that doesn't exist";
8141             }
8142             var c = false; //constrain to viewport
8143             var p1 = "", p2 = "";
8144             o = o || [0,0];
8145
8146             if(!p){
8147                 p = "tl-bl";
8148             }else if(p == "?"){
8149                 p = "tl-bl?";
8150             }else if(p.indexOf("-") == -1){
8151                 p = "tl-" + p;
8152             }
8153             p = p.toLowerCase();
8154             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8155             if(!m){
8156                throw "Element.alignTo with an invalid alignment " + p;
8157             }
8158             p1 = m[1]; p2 = m[2]; c = !!m[3];
8159
8160             //Subtract the aligned el's internal xy from the target's offset xy
8161             //plus custom offset to get the aligned el's new offset xy
8162             var a1 = this.getAnchorXY(p1, true);
8163             var a2 = el.getAnchorXY(p2, false);
8164             var x = a2[0] - a1[0] + o[0];
8165             var y = a2[1] - a1[1] + o[1];
8166             if(c){
8167                 //constrain the aligned el to viewport if necessary
8168                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8169                 // 5px of margin for ie
8170                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8171
8172                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8173                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8174                 //otherwise swap the aligned el to the opposite border of the target.
8175                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8176                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8177                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8178                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8179
8180                var doc = document;
8181                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8182                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8183
8184                if((x+w) > dw + scrollX){
8185                     x = swapX ? r.left-w : dw+scrollX-w;
8186                 }
8187                if(x < scrollX){
8188                    x = swapX ? r.right : scrollX;
8189                }
8190                if((y+h) > dh + scrollY){
8191                     y = swapY ? r.top-h : dh+scrollY-h;
8192                 }
8193                if (y < scrollY){
8194                    y = swapY ? r.bottom : scrollY;
8195                }
8196             }
8197             return [x,y];
8198         },
8199
8200         // private
8201         getConstrainToXY : function(){
8202             var os = {top:0, left:0, bottom:0, right: 0};
8203
8204             return function(el, local, offsets, proposedXY){
8205                 el = Roo.get(el);
8206                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8207
8208                 var vw, vh, vx = 0, vy = 0;
8209                 if(el.dom == document.body || el.dom == document){
8210                     vw = Roo.lib.Dom.getViewWidth();
8211                     vh = Roo.lib.Dom.getViewHeight();
8212                 }else{
8213                     vw = el.dom.clientWidth;
8214                     vh = el.dom.clientHeight;
8215                     if(!local){
8216                         var vxy = el.getXY();
8217                         vx = vxy[0];
8218                         vy = vxy[1];
8219                     }
8220                 }
8221
8222                 var s = el.getScroll();
8223
8224                 vx += offsets.left + s.left;
8225                 vy += offsets.top + s.top;
8226
8227                 vw -= offsets.right;
8228                 vh -= offsets.bottom;
8229
8230                 var vr = vx+vw;
8231                 var vb = vy+vh;
8232
8233                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8234                 var x = xy[0], y = xy[1];
8235                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8236
8237                 // only move it if it needs it
8238                 var moved = false;
8239
8240                 // first validate right/bottom
8241                 if((x + w) > vr){
8242                     x = vr - w;
8243                     moved = true;
8244                 }
8245                 if((y + h) > vb){
8246                     y = vb - h;
8247                     moved = true;
8248                 }
8249                 // then make sure top/left isn't negative
8250                 if(x < vx){
8251                     x = vx;
8252                     moved = true;
8253                 }
8254                 if(y < vy){
8255                     y = vy;
8256                     moved = true;
8257                 }
8258                 return moved ? [x, y] : false;
8259             };
8260         }(),
8261
8262         // private
8263         adjustForConstraints : function(xy, parent, offsets){
8264             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8265         },
8266
8267         /**
8268          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8269          * document it aligns it to the viewport.
8270          * The position parameter is optional, and can be specified in any one of the following formats:
8271          * <ul>
8272          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8273          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8274          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8275          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8276          *   <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
8277          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8278          * </ul>
8279          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8280          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8281          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8282          * that specified in order to enforce the viewport constraints.
8283          * Following are all of the supported anchor positions:
8284     <pre>
8285     Value  Description
8286     -----  -----------------------------
8287     tl     The top left corner (default)
8288     t      The center of the top edge
8289     tr     The top right corner
8290     l      The center of the left edge
8291     c      In the center of the element
8292     r      The center of the right edge
8293     bl     The bottom left corner
8294     b      The center of the bottom edge
8295     br     The bottom right corner
8296     </pre>
8297     Example Usage:
8298     <pre><code>
8299     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8300     el.alignTo("other-el");
8301
8302     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8303     el.alignTo("other-el", "tr?");
8304
8305     // align the bottom right corner of el with the center left edge of other-el
8306     el.alignTo("other-el", "br-l?");
8307
8308     // align the center of el with the bottom left corner of other-el and
8309     // adjust the x position by -6 pixels (and the y position by 0)
8310     el.alignTo("other-el", "c-bl", [-6, 0]);
8311     </code></pre>
8312          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8313          * @param {String} position The position to align to.
8314          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8315          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8316          * @return {Roo.Element} this
8317          */
8318         alignTo : function(element, position, offsets, animate){
8319             var xy = this.getAlignToXY(element, position, offsets);
8320             this.setXY(xy, this.preanim(arguments, 3));
8321             return this;
8322         },
8323
8324         /**
8325          * Anchors an element to another element and realigns it when the window is resized.
8326          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8327          * @param {String} position The position to align to.
8328          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8329          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8330          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8331          * is a number, it is used as the buffer delay (defaults to 50ms).
8332          * @param {Function} callback The function to call after the animation finishes
8333          * @return {Roo.Element} this
8334          */
8335         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8336             var action = function(){
8337                 this.alignTo(el, alignment, offsets, animate);
8338                 Roo.callback(callback, this);
8339             };
8340             Roo.EventManager.onWindowResize(action, this);
8341             var tm = typeof monitorScroll;
8342             if(tm != 'undefined'){
8343                 Roo.EventManager.on(window, 'scroll', action, this,
8344                     {buffer: tm == 'number' ? monitorScroll : 50});
8345             }
8346             action.call(this); // align immediately
8347             return this;
8348         },
8349         /**
8350          * Clears any opacity settings from this element. Required in some cases for IE.
8351          * @return {Roo.Element} this
8352          */
8353         clearOpacity : function(){
8354             if (window.ActiveXObject) {
8355                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8356                     this.dom.style.filter = "";
8357                 }
8358             } else {
8359                 this.dom.style.opacity = "";
8360                 this.dom.style["-moz-opacity"] = "";
8361                 this.dom.style["-khtml-opacity"] = "";
8362             }
8363             return this;
8364         },
8365
8366         /**
8367          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8368          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8369          * @return {Roo.Element} this
8370          */
8371         hide : function(animate){
8372             this.setVisible(false, this.preanim(arguments, 0));
8373             return this;
8374         },
8375
8376         /**
8377         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8378         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8379          * @return {Roo.Element} this
8380          */
8381         show : function(animate){
8382             this.setVisible(true, this.preanim(arguments, 0));
8383             return this;
8384         },
8385
8386         /**
8387          * @private Test if size has a unit, otherwise appends the default
8388          */
8389         addUnits : function(size){
8390             return Roo.Element.addUnits(size, this.defaultUnit);
8391         },
8392
8393         /**
8394          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8395          * @return {Roo.Element} this
8396          */
8397         beginMeasure : function(){
8398             var el = this.dom;
8399             if(el.offsetWidth || el.offsetHeight){
8400                 return this; // offsets work already
8401             }
8402             var changed = [];
8403             var p = this.dom, b = document.body; // start with this element
8404             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8405                 var pe = Roo.get(p);
8406                 if(pe.getStyle('display') == 'none'){
8407                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8408                     p.style.visibility = "hidden";
8409                     p.style.display = "block";
8410                 }
8411                 p = p.parentNode;
8412             }
8413             this._measureChanged = changed;
8414             return this;
8415
8416         },
8417
8418         /**
8419          * Restores displays to before beginMeasure was called
8420          * @return {Roo.Element} this
8421          */
8422         endMeasure : function(){
8423             var changed = this._measureChanged;
8424             if(changed){
8425                 for(var i = 0, len = changed.length; i < len; i++) {
8426                     var r = changed[i];
8427                     r.el.style.visibility = r.visibility;
8428                     r.el.style.display = "none";
8429                 }
8430                 this._measureChanged = null;
8431             }
8432             return this;
8433         },
8434
8435         /**
8436         * Update the innerHTML of this element, optionally searching for and processing scripts
8437         * @param {String} html The new HTML
8438         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8439         * @param {Function} callback For async script loading you can be noticed when the update completes
8440         * @return {Roo.Element} this
8441          */
8442         update : function(html, loadScripts, callback){
8443             if(typeof html == "undefined"){
8444                 html = "";
8445             }
8446             if(loadScripts !== true){
8447                 this.dom.innerHTML = html;
8448                 if(typeof callback == "function"){
8449                     callback();
8450                 }
8451                 return this;
8452             }
8453             var id = Roo.id();
8454             var dom = this.dom;
8455
8456             html += '<span id="' + id + '"></span>';
8457
8458             E.onAvailable(id, function(){
8459                 var hd = document.getElementsByTagName("head")[0];
8460                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8461                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8462                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8463
8464                 var match;
8465                 while(match = re.exec(html)){
8466                     var attrs = match[1];
8467                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8468                     if(srcMatch && srcMatch[2]){
8469                        var s = document.createElement("script");
8470                        s.src = srcMatch[2];
8471                        var typeMatch = attrs.match(typeRe);
8472                        if(typeMatch && typeMatch[2]){
8473                            s.type = typeMatch[2];
8474                        }
8475                        hd.appendChild(s);
8476                     }else if(match[2] && match[2].length > 0){
8477                         if(window.execScript) {
8478                            window.execScript(match[2]);
8479                         } else {
8480                             /**
8481                              * eval:var:id
8482                              * eval:var:dom
8483                              * eval:var:html
8484                              * 
8485                              */
8486                            window.eval(match[2]);
8487                         }
8488                     }
8489                 }
8490                 var el = document.getElementById(id);
8491                 if(el){el.parentNode.removeChild(el);}
8492                 if(typeof callback == "function"){
8493                     callback();
8494                 }
8495             });
8496             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8497             return this;
8498         },
8499
8500         /**
8501          * Direct access to the UpdateManager update() method (takes the same parameters).
8502          * @param {String/Function} url The url for this request or a function to call to get the url
8503          * @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}
8504          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8505          * @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.
8506          * @return {Roo.Element} this
8507          */
8508         load : function(){
8509             var um = this.getUpdateManager();
8510             um.update.apply(um, arguments);
8511             return this;
8512         },
8513
8514         /**
8515         * Gets this element's UpdateManager
8516         * @return {Roo.UpdateManager} The UpdateManager
8517         */
8518         getUpdateManager : function(){
8519             if(!this.updateManager){
8520                 this.updateManager = new Roo.UpdateManager(this);
8521             }
8522             return this.updateManager;
8523         },
8524
8525         /**
8526          * Disables text selection for this element (normalized across browsers)
8527          * @return {Roo.Element} this
8528          */
8529         unselectable : function(){
8530             this.dom.unselectable = "on";
8531             this.swallowEvent("selectstart", true);
8532             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8533             this.addClass("x-unselectable");
8534             return this;
8535         },
8536
8537         /**
8538         * Calculates the x, y to center this element on the screen
8539         * @return {Array} The x, y values [x, y]
8540         */
8541         getCenterXY : function(){
8542             return this.getAlignToXY(document, 'c-c');
8543         },
8544
8545         /**
8546         * Centers the Element in either the viewport, or another Element.
8547         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8548         */
8549         center : function(centerIn){
8550             this.alignTo(centerIn || document, 'c-c');
8551             return this;
8552         },
8553
8554         /**
8555          * Tests various css rules/browsers to determine if this element uses a border box
8556          * @return {Boolean}
8557          */
8558         isBorderBox : function(){
8559             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8560         },
8561
8562         /**
8563          * Return a box {x, y, width, height} that can be used to set another elements
8564          * size/location to match this element.
8565          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8566          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8567          * @return {Object} box An object in the format {x, y, width, height}
8568          */
8569         getBox : function(contentBox, local){
8570             var xy;
8571             if(!local){
8572                 xy = this.getXY();
8573             }else{
8574                 var left = parseInt(this.getStyle("left"), 10) || 0;
8575                 var top = parseInt(this.getStyle("top"), 10) || 0;
8576                 xy = [left, top];
8577             }
8578             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8579             if(!contentBox){
8580                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8581             }else{
8582                 var l = this.getBorderWidth("l")+this.getPadding("l");
8583                 var r = this.getBorderWidth("r")+this.getPadding("r");
8584                 var t = this.getBorderWidth("t")+this.getPadding("t");
8585                 var b = this.getBorderWidth("b")+this.getPadding("b");
8586                 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)};
8587             }
8588             bx.right = bx.x + bx.width;
8589             bx.bottom = bx.y + bx.height;
8590             return bx;
8591         },
8592
8593         /**
8594          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8595          for more information about the sides.
8596          * @param {String} sides
8597          * @return {Number}
8598          */
8599         getFrameWidth : function(sides, onlyContentBox){
8600             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8601         },
8602
8603         /**
8604          * 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.
8605          * @param {Object} box The box to fill {x, y, width, height}
8606          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8607          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8608          * @return {Roo.Element} this
8609          */
8610         setBox : function(box, adjust, animate){
8611             var w = box.width, h = box.height;
8612             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8613                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8614                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8615             }
8616             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8617             return this;
8618         },
8619
8620         /**
8621          * Forces the browser to repaint this element
8622          * @return {Roo.Element} this
8623          */
8624          repaint : function(){
8625             var dom = this.dom;
8626             this.addClass("x-repaint");
8627             setTimeout(function(){
8628                 Roo.get(dom).removeClass("x-repaint");
8629             }, 1);
8630             return this;
8631         },
8632
8633         /**
8634          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8635          * then it returns the calculated width of the sides (see getPadding)
8636          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8637          * @return {Object/Number}
8638          */
8639         getMargins : function(side){
8640             if(!side){
8641                 return {
8642                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8643                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8644                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8645                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8646                 };
8647             }else{
8648                 return this.addStyles(side, El.margins);
8649              }
8650         },
8651
8652         // private
8653         addStyles : function(sides, styles){
8654             var val = 0, v, w;
8655             for(var i = 0, len = sides.length; i < len; i++){
8656                 v = this.getStyle(styles[sides.charAt(i)]);
8657                 if(v){
8658                      w = parseInt(v, 10);
8659                      if(w){ val += w; }
8660                 }
8661             }
8662             return val;
8663         },
8664
8665         /**
8666          * Creates a proxy element of this element
8667          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8668          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8669          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8670          * @return {Roo.Element} The new proxy element
8671          */
8672         createProxy : function(config, renderTo, matchBox){
8673             if(renderTo){
8674                 renderTo = Roo.getDom(renderTo);
8675             }else{
8676                 renderTo = document.body;
8677             }
8678             config = typeof config == "object" ?
8679                 config : {tag : "div", cls: config};
8680             var proxy = Roo.DomHelper.append(renderTo, config, true);
8681             if(matchBox){
8682                proxy.setBox(this.getBox());
8683             }
8684             return proxy;
8685         },
8686
8687         /**
8688          * Puts a mask over this element to disable user interaction. Requires core.css.
8689          * This method can only be applied to elements which accept child nodes.
8690          * @param {String} msg (optional) A message to display in the mask
8691          * @param {String} msgCls (optional) A css class to apply to the msg element
8692          * @return {Element} The mask  element
8693          */
8694         mask : function(msg, msgCls){
8695             if(this.getStyle("position") == "static"){
8696                 this.setStyle("position", "relative");
8697             }
8698             if(!this._mask){
8699                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8700             }
8701             this.addClass("x-masked");
8702             this._mask.setDisplayed(true);
8703             if(typeof msg == 'string'){
8704                 if(!this._maskMsg){
8705                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8706                 }
8707                 var mm = this._maskMsg;
8708                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8709                 mm.dom.firstChild.innerHTML = msg;
8710                 mm.setDisplayed(true);
8711                 mm.center(this);
8712             }
8713             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8714                 this._mask.setHeight(this.getHeight());
8715             }
8716             return this._mask;
8717         },
8718
8719         /**
8720          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8721          * it is cached for reuse.
8722          */
8723         unmask : function(removeEl){
8724             if(this._mask){
8725                 if(removeEl === true){
8726                     this._mask.remove();
8727                     delete this._mask;
8728                     if(this._maskMsg){
8729                         this._maskMsg.remove();
8730                         delete this._maskMsg;
8731                     }
8732                 }else{
8733                     this._mask.setDisplayed(false);
8734                     if(this._maskMsg){
8735                         this._maskMsg.setDisplayed(false);
8736                     }
8737                 }
8738             }
8739             this.removeClass("x-masked");
8740         },
8741
8742         /**
8743          * Returns true if this element is masked
8744          * @return {Boolean}
8745          */
8746         isMasked : function(){
8747             return this._mask && this._mask.isVisible();
8748         },
8749
8750         /**
8751          * Creates an iframe shim for this element to keep selects and other windowed objects from
8752          * showing through.
8753          * @return {Roo.Element} The new shim element
8754          */
8755         createShim : function(){
8756             var el = document.createElement('iframe');
8757             el.frameBorder = 'no';
8758             el.className = 'roo-shim';
8759             if(Roo.isIE && Roo.isSecure){
8760                 el.src = Roo.SSL_SECURE_URL;
8761             }
8762             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8763             shim.autoBoxAdjust = false;
8764             return shim;
8765         },
8766
8767         /**
8768          * Removes this element from the DOM and deletes it from the cache
8769          */
8770         remove : function(){
8771             if(this.dom.parentNode){
8772                 this.dom.parentNode.removeChild(this.dom);
8773             }
8774             delete El.cache[this.dom.id];
8775         },
8776
8777         /**
8778          * Sets up event handlers to add and remove a css class when the mouse is over this element
8779          * @param {String} className
8780          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8781          * mouseout events for children elements
8782          * @return {Roo.Element} this
8783          */
8784         addClassOnOver : function(className, preventFlicker){
8785             this.on("mouseover", function(){
8786                 Roo.fly(this, '_internal').addClass(className);
8787             }, this.dom);
8788             var removeFn = function(e){
8789                 if(preventFlicker !== true || !e.within(this, true)){
8790                     Roo.fly(this, '_internal').removeClass(className);
8791                 }
8792             };
8793             this.on("mouseout", removeFn, this.dom);
8794             return this;
8795         },
8796
8797         /**
8798          * Sets up event handlers to add and remove a css class when this element has the focus
8799          * @param {String} className
8800          * @return {Roo.Element} this
8801          */
8802         addClassOnFocus : function(className){
8803             this.on("focus", function(){
8804                 Roo.fly(this, '_internal').addClass(className);
8805             }, this.dom);
8806             this.on("blur", function(){
8807                 Roo.fly(this, '_internal').removeClass(className);
8808             }, this.dom);
8809             return this;
8810         },
8811         /**
8812          * 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)
8813          * @param {String} className
8814          * @return {Roo.Element} this
8815          */
8816         addClassOnClick : function(className){
8817             var dom = this.dom;
8818             this.on("mousedown", function(){
8819                 Roo.fly(dom, '_internal').addClass(className);
8820                 var d = Roo.get(document);
8821                 var fn = function(){
8822                     Roo.fly(dom, '_internal').removeClass(className);
8823                     d.removeListener("mouseup", fn);
8824                 };
8825                 d.on("mouseup", fn);
8826             });
8827             return this;
8828         },
8829
8830         /**
8831          * Stops the specified event from bubbling and optionally prevents the default action
8832          * @param {String} eventName
8833          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8834          * @return {Roo.Element} this
8835          */
8836         swallowEvent : function(eventName, preventDefault){
8837             var fn = function(e){
8838                 e.stopPropagation();
8839                 if(preventDefault){
8840                     e.preventDefault();
8841                 }
8842             };
8843             if(eventName instanceof Array){
8844                 for(var i = 0, len = eventName.length; i < len; i++){
8845                      this.on(eventName[i], fn);
8846                 }
8847                 return this;
8848             }
8849             this.on(eventName, fn);
8850             return this;
8851         },
8852
8853         /**
8854          * @private
8855          */
8856       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8857
8858         /**
8859          * Sizes this element to its parent element's dimensions performing
8860          * neccessary box adjustments.
8861          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8862          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8863          * @return {Roo.Element} this
8864          */
8865         fitToParent : function(monitorResize, targetParent) {
8866           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8867           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8868           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8869             return;
8870           }
8871           var p = Roo.get(targetParent || this.dom.parentNode);
8872           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8873           if (monitorResize === true) {
8874             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8875             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8876           }
8877           return this;
8878         },
8879
8880         /**
8881          * Gets the next sibling, skipping text nodes
8882          * @return {HTMLElement} The next sibling or null
8883          */
8884         getNextSibling : function(){
8885             var n = this.dom.nextSibling;
8886             while(n && n.nodeType != 1){
8887                 n = n.nextSibling;
8888             }
8889             return n;
8890         },
8891
8892         /**
8893          * Gets the previous sibling, skipping text nodes
8894          * @return {HTMLElement} The previous sibling or null
8895          */
8896         getPrevSibling : function(){
8897             var n = this.dom.previousSibling;
8898             while(n && n.nodeType != 1){
8899                 n = n.previousSibling;
8900             }
8901             return n;
8902         },
8903
8904
8905         /**
8906          * Appends the passed element(s) to this element
8907          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8908          * @return {Roo.Element} this
8909          */
8910         appendChild: function(el){
8911             el = Roo.get(el);
8912             el.appendTo(this);
8913             return this;
8914         },
8915
8916         /**
8917          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8918          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8919          * automatically generated with the specified attributes.
8920          * @param {HTMLElement} insertBefore (optional) a child element of this element
8921          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8922          * @return {Roo.Element} The new child element
8923          */
8924         createChild: function(config, insertBefore, returnDom){
8925             config = config || {tag:'div'};
8926             if(insertBefore){
8927                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8928             }
8929             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8930         },
8931
8932         /**
8933          * Appends this element to the passed element
8934          * @param {String/HTMLElement/Element} el The new parent element
8935          * @return {Roo.Element} this
8936          */
8937         appendTo: function(el){
8938             el = Roo.getDom(el);
8939             el.appendChild(this.dom);
8940             return this;
8941         },
8942
8943         /**
8944          * Inserts this element before the passed element in the DOM
8945          * @param {String/HTMLElement/Element} el The element to insert before
8946          * @return {Roo.Element} this
8947          */
8948         insertBefore: function(el){
8949             el = Roo.getDom(el);
8950             el.parentNode.insertBefore(this.dom, el);
8951             return this;
8952         },
8953
8954         /**
8955          * Inserts this element after the passed element in the DOM
8956          * @param {String/HTMLElement/Element} el The element to insert after
8957          * @return {Roo.Element} this
8958          */
8959         insertAfter: function(el){
8960             el = Roo.getDom(el);
8961             el.parentNode.insertBefore(this.dom, el.nextSibling);
8962             return this;
8963         },
8964
8965         /**
8966          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8967          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8968          * @return {Roo.Element} The new child
8969          */
8970         insertFirst: function(el, returnDom){
8971             el = el || {};
8972             if(typeof el == 'object' && !el.nodeType){ // dh config
8973                 return this.createChild(el, this.dom.firstChild, returnDom);
8974             }else{
8975                 el = Roo.getDom(el);
8976                 this.dom.insertBefore(el, this.dom.firstChild);
8977                 return !returnDom ? Roo.get(el) : el;
8978             }
8979         },
8980
8981         /**
8982          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8983          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8984          * @param {String} where (optional) 'before' or 'after' defaults to before
8985          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8986          * @return {Roo.Element} the inserted Element
8987          */
8988         insertSibling: function(el, where, returnDom){
8989             where = where ? where.toLowerCase() : 'before';
8990             el = el || {};
8991             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8992
8993             if(typeof el == 'object' && !el.nodeType){ // dh config
8994                 if(where == 'after' && !this.dom.nextSibling){
8995                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8996                 }else{
8997                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8998                 }
8999
9000             }else{
9001                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9002                             where == 'before' ? this.dom : this.dom.nextSibling);
9003                 if(!returnDom){
9004                     rt = Roo.get(rt);
9005                 }
9006             }
9007             return rt;
9008         },
9009
9010         /**
9011          * Creates and wraps this element with another element
9012          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9013          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9014          * @return {HTMLElement/Element} The newly created wrapper element
9015          */
9016         wrap: function(config, returnDom){
9017             if(!config){
9018                 config = {tag: "div"};
9019             }
9020             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9021             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9022             return newEl;
9023         },
9024
9025         /**
9026          * Replaces the passed element with this element
9027          * @param {String/HTMLElement/Element} el The element to replace
9028          * @return {Roo.Element} this
9029          */
9030         replace: function(el){
9031             el = Roo.get(el);
9032             this.insertBefore(el);
9033             el.remove();
9034             return this;
9035         },
9036
9037         /**
9038          * Inserts an html fragment into this element
9039          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9040          * @param {String} html The HTML fragment
9041          * @param {Boolean} returnEl True to return an Roo.Element
9042          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9043          */
9044         insertHtml : function(where, html, returnEl){
9045             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9046             return returnEl ? Roo.get(el) : el;
9047         },
9048
9049         /**
9050          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9051          * @param {Object} o The object with the attributes
9052          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9053          * @return {Roo.Element} this
9054          */
9055         set : function(o, useSet){
9056             var el = this.dom;
9057             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9058             for(var attr in o){
9059                 if(attr == "style" || typeof o[attr] == "function") continue;
9060                 if(attr=="cls"){
9061                     el.className = o["cls"];
9062                 }else{
9063                     if(useSet) el.setAttribute(attr, o[attr]);
9064                     else el[attr] = o[attr];
9065                 }
9066             }
9067             if(o.style){
9068                 Roo.DomHelper.applyStyles(el, o.style);
9069             }
9070             return this;
9071         },
9072
9073         /**
9074          * Convenience method for constructing a KeyMap
9075          * @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:
9076          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9077          * @param {Function} fn The function to call
9078          * @param {Object} scope (optional) The scope of the function
9079          * @return {Roo.KeyMap} The KeyMap created
9080          */
9081         addKeyListener : function(key, fn, scope){
9082             var config;
9083             if(typeof key != "object" || key instanceof Array){
9084                 config = {
9085                     key: key,
9086                     fn: fn,
9087                     scope: scope
9088                 };
9089             }else{
9090                 config = {
9091                     key : key.key,
9092                     shift : key.shift,
9093                     ctrl : key.ctrl,
9094                     alt : key.alt,
9095                     fn: fn,
9096                     scope: scope
9097                 };
9098             }
9099             return new Roo.KeyMap(this, config);
9100         },
9101
9102         /**
9103          * Creates a KeyMap for this element
9104          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9105          * @return {Roo.KeyMap} The KeyMap created
9106          */
9107         addKeyMap : function(config){
9108             return new Roo.KeyMap(this, config);
9109         },
9110
9111         /**
9112          * Returns true if this element is scrollable.
9113          * @return {Boolean}
9114          */
9115          isScrollable : function(){
9116             var dom = this.dom;
9117             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9118         },
9119
9120         /**
9121          * 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().
9122          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9123          * @param {Number} value The new scroll value
9124          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9125          * @return {Element} this
9126          */
9127
9128         scrollTo : function(side, value, animate){
9129             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9130             if(!animate || !A){
9131                 this.dom[prop] = value;
9132             }else{
9133                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9134                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9135             }
9136             return this;
9137         },
9138
9139         /**
9140          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9141          * within this element's scrollable range.
9142          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9143          * @param {Number} distance How far to scroll the element in pixels
9144          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9145          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9146          * was scrolled as far as it could go.
9147          */
9148          scroll : function(direction, distance, animate){
9149              if(!this.isScrollable()){
9150                  return;
9151              }
9152              var el = this.dom;
9153              var l = el.scrollLeft, t = el.scrollTop;
9154              var w = el.scrollWidth, h = el.scrollHeight;
9155              var cw = el.clientWidth, ch = el.clientHeight;
9156              direction = direction.toLowerCase();
9157              var scrolled = false;
9158              var a = this.preanim(arguments, 2);
9159              switch(direction){
9160                  case "l":
9161                  case "left":
9162                      if(w - l > cw){
9163                          var v = Math.min(l + distance, w-cw);
9164                          this.scrollTo("left", v, a);
9165                          scrolled = true;
9166                      }
9167                      break;
9168                 case "r":
9169                 case "right":
9170                      if(l > 0){
9171                          var v = Math.max(l - distance, 0);
9172                          this.scrollTo("left", v, a);
9173                          scrolled = true;
9174                      }
9175                      break;
9176                 case "t":
9177                 case "top":
9178                 case "up":
9179                      if(t > 0){
9180                          var v = Math.max(t - distance, 0);
9181                          this.scrollTo("top", v, a);
9182                          scrolled = true;
9183                      }
9184                      break;
9185                 case "b":
9186                 case "bottom":
9187                 case "down":
9188                      if(h - t > ch){
9189                          var v = Math.min(t + distance, h-ch);
9190                          this.scrollTo("top", v, a);
9191                          scrolled = true;
9192                      }
9193                      break;
9194              }
9195              return scrolled;
9196         },
9197
9198         /**
9199          * Translates the passed page coordinates into left/top css values for this element
9200          * @param {Number/Array} x The page x or an array containing [x, y]
9201          * @param {Number} y The page y
9202          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9203          */
9204         translatePoints : function(x, y){
9205             if(typeof x == 'object' || x instanceof Array){
9206                 y = x[1]; x = x[0];
9207             }
9208             var p = this.getStyle('position');
9209             var o = this.getXY();
9210
9211             var l = parseInt(this.getStyle('left'), 10);
9212             var t = parseInt(this.getStyle('top'), 10);
9213
9214             if(isNaN(l)){
9215                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9216             }
9217             if(isNaN(t)){
9218                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9219             }
9220
9221             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9222         },
9223
9224         /**
9225          * Returns the current scroll position of the element.
9226          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9227          */
9228         getScroll : function(){
9229             var d = this.dom, doc = document;
9230             if(d == doc || d == doc.body){
9231                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9232                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9233                 return {left: l, top: t};
9234             }else{
9235                 return {left: d.scrollLeft, top: d.scrollTop};
9236             }
9237         },
9238
9239         /**
9240          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9241          * are convert to standard 6 digit hex color.
9242          * @param {String} attr The css attribute
9243          * @param {String} defaultValue The default value to use when a valid color isn't found
9244          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9245          * YUI color anims.
9246          */
9247         getColor : function(attr, defaultValue, prefix){
9248             var v = this.getStyle(attr);
9249             if(!v || v == "transparent" || v == "inherit") {
9250                 return defaultValue;
9251             }
9252             var color = typeof prefix == "undefined" ? "#" : prefix;
9253             if(v.substr(0, 4) == "rgb("){
9254                 var rvs = v.slice(4, v.length -1).split(",");
9255                 for(var i = 0; i < 3; i++){
9256                     var h = parseInt(rvs[i]).toString(16);
9257                     if(h < 16){
9258                         h = "0" + h;
9259                     }
9260                     color += h;
9261                 }
9262             } else {
9263                 if(v.substr(0, 1) == "#"){
9264                     if(v.length == 4) {
9265                         for(var i = 1; i < 4; i++){
9266                             var c = v.charAt(i);
9267                             color +=  c + c;
9268                         }
9269                     }else if(v.length == 7){
9270                         color += v.substr(1);
9271                     }
9272                 }
9273             }
9274             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9275         },
9276
9277         /**
9278          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9279          * gradient background, rounded corners and a 4-way shadow.
9280          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9281          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9282          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9283          * @return {Roo.Element} this
9284          */
9285         boxWrap : function(cls){
9286             cls = cls || 'x-box';
9287             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9288             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9289             return el;
9290         },
9291
9292         /**
9293          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9294          * @param {String} namespace The namespace in which to look for the attribute
9295          * @param {String} name The attribute name
9296          * @return {String} The attribute value
9297          */
9298         getAttributeNS : Roo.isIE ? function(ns, name){
9299             var d = this.dom;
9300             var type = typeof d[ns+":"+name];
9301             if(type != 'undefined' && type != 'unknown'){
9302                 return d[ns+":"+name];
9303             }
9304             return d[name];
9305         } : function(ns, name){
9306             var d = this.dom;
9307             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9308         }
9309     };
9310
9311     var ep = El.prototype;
9312
9313     /**
9314      * Appends an event handler (Shorthand for addListener)
9315      * @param {String}   eventName     The type of event to append
9316      * @param {Function} fn        The method the event invokes
9317      * @param {Object} scope       (optional) The scope (this object) of the fn
9318      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9319      * @method
9320      */
9321     ep.on = ep.addListener;
9322         // backwards compat
9323     ep.mon = ep.addListener;
9324
9325     /**
9326      * Removes an event handler from this element (shorthand for removeListener)
9327      * @param {String} eventName the type of event to remove
9328      * @param {Function} fn the method the event invokes
9329      * @return {Roo.Element} this
9330      * @method
9331      */
9332     ep.un = ep.removeListener;
9333
9334     /**
9335      * true to automatically adjust width and height settings for box-model issues (default to true)
9336      */
9337     ep.autoBoxAdjust = true;
9338
9339     // private
9340     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9341
9342     // private
9343     El.addUnits = function(v, defaultUnit){
9344         if(v === "" || v == "auto"){
9345             return v;
9346         }
9347         if(v === undefined){
9348             return '';
9349         }
9350         if(typeof v == "number" || !El.unitPattern.test(v)){
9351             return v + (defaultUnit || 'px');
9352         }
9353         return v;
9354     };
9355
9356     // special markup used throughout Roo when box wrapping elements
9357     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>';
9358     /**
9359      * Visibility mode constant - Use visibility to hide element
9360      * @static
9361      * @type Number
9362      */
9363     El.VISIBILITY = 1;
9364     /**
9365      * Visibility mode constant - Use display to hide element
9366      * @static
9367      * @type Number
9368      */
9369     El.DISPLAY = 2;
9370
9371     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9372     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9373     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9374
9375
9376
9377     /**
9378      * @private
9379      */
9380     El.cache = {};
9381
9382     var docEl;
9383
9384     /**
9385      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9386      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9387      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9388      * @return {Element} The Element object
9389      * @static
9390      */
9391     El.get = function(el){
9392         var ex, elm, id;
9393         if(!el){ return null; }
9394         if(typeof el == "string"){ // element id
9395             if(!(elm = document.getElementById(el))){
9396                 return null;
9397             }
9398             if(ex = El.cache[el]){
9399                 ex.dom = elm;
9400             }else{
9401                 ex = El.cache[el] = new El(elm);
9402             }
9403             return ex;
9404         }else if(el.tagName){ // dom element
9405             if(!(id = el.id)){
9406                 id = Roo.id(el);
9407             }
9408             if(ex = El.cache[id]){
9409                 ex.dom = el;
9410             }else{
9411                 ex = El.cache[id] = new El(el);
9412             }
9413             return ex;
9414         }else if(el instanceof El){
9415             if(el != docEl){
9416                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9417                                                               // catch case where it hasn't been appended
9418                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9419             }
9420             return el;
9421         }else if(el.isComposite){
9422             return el;
9423         }else if(el instanceof Array){
9424             return El.select(el);
9425         }else if(el == document){
9426             // create a bogus element object representing the document object
9427             if(!docEl){
9428                 var f = function(){};
9429                 f.prototype = El.prototype;
9430                 docEl = new f();
9431                 docEl.dom = document;
9432             }
9433             return docEl;
9434         }
9435         return null;
9436     };
9437
9438     // private
9439     El.uncache = function(el){
9440         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9441             if(a[i]){
9442                 delete El.cache[a[i].id || a[i]];
9443             }
9444         }
9445     };
9446
9447     // private
9448     // Garbage collection - uncache elements/purge listeners on orphaned elements
9449     // so we don't hold a reference and cause the browser to retain them
9450     El.garbageCollect = function(){
9451         if(!Roo.enableGarbageCollector){
9452             clearInterval(El.collectorThread);
9453             return;
9454         }
9455         for(var eid in El.cache){
9456             var el = El.cache[eid], d = el.dom;
9457             // -------------------------------------------------------
9458             // Determining what is garbage:
9459             // -------------------------------------------------------
9460             // !d
9461             // dom node is null, definitely garbage
9462             // -------------------------------------------------------
9463             // !d.parentNode
9464             // no parentNode == direct orphan, definitely garbage
9465             // -------------------------------------------------------
9466             // !d.offsetParent && !document.getElementById(eid)
9467             // display none elements have no offsetParent so we will
9468             // also try to look it up by it's id. However, check
9469             // offsetParent first so we don't do unneeded lookups.
9470             // This enables collection of elements that are not orphans
9471             // directly, but somewhere up the line they have an orphan
9472             // parent.
9473             // -------------------------------------------------------
9474             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9475                 delete El.cache[eid];
9476                 if(d && Roo.enableListenerCollection){
9477                     E.purgeElement(d);
9478                 }
9479             }
9480         }
9481     }
9482     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9483
9484
9485     // dom is optional
9486     El.Flyweight = function(dom){
9487         this.dom = dom;
9488     };
9489     El.Flyweight.prototype = El.prototype;
9490
9491     El._flyweights = {};
9492     /**
9493      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9494      * the dom node can be overwritten by other code.
9495      * @param {String/HTMLElement} el The dom node or id
9496      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9497      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9498      * @static
9499      * @return {Element} The shared Element object
9500      */
9501     El.fly = function(el, named){
9502         named = named || '_global';
9503         el = Roo.getDom(el);
9504         if(!el){
9505             return null;
9506         }
9507         if(!El._flyweights[named]){
9508             El._flyweights[named] = new El.Flyweight();
9509         }
9510         El._flyweights[named].dom = el;
9511         return El._flyweights[named];
9512     };
9513
9514     /**
9515      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9516      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9517      * Shorthand of {@link Roo.Element#get}
9518      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9519      * @return {Element} The Element object
9520      * @member Roo
9521      * @method get
9522      */
9523     Roo.get = El.get;
9524     /**
9525      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9526      * the dom node can be overwritten by other code.
9527      * Shorthand of {@link Roo.Element#fly}
9528      * @param {String/HTMLElement} el The dom node or id
9529      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9530      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9531      * @static
9532      * @return {Element} The shared Element object
9533      * @member Roo
9534      * @method fly
9535      */
9536     Roo.fly = El.fly;
9537
9538     // speedy lookup for elements never to box adjust
9539     var noBoxAdjust = Roo.isStrict ? {
9540         select:1
9541     } : {
9542         input:1, select:1, textarea:1
9543     };
9544     if(Roo.isIE || Roo.isGecko){
9545         noBoxAdjust['button'] = 1;
9546     }
9547
9548
9549     Roo.EventManager.on(window, 'unload', function(){
9550         delete El.cache;
9551         delete El._flyweights;
9552     });
9553 })();
9554
9555
9556
9557
9558 if(Roo.DomQuery){
9559     Roo.Element.selectorFunction = Roo.DomQuery.select;
9560 }
9561
9562 Roo.Element.select = function(selector, unique, root){
9563     var els;
9564     if(typeof selector == "string"){
9565         els = Roo.Element.selectorFunction(selector, root);
9566     }else if(selector.length !== undefined){
9567         els = selector;
9568     }else{
9569         throw "Invalid selector";
9570     }
9571     if(unique === true){
9572         return new Roo.CompositeElement(els);
9573     }else{
9574         return new Roo.CompositeElementLite(els);
9575     }
9576 };
9577 /**
9578  * Selects elements based on the passed CSS selector to enable working on them as 1.
9579  * @param {String/Array} selector The CSS selector or an array of elements
9580  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9581  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9582  * @return {CompositeElementLite/CompositeElement}
9583  * @member Roo
9584  * @method select
9585  */
9586 Roo.select = Roo.Element.select;
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601 /*
9602  * Based on:
9603  * Ext JS Library 1.1.1
9604  * Copyright(c) 2006-2007, Ext JS, LLC.
9605  *
9606  * Originally Released Under LGPL - original licence link has changed is not relivant.
9607  *
9608  * Fork - LGPL
9609  * <script type="text/javascript">
9610  */
9611
9612
9613
9614 //Notifies Element that fx methods are available
9615 Roo.enableFx = true;
9616
9617 /**
9618  * @class Roo.Fx
9619  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9620  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9621  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9622  * Element effects to work.</p><br/>
9623  *
9624  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9625  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9626  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9627  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9628  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9629  * expected results and should be done with care.</p><br/>
9630  *
9631  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9632  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9633 <pre>
9634 Value  Description
9635 -----  -----------------------------
9636 tl     The top left corner
9637 t      The center of the top edge
9638 tr     The top right corner
9639 l      The center of the left edge
9640 r      The center of the right edge
9641 bl     The bottom left corner
9642 b      The center of the bottom edge
9643 br     The bottom right corner
9644 </pre>
9645  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9646  * below are common options that can be passed to any Fx method.</b>
9647  * @cfg {Function} callback A function called when the effect is finished
9648  * @cfg {Object} scope The scope of the effect function
9649  * @cfg {String} easing A valid Easing value for the effect
9650  * @cfg {String} afterCls A css class to apply after the effect
9651  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9652  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9653  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9654  * effects that end with the element being visually hidden, ignored otherwise)
9655  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9656  * a function which returns such a specification that will be applied to the Element after the effect finishes
9657  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9658  * @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
9659  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9660  */
9661 Roo.Fx = {
9662         /**
9663          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9664          * origin for the slide effect.  This function automatically handles wrapping the element with
9665          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9666          * Usage:
9667          *<pre><code>
9668 // default: slide the element in from the top
9669 el.slideIn();
9670
9671 // custom: slide the element in from the right with a 2-second duration
9672 el.slideIn('r', { duration: 2 });
9673
9674 // common config options shown with default values
9675 el.slideIn('t', {
9676     easing: 'easeOut',
9677     duration: .5
9678 });
9679 </code></pre>
9680          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9681          * @param {Object} options (optional) Object literal with any of the Fx config options
9682          * @return {Roo.Element} The Element
9683          */
9684     slideIn : function(anchor, o){
9685         var el = this.getFxEl();
9686         o = o || {};
9687
9688         el.queueFx(o, function(){
9689
9690             anchor = anchor || "t";
9691
9692             // fix display to visibility
9693             this.fixDisplay();
9694
9695             // restore values after effect
9696             var r = this.getFxRestore();
9697             var b = this.getBox();
9698             // fixed size for slide
9699             this.setSize(b);
9700
9701             // wrap if needed
9702             var wrap = this.fxWrap(r.pos, o, "hidden");
9703
9704             var st = this.dom.style;
9705             st.visibility = "visible";
9706             st.position = "absolute";
9707
9708             // clear out temp styles after slide and unwrap
9709             var after = function(){
9710                 el.fxUnwrap(wrap, r.pos, o);
9711                 st.width = r.width;
9712                 st.height = r.height;
9713                 el.afterFx(o);
9714             };
9715             // time to calc the positions
9716             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9717
9718             switch(anchor.toLowerCase()){
9719                 case "t":
9720                     wrap.setSize(b.width, 0);
9721                     st.left = st.bottom = "0";
9722                     a = {height: bh};
9723                 break;
9724                 case "l":
9725                     wrap.setSize(0, b.height);
9726                     st.right = st.top = "0";
9727                     a = {width: bw};
9728                 break;
9729                 case "r":
9730                     wrap.setSize(0, b.height);
9731                     wrap.setX(b.right);
9732                     st.left = st.top = "0";
9733                     a = {width: bw, points: pt};
9734                 break;
9735                 case "b":
9736                     wrap.setSize(b.width, 0);
9737                     wrap.setY(b.bottom);
9738                     st.left = st.top = "0";
9739                     a = {height: bh, points: pt};
9740                 break;
9741                 case "tl":
9742                     wrap.setSize(0, 0);
9743                     st.right = st.bottom = "0";
9744                     a = {width: bw, height: bh};
9745                 break;
9746                 case "bl":
9747                     wrap.setSize(0, 0);
9748                     wrap.setY(b.y+b.height);
9749                     st.right = st.top = "0";
9750                     a = {width: bw, height: bh, points: pt};
9751                 break;
9752                 case "br":
9753                     wrap.setSize(0, 0);
9754                     wrap.setXY([b.right, b.bottom]);
9755                     st.left = st.top = "0";
9756                     a = {width: bw, height: bh, points: pt};
9757                 break;
9758                 case "tr":
9759                     wrap.setSize(0, 0);
9760                     wrap.setX(b.x+b.width);
9761                     st.left = st.bottom = "0";
9762                     a = {width: bw, height: bh, points: pt};
9763                 break;
9764             }
9765             this.dom.style.visibility = "visible";
9766             wrap.show();
9767
9768             arguments.callee.anim = wrap.fxanim(a,
9769                 o,
9770                 'motion',
9771                 .5,
9772                 'easeOut', after);
9773         });
9774         return this;
9775     },
9776     
9777         /**
9778          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9779          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9780          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9781          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9782          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9783          * Usage:
9784          *<pre><code>
9785 // default: slide the element out to the top
9786 el.slideOut();
9787
9788 // custom: slide the element out to the right with a 2-second duration
9789 el.slideOut('r', { duration: 2 });
9790
9791 // common config options shown with default values
9792 el.slideOut('t', {
9793     easing: 'easeOut',
9794     duration: .5,
9795     remove: false,
9796     useDisplay: false
9797 });
9798 </code></pre>
9799          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9800          * @param {Object} options (optional) Object literal with any of the Fx config options
9801          * @return {Roo.Element} The Element
9802          */
9803     slideOut : function(anchor, o){
9804         var el = this.getFxEl();
9805         o = o || {};
9806
9807         el.queueFx(o, function(){
9808
9809             anchor = anchor || "t";
9810
9811             // restore values after effect
9812             var r = this.getFxRestore();
9813             
9814             var b = this.getBox();
9815             // fixed size for slide
9816             this.setSize(b);
9817
9818             // wrap if needed
9819             var wrap = this.fxWrap(r.pos, o, "visible");
9820
9821             var st = this.dom.style;
9822             st.visibility = "visible";
9823             st.position = "absolute";
9824
9825             wrap.setSize(b);
9826
9827             var after = function(){
9828                 if(o.useDisplay){
9829                     el.setDisplayed(false);
9830                 }else{
9831                     el.hide();
9832                 }
9833
9834                 el.fxUnwrap(wrap, r.pos, o);
9835
9836                 st.width = r.width;
9837                 st.height = r.height;
9838
9839                 el.afterFx(o);
9840             };
9841
9842             var a, zero = {to: 0};
9843             switch(anchor.toLowerCase()){
9844                 case "t":
9845                     st.left = st.bottom = "0";
9846                     a = {height: zero};
9847                 break;
9848                 case "l":
9849                     st.right = st.top = "0";
9850                     a = {width: zero};
9851                 break;
9852                 case "r":
9853                     st.left = st.top = "0";
9854                     a = {width: zero, points: {to:[b.right, b.y]}};
9855                 break;
9856                 case "b":
9857                     st.left = st.top = "0";
9858                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9859                 break;
9860                 case "tl":
9861                     st.right = st.bottom = "0";
9862                     a = {width: zero, height: zero};
9863                 break;
9864                 case "bl":
9865                     st.right = st.top = "0";
9866                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9867                 break;
9868                 case "br":
9869                     st.left = st.top = "0";
9870                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9871                 break;
9872                 case "tr":
9873                     st.left = st.bottom = "0";
9874                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9875                 break;
9876             }
9877
9878             arguments.callee.anim = wrap.fxanim(a,
9879                 o,
9880                 'motion',
9881                 .5,
9882                 "easeOut", after);
9883         });
9884         return this;
9885     },
9886
9887         /**
9888          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9889          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9890          * The element must be removed from the DOM using the 'remove' config option if desired.
9891          * Usage:
9892          *<pre><code>
9893 // default
9894 el.puff();
9895
9896 // common config options shown with default values
9897 el.puff({
9898     easing: 'easeOut',
9899     duration: .5,
9900     remove: false,
9901     useDisplay: false
9902 });
9903 </code></pre>
9904          * @param {Object} options (optional) Object literal with any of the Fx config options
9905          * @return {Roo.Element} The Element
9906          */
9907     puff : function(o){
9908         var el = this.getFxEl();
9909         o = o || {};
9910
9911         el.queueFx(o, function(){
9912             this.clearOpacity();
9913             this.show();
9914
9915             // restore values after effect
9916             var r = this.getFxRestore();
9917             var st = this.dom.style;
9918
9919             var after = function(){
9920                 if(o.useDisplay){
9921                     el.setDisplayed(false);
9922                 }else{
9923                     el.hide();
9924                 }
9925
9926                 el.clearOpacity();
9927
9928                 el.setPositioning(r.pos);
9929                 st.width = r.width;
9930                 st.height = r.height;
9931                 st.fontSize = '';
9932                 el.afterFx(o);
9933             };
9934
9935             var width = this.getWidth();
9936             var height = this.getHeight();
9937
9938             arguments.callee.anim = this.fxanim({
9939                     width : {to: this.adjustWidth(width * 2)},
9940                     height : {to: this.adjustHeight(height * 2)},
9941                     points : {by: [-(width * .5), -(height * .5)]},
9942                     opacity : {to: 0},
9943                     fontSize: {to:200, unit: "%"}
9944                 },
9945                 o,
9946                 'motion',
9947                 .5,
9948                 "easeOut", after);
9949         });
9950         return this;
9951     },
9952
9953         /**
9954          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9955          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9956          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9957          * Usage:
9958          *<pre><code>
9959 // default
9960 el.switchOff();
9961
9962 // all config options shown with default values
9963 el.switchOff({
9964     easing: 'easeIn',
9965     duration: .3,
9966     remove: false,
9967     useDisplay: false
9968 });
9969 </code></pre>
9970          * @param {Object} options (optional) Object literal with any of the Fx config options
9971          * @return {Roo.Element} The Element
9972          */
9973     switchOff : function(o){
9974         var el = this.getFxEl();
9975         o = o || {};
9976
9977         el.queueFx(o, function(){
9978             this.clearOpacity();
9979             this.clip();
9980
9981             // restore values after effect
9982             var r = this.getFxRestore();
9983             var st = this.dom.style;
9984
9985             var after = function(){
9986                 if(o.useDisplay){
9987                     el.setDisplayed(false);
9988                 }else{
9989                     el.hide();
9990                 }
9991
9992                 el.clearOpacity();
9993                 el.setPositioning(r.pos);
9994                 st.width = r.width;
9995                 st.height = r.height;
9996
9997                 el.afterFx(o);
9998             };
9999
10000             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10001                 this.clearOpacity();
10002                 (function(){
10003                     this.fxanim({
10004                         height:{to:1},
10005                         points:{by:[0, this.getHeight() * .5]}
10006                     }, o, 'motion', 0.3, 'easeIn', after);
10007                 }).defer(100, this);
10008             });
10009         });
10010         return this;
10011     },
10012
10013     /**
10014      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10015      * changed using the "attr" config option) and then fading back to the original color. If no original
10016      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10017      * Usage:
10018 <pre><code>
10019 // default: highlight background to yellow
10020 el.highlight();
10021
10022 // custom: highlight foreground text to blue for 2 seconds
10023 el.highlight("0000ff", { attr: 'color', duration: 2 });
10024
10025 // common config options shown with default values
10026 el.highlight("ffff9c", {
10027     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10028     endColor: (current color) or "ffffff",
10029     easing: 'easeIn',
10030     duration: 1
10031 });
10032 </code></pre>
10033      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10034      * @param {Object} options (optional) Object literal with any of the Fx config options
10035      * @return {Roo.Element} The Element
10036      */ 
10037     highlight : function(color, o){
10038         var el = this.getFxEl();
10039         o = o || {};
10040
10041         el.queueFx(o, function(){
10042             color = color || "ffff9c";
10043             attr = o.attr || "backgroundColor";
10044
10045             this.clearOpacity();
10046             this.show();
10047
10048             var origColor = this.getColor(attr);
10049             var restoreColor = this.dom.style[attr];
10050             endColor = (o.endColor || origColor) || "ffffff";
10051
10052             var after = function(){
10053                 el.dom.style[attr] = restoreColor;
10054                 el.afterFx(o);
10055             };
10056
10057             var a = {};
10058             a[attr] = {from: color, to: endColor};
10059             arguments.callee.anim = this.fxanim(a,
10060                 o,
10061                 'color',
10062                 1,
10063                 'easeIn', after);
10064         });
10065         return this;
10066     },
10067
10068    /**
10069     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10070     * Usage:
10071 <pre><code>
10072 // default: a single light blue ripple
10073 el.frame();
10074
10075 // custom: 3 red ripples lasting 3 seconds total
10076 el.frame("ff0000", 3, { duration: 3 });
10077
10078 // common config options shown with default values
10079 el.frame("C3DAF9", 1, {
10080     duration: 1 //duration of entire animation (not each individual ripple)
10081     // Note: Easing is not configurable and will be ignored if included
10082 });
10083 </code></pre>
10084     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10085     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10086     * @param {Object} options (optional) Object literal with any of the Fx config options
10087     * @return {Roo.Element} The Element
10088     */
10089     frame : function(color, count, o){
10090         var el = this.getFxEl();
10091         o = o || {};
10092
10093         el.queueFx(o, function(){
10094             color = color || "#C3DAF9";
10095             if(color.length == 6){
10096                 color = "#" + color;
10097             }
10098             count = count || 1;
10099             duration = o.duration || 1;
10100             this.show();
10101
10102             var b = this.getBox();
10103             var animFn = function(){
10104                 var proxy = this.createProxy({
10105
10106                      style:{
10107                         visbility:"hidden",
10108                         position:"absolute",
10109                         "z-index":"35000", // yee haw
10110                         border:"0px solid " + color
10111                      }
10112                   });
10113                 var scale = Roo.isBorderBox ? 2 : 1;
10114                 proxy.animate({
10115                     top:{from:b.y, to:b.y - 20},
10116                     left:{from:b.x, to:b.x - 20},
10117                     borderWidth:{from:0, to:10},
10118                     opacity:{from:1, to:0},
10119                     height:{from:b.height, to:(b.height + (20*scale))},
10120                     width:{from:b.width, to:(b.width + (20*scale))}
10121                 }, duration, function(){
10122                     proxy.remove();
10123                 });
10124                 if(--count > 0){
10125                      animFn.defer((duration/2)*1000, this);
10126                 }else{
10127                     el.afterFx(o);
10128                 }
10129             };
10130             animFn.call(this);
10131         });
10132         return this;
10133     },
10134
10135    /**
10136     * Creates a pause before any subsequent queued effects begin.  If there are
10137     * no effects queued after the pause it will have no effect.
10138     * Usage:
10139 <pre><code>
10140 el.pause(1);
10141 </code></pre>
10142     * @param {Number} seconds The length of time to pause (in seconds)
10143     * @return {Roo.Element} The Element
10144     */
10145     pause : function(seconds){
10146         var el = this.getFxEl();
10147         var o = {};
10148
10149         el.queueFx(o, function(){
10150             setTimeout(function(){
10151                 el.afterFx(o);
10152             }, seconds * 1000);
10153         });
10154         return this;
10155     },
10156
10157    /**
10158     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10159     * using the "endOpacity" config option.
10160     * Usage:
10161 <pre><code>
10162 // default: fade in from opacity 0 to 100%
10163 el.fadeIn();
10164
10165 // custom: fade in from opacity 0 to 75% over 2 seconds
10166 el.fadeIn({ endOpacity: .75, duration: 2});
10167
10168 // common config options shown with default values
10169 el.fadeIn({
10170     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10171     easing: 'easeOut',
10172     duration: .5
10173 });
10174 </code></pre>
10175     * @param {Object} options (optional) Object literal with any of the Fx config options
10176     * @return {Roo.Element} The Element
10177     */
10178     fadeIn : function(o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181         el.queueFx(o, function(){
10182             this.setOpacity(0);
10183             this.fixDisplay();
10184             this.dom.style.visibility = 'visible';
10185             var to = o.endOpacity || 1;
10186             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10187                 o, null, .5, "easeOut", function(){
10188                 if(to == 1){
10189                     this.clearOpacity();
10190                 }
10191                 el.afterFx(o);
10192             });
10193         });
10194         return this;
10195     },
10196
10197    /**
10198     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10199     * using the "endOpacity" config option.
10200     * Usage:
10201 <pre><code>
10202 // default: fade out from the element's current opacity to 0
10203 el.fadeOut();
10204
10205 // custom: fade out from the element's current opacity to 25% over 2 seconds
10206 el.fadeOut({ endOpacity: .25, duration: 2});
10207
10208 // common config options shown with default values
10209 el.fadeOut({
10210     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10211     easing: 'easeOut',
10212     duration: .5
10213     remove: false,
10214     useDisplay: false
10215 });
10216 </code></pre>
10217     * @param {Object} options (optional) Object literal with any of the Fx config options
10218     * @return {Roo.Element} The Element
10219     */
10220     fadeOut : function(o){
10221         var el = this.getFxEl();
10222         o = o || {};
10223         el.queueFx(o, function(){
10224             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10225                 o, null, .5, "easeOut", function(){
10226                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10227                      this.dom.style.display = "none";
10228                 }else{
10229                      this.dom.style.visibility = "hidden";
10230                 }
10231                 this.clearOpacity();
10232                 el.afterFx(o);
10233             });
10234         });
10235         return this;
10236     },
10237
10238    /**
10239     * Animates the transition of an element's dimensions from a starting height/width
10240     * to an ending height/width.
10241     * Usage:
10242 <pre><code>
10243 // change height and width to 100x100 pixels
10244 el.scale(100, 100);
10245
10246 // common config options shown with default values.  The height and width will default to
10247 // the element's existing values if passed as null.
10248 el.scale(
10249     [element's width],
10250     [element's height], {
10251     easing: 'easeOut',
10252     duration: .35
10253 });
10254 </code></pre>
10255     * @param {Number} width  The new width (pass undefined to keep the original width)
10256     * @param {Number} height  The new height (pass undefined to keep the original height)
10257     * @param {Object} options (optional) Object literal with any of the Fx config options
10258     * @return {Roo.Element} The Element
10259     */
10260     scale : function(w, h, o){
10261         this.shift(Roo.apply({}, o, {
10262             width: w,
10263             height: h
10264         }));
10265         return this;
10266     },
10267
10268    /**
10269     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10270     * Any of these properties not specified in the config object will not be changed.  This effect 
10271     * requires that at least one new dimension, position or opacity setting must be passed in on
10272     * the config object in order for the function to have any effect.
10273     * Usage:
10274 <pre><code>
10275 // slide the element horizontally to x position 200 while changing the height and opacity
10276 el.shift({ x: 200, height: 50, opacity: .8 });
10277
10278 // common config options shown with default values.
10279 el.shift({
10280     width: [element's width],
10281     height: [element's height],
10282     x: [element's x position],
10283     y: [element's y position],
10284     opacity: [element's opacity],
10285     easing: 'easeOut',
10286     duration: .35
10287 });
10288 </code></pre>
10289     * @param {Object} options  Object literal with any of the Fx config options
10290     * @return {Roo.Element} The Element
10291     */
10292     shift : function(o){
10293         var el = this.getFxEl();
10294         o = o || {};
10295         el.queueFx(o, function(){
10296             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10297             if(w !== undefined){
10298                 a.width = {to: this.adjustWidth(w)};
10299             }
10300             if(h !== undefined){
10301                 a.height = {to: this.adjustHeight(h)};
10302             }
10303             if(x !== undefined || y !== undefined){
10304                 a.points = {to: [
10305                     x !== undefined ? x : this.getX(),
10306                     y !== undefined ? y : this.getY()
10307                 ]};
10308             }
10309             if(op !== undefined){
10310                 a.opacity = {to: op};
10311             }
10312             if(o.xy !== undefined){
10313                 a.points = {to: o.xy};
10314             }
10315             arguments.callee.anim = this.fxanim(a,
10316                 o, 'motion', .35, "easeOut", function(){
10317                 el.afterFx(o);
10318             });
10319         });
10320         return this;
10321     },
10322
10323         /**
10324          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10325          * ending point of the effect.
10326          * Usage:
10327          *<pre><code>
10328 // default: slide the element downward while fading out
10329 el.ghost();
10330
10331 // custom: slide the element out to the right with a 2-second duration
10332 el.ghost('r', { duration: 2 });
10333
10334 // common config options shown with default values
10335 el.ghost('b', {
10336     easing: 'easeOut',
10337     duration: .5
10338     remove: false,
10339     useDisplay: false
10340 });
10341 </code></pre>
10342          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10343          * @param {Object} options (optional) Object literal with any of the Fx config options
10344          * @return {Roo.Element} The Element
10345          */
10346     ghost : function(anchor, o){
10347         var el = this.getFxEl();
10348         o = o || {};
10349
10350         el.queueFx(o, function(){
10351             anchor = anchor || "b";
10352
10353             // restore values after effect
10354             var r = this.getFxRestore();
10355             var w = this.getWidth(),
10356                 h = this.getHeight();
10357
10358             var st = this.dom.style;
10359
10360             var after = function(){
10361                 if(o.useDisplay){
10362                     el.setDisplayed(false);
10363                 }else{
10364                     el.hide();
10365                 }
10366
10367                 el.clearOpacity();
10368                 el.setPositioning(r.pos);
10369                 st.width = r.width;
10370                 st.height = r.height;
10371
10372                 el.afterFx(o);
10373             };
10374
10375             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10376             switch(anchor.toLowerCase()){
10377                 case "t":
10378                     pt.by = [0, -h];
10379                 break;
10380                 case "l":
10381                     pt.by = [-w, 0];
10382                 break;
10383                 case "r":
10384                     pt.by = [w, 0];
10385                 break;
10386                 case "b":
10387                     pt.by = [0, h];
10388                 break;
10389                 case "tl":
10390                     pt.by = [-w, -h];
10391                 break;
10392                 case "bl":
10393                     pt.by = [-w, h];
10394                 break;
10395                 case "br":
10396                     pt.by = [w, h];
10397                 break;
10398                 case "tr":
10399                     pt.by = [w, -h];
10400                 break;
10401             }
10402
10403             arguments.callee.anim = this.fxanim(a,
10404                 o,
10405                 'motion',
10406                 .5,
10407                 "easeOut", after);
10408         });
10409         return this;
10410     },
10411
10412         /**
10413          * Ensures that all effects queued after syncFx is called on the element are
10414          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10415          * @return {Roo.Element} The Element
10416          */
10417     syncFx : function(){
10418         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10419             block : false,
10420             concurrent : true,
10421             stopFx : false
10422         });
10423         return this;
10424     },
10425
10426         /**
10427          * Ensures that all effects queued after sequenceFx is called on the element are
10428          * run in sequence.  This is the opposite of {@link #syncFx}.
10429          * @return {Roo.Element} The Element
10430          */
10431     sequenceFx : function(){
10432         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10433             block : false,
10434             concurrent : false,
10435             stopFx : false
10436         });
10437         return this;
10438     },
10439
10440         /* @private */
10441     nextFx : function(){
10442         var ef = this.fxQueue[0];
10443         if(ef){
10444             ef.call(this);
10445         }
10446     },
10447
10448         /**
10449          * Returns true if the element has any effects actively running or queued, else returns false.
10450          * @return {Boolean} True if element has active effects, else false
10451          */
10452     hasActiveFx : function(){
10453         return this.fxQueue && this.fxQueue[0];
10454     },
10455
10456         /**
10457          * Stops any running effects and clears the element's internal effects queue if it contains
10458          * any additional effects that haven't started yet.
10459          * @return {Roo.Element} The Element
10460          */
10461     stopFx : function(){
10462         if(this.hasActiveFx()){
10463             var cur = this.fxQueue[0];
10464             if(cur && cur.anim && cur.anim.isAnimated()){
10465                 this.fxQueue = [cur]; // clear out others
10466                 cur.anim.stop(true);
10467             }
10468         }
10469         return this;
10470     },
10471
10472         /* @private */
10473     beforeFx : function(o){
10474         if(this.hasActiveFx() && !o.concurrent){
10475            if(o.stopFx){
10476                this.stopFx();
10477                return true;
10478            }
10479            return false;
10480         }
10481         return true;
10482     },
10483
10484         /**
10485          * Returns true if the element is currently blocking so that no other effect can be queued
10486          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10487          * used to ensure that an effect initiated by a user action runs to completion prior to the
10488          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10489          * @return {Boolean} True if blocking, else false
10490          */
10491     hasFxBlock : function(){
10492         var q = this.fxQueue;
10493         return q && q[0] && q[0].block;
10494     },
10495
10496         /* @private */
10497     queueFx : function(o, fn){
10498         if(!this.fxQueue){
10499             this.fxQueue = [];
10500         }
10501         if(!this.hasFxBlock()){
10502             Roo.applyIf(o, this.fxDefaults);
10503             if(!o.concurrent){
10504                 var run = this.beforeFx(o);
10505                 fn.block = o.block;
10506                 this.fxQueue.push(fn);
10507                 if(run){
10508                     this.nextFx();
10509                 }
10510             }else{
10511                 fn.call(this);
10512             }
10513         }
10514         return this;
10515     },
10516
10517         /* @private */
10518     fxWrap : function(pos, o, vis){
10519         var wrap;
10520         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10521             var wrapXY;
10522             if(o.fixPosition){
10523                 wrapXY = this.getXY();
10524             }
10525             var div = document.createElement("div");
10526             div.style.visibility = vis;
10527             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10528             wrap.setPositioning(pos);
10529             if(wrap.getStyle("position") == "static"){
10530                 wrap.position("relative");
10531             }
10532             this.clearPositioning('auto');
10533             wrap.clip();
10534             wrap.dom.appendChild(this.dom);
10535             if(wrapXY){
10536                 wrap.setXY(wrapXY);
10537             }
10538         }
10539         return wrap;
10540     },
10541
10542         /* @private */
10543     fxUnwrap : function(wrap, pos, o){
10544         this.clearPositioning();
10545         this.setPositioning(pos);
10546         if(!o.wrap){
10547             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10548             wrap.remove();
10549         }
10550     },
10551
10552         /* @private */
10553     getFxRestore : function(){
10554         var st = this.dom.style;
10555         return {pos: this.getPositioning(), width: st.width, height : st.height};
10556     },
10557
10558         /* @private */
10559     afterFx : function(o){
10560         if(o.afterStyle){
10561             this.applyStyles(o.afterStyle);
10562         }
10563         if(o.afterCls){
10564             this.addClass(o.afterCls);
10565         }
10566         if(o.remove === true){
10567             this.remove();
10568         }
10569         Roo.callback(o.callback, o.scope, [this]);
10570         if(!o.concurrent){
10571             this.fxQueue.shift();
10572             this.nextFx();
10573         }
10574     },
10575
10576         /* @private */
10577     getFxEl : function(){ // support for composite element fx
10578         return Roo.get(this.dom);
10579     },
10580
10581         /* @private */
10582     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10583         animType = animType || 'run';
10584         opt = opt || {};
10585         var anim = Roo.lib.Anim[animType](
10586             this.dom, args,
10587             (opt.duration || defaultDur) || .35,
10588             (opt.easing || defaultEase) || 'easeOut',
10589             function(){
10590                 Roo.callback(cb, this);
10591             },
10592             this
10593         );
10594         opt.anim = anim;
10595         return anim;
10596     }
10597 };
10598
10599 // backwords compat
10600 Roo.Fx.resize = Roo.Fx.scale;
10601
10602 //When included, Roo.Fx is automatically applied to Element so that all basic
10603 //effects are available directly via the Element API
10604 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10605  * Based on:
10606  * Ext JS Library 1.1.1
10607  * Copyright(c) 2006-2007, Ext JS, LLC.
10608  *
10609  * Originally Released Under LGPL - original licence link has changed is not relivant.
10610  *
10611  * Fork - LGPL
10612  * <script type="text/javascript">
10613  */
10614
10615
10616 /**
10617  * @class Roo.CompositeElement
10618  * Standard composite class. Creates a Roo.Element for every element in the collection.
10619  * <br><br>
10620  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10621  * actions will be performed on all the elements in this collection.</b>
10622  * <br><br>
10623  * All methods return <i>this</i> and can be chained.
10624  <pre><code>
10625  var els = Roo.select("#some-el div.some-class", true);
10626  // or select directly from an existing element
10627  var el = Roo.get('some-el');
10628  el.select('div.some-class', true);
10629
10630  els.setWidth(100); // all elements become 100 width
10631  els.hide(true); // all elements fade out and hide
10632  // or
10633  els.setWidth(100).hide(true);
10634  </code></pre>
10635  */
10636 Roo.CompositeElement = function(els){
10637     this.elements = [];
10638     this.addElements(els);
10639 };
10640 Roo.CompositeElement.prototype = {
10641     isComposite: true,
10642     addElements : function(els){
10643         if(!els) return this;
10644         if(typeof els == "string"){
10645             els = Roo.Element.selectorFunction(els);
10646         }
10647         var yels = this.elements;
10648         var index = yels.length-1;
10649         for(var i = 0, len = els.length; i < len; i++) {
10650                 yels[++index] = Roo.get(els[i]);
10651         }
10652         return this;
10653     },
10654
10655     /**
10656     * Clears this composite and adds the elements returned by the passed selector.
10657     * @param {String/Array} els A string CSS selector, an array of elements or an element
10658     * @return {CompositeElement} this
10659     */
10660     fill : function(els){
10661         this.elements = [];
10662         this.add(els);
10663         return this;
10664     },
10665
10666     /**
10667     * Filters this composite to only elements that match the passed selector.
10668     * @param {String} selector A string CSS selector
10669     * @return {CompositeElement} this
10670     */
10671     filter : function(selector){
10672         var els = [];
10673         this.each(function(el){
10674             if(el.is(selector)){
10675                 els[els.length] = el.dom;
10676             }
10677         });
10678         this.fill(els);
10679         return this;
10680     },
10681
10682     invoke : function(fn, args){
10683         var els = this.elements;
10684         for(var i = 0, len = els.length; i < len; i++) {
10685                 Roo.Element.prototype[fn].apply(els[i], args);
10686         }
10687         return this;
10688     },
10689     /**
10690     * Adds elements to this composite.
10691     * @param {String/Array} els A string CSS selector, an array of elements or an element
10692     * @return {CompositeElement} this
10693     */
10694     add : function(els){
10695         if(typeof els == "string"){
10696             this.addElements(Roo.Element.selectorFunction(els));
10697         }else if(els.length !== undefined){
10698             this.addElements(els);
10699         }else{
10700             this.addElements([els]);
10701         }
10702         return this;
10703     },
10704     /**
10705     * Calls the passed function passing (el, this, index) for each element in this composite.
10706     * @param {Function} fn The function to call
10707     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10708     * @return {CompositeElement} this
10709     */
10710     each : function(fn, scope){
10711         var els = this.elements;
10712         for(var i = 0, len = els.length; i < len; i++){
10713             if(fn.call(scope || els[i], els[i], this, i) === false) {
10714                 break;
10715             }
10716         }
10717         return this;
10718     },
10719
10720     /**
10721      * Returns the Element object at the specified index
10722      * @param {Number} index
10723      * @return {Roo.Element}
10724      */
10725     item : function(index){
10726         return this.elements[index] || null;
10727     },
10728
10729     /**
10730      * Returns the first Element
10731      * @return {Roo.Element}
10732      */
10733     first : function(){
10734         return this.item(0);
10735     },
10736
10737     /**
10738      * Returns the last Element
10739      * @return {Roo.Element}
10740      */
10741     last : function(){
10742         return this.item(this.elements.length-1);
10743     },
10744
10745     /**
10746      * Returns the number of elements in this composite
10747      * @return Number
10748      */
10749     getCount : function(){
10750         return this.elements.length;
10751     },
10752
10753     /**
10754      * Returns true if this composite contains the passed element
10755      * @return Boolean
10756      */
10757     contains : function(el){
10758         return this.indexOf(el) !== -1;
10759     },
10760
10761     /**
10762      * Returns true if this composite contains the passed element
10763      * @return Boolean
10764      */
10765     indexOf : function(el){
10766         return this.elements.indexOf(Roo.get(el));
10767     },
10768
10769
10770     /**
10771     * Removes the specified element(s).
10772     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10773     * or an array of any of those.
10774     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10775     * @return {CompositeElement} this
10776     */
10777     removeElement : function(el, removeDom){
10778         if(el instanceof Array){
10779             for(var i = 0, len = el.length; i < len; i++){
10780                 this.removeElement(el[i]);
10781             }
10782             return this;
10783         }
10784         var index = typeof el == 'number' ? el : this.indexOf(el);
10785         if(index !== -1){
10786             if(removeDom){
10787                 var d = this.elements[index];
10788                 if(d.dom){
10789                     d.remove();
10790                 }else{
10791                     d.parentNode.removeChild(d);
10792                 }
10793             }
10794             this.elements.splice(index, 1);
10795         }
10796         return this;
10797     },
10798
10799     /**
10800     * Replaces the specified element with the passed element.
10801     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10802     * to replace.
10803     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10804     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10805     * @return {CompositeElement} this
10806     */
10807     replaceElement : function(el, replacement, domReplace){
10808         var index = typeof el == 'number' ? el : this.indexOf(el);
10809         if(index !== -1){
10810             if(domReplace){
10811                 this.elements[index].replaceWith(replacement);
10812             }else{
10813                 this.elements.splice(index, 1, Roo.get(replacement))
10814             }
10815         }
10816         return this;
10817     },
10818
10819     /**
10820      * Removes all elements.
10821      */
10822     clear : function(){
10823         this.elements = [];
10824     }
10825 };
10826 (function(){
10827     Roo.CompositeElement.createCall = function(proto, fnName){
10828         if(!proto[fnName]){
10829             proto[fnName] = function(){
10830                 return this.invoke(fnName, arguments);
10831             };
10832         }
10833     };
10834     for(var fnName in Roo.Element.prototype){
10835         if(typeof Roo.Element.prototype[fnName] == "function"){
10836             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10837         }
10838     };
10839 })();
10840 /*
10841  * Based on:
10842  * Ext JS Library 1.1.1
10843  * Copyright(c) 2006-2007, Ext JS, LLC.
10844  *
10845  * Originally Released Under LGPL - original licence link has changed is not relivant.
10846  *
10847  * Fork - LGPL
10848  * <script type="text/javascript">
10849  */
10850
10851 /**
10852  * @class Roo.CompositeElementLite
10853  * @extends Roo.CompositeElement
10854  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10855  <pre><code>
10856  var els = Roo.select("#some-el div.some-class");
10857  // or select directly from an existing element
10858  var el = Roo.get('some-el');
10859  el.select('div.some-class');
10860
10861  els.setWidth(100); // all elements become 100 width
10862  els.hide(true); // all elements fade out and hide
10863  // or
10864  els.setWidth(100).hide(true);
10865  </code></pre><br><br>
10866  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10867  * actions will be performed on all the elements in this collection.</b>
10868  */
10869 Roo.CompositeElementLite = function(els){
10870     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10871     this.el = new Roo.Element.Flyweight();
10872 };
10873 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10874     addElements : function(els){
10875         if(els){
10876             if(els instanceof Array){
10877                 this.elements = this.elements.concat(els);
10878             }else{
10879                 var yels = this.elements;
10880                 var index = yels.length-1;
10881                 for(var i = 0, len = els.length; i < len; i++) {
10882                     yels[++index] = els[i];
10883                 }
10884             }
10885         }
10886         return this;
10887     },
10888     invoke : function(fn, args){
10889         var els = this.elements;
10890         var el = this.el;
10891         for(var i = 0, len = els.length; i < len; i++) {
10892             el.dom = els[i];
10893                 Roo.Element.prototype[fn].apply(el, args);
10894         }
10895         return this;
10896     },
10897     /**
10898      * Returns a flyweight Element of the dom element object at the specified index
10899      * @param {Number} index
10900      * @return {Roo.Element}
10901      */
10902     item : function(index){
10903         if(!this.elements[index]){
10904             return null;
10905         }
10906         this.el.dom = this.elements[index];
10907         return this.el;
10908     },
10909
10910     // fixes scope with flyweight
10911     addListener : function(eventName, handler, scope, opt){
10912         var els = this.elements;
10913         for(var i = 0, len = els.length; i < len; i++) {
10914             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10915         }
10916         return this;
10917     },
10918
10919     /**
10920     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10921     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10922     * a reference to the dom node, use el.dom.</b>
10923     * @param {Function} fn The function to call
10924     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10925     * @return {CompositeElement} this
10926     */
10927     each : function(fn, scope){
10928         var els = this.elements;
10929         var el = this.el;
10930         for(var i = 0, len = els.length; i < len; i++){
10931             el.dom = els[i];
10932                 if(fn.call(scope || el, el, this, i) === false){
10933                 break;
10934             }
10935         }
10936         return this;
10937     },
10938
10939     indexOf : function(el){
10940         return this.elements.indexOf(Roo.getDom(el));
10941     },
10942
10943     replaceElement : function(el, replacement, domReplace){
10944         var index = typeof el == 'number' ? el : this.indexOf(el);
10945         if(index !== -1){
10946             replacement = Roo.getDom(replacement);
10947             if(domReplace){
10948                 var d = this.elements[index];
10949                 d.parentNode.insertBefore(replacement, d);
10950                 d.parentNode.removeChild(d);
10951             }
10952             this.elements.splice(index, 1, replacement);
10953         }
10954         return this;
10955     }
10956 });
10957 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10958
10959 /*
10960  * Based on:
10961  * Ext JS Library 1.1.1
10962  * Copyright(c) 2006-2007, Ext JS, LLC.
10963  *
10964  * Originally Released Under LGPL - original licence link has changed is not relivant.
10965  *
10966  * Fork - LGPL
10967  * <script type="text/javascript">
10968  */
10969
10970  
10971
10972 /**
10973  * @class Roo.data.Connection
10974  * @extends Roo.util.Observable
10975  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10976  * either to a configured URL, or to a URL specified at request time.<br><br>
10977  * <p>
10978  * Requests made by this class are asynchronous, and will return immediately. No data from
10979  * the server will be available to the statement immediately following the {@link #request} call.
10980  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10981  * <p>
10982  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10983  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10984  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10985  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10986  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10987  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10988  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10989  * standard DOM methods.
10990  * @constructor
10991  * @param {Object} config a configuration object.
10992  */
10993 Roo.data.Connection = function(config){
10994     Roo.apply(this, config);
10995     this.addEvents({
10996         /**
10997          * @event beforerequest
10998          * Fires before a network request is made to retrieve a data object.
10999          * @param {Connection} conn This Connection object.
11000          * @param {Object} options The options config object passed to the {@link #request} method.
11001          */
11002         "beforerequest" : true,
11003         /**
11004          * @event requestcomplete
11005          * Fires if the request was successfully completed.
11006          * @param {Connection} conn This Connection object.
11007          * @param {Object} response The XHR object containing the response data.
11008          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11009          * @param {Object} options The options config object passed to the {@link #request} method.
11010          */
11011         "requestcomplete" : true,
11012         /**
11013          * @event requestexception
11014          * Fires if an error HTTP status was returned from the server.
11015          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11016          * @param {Connection} conn This Connection object.
11017          * @param {Object} response The XHR object containing the response data.
11018          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11019          * @param {Object} options The options config object passed to the {@link #request} method.
11020          */
11021         "requestexception" : true
11022     });
11023     Roo.data.Connection.superclass.constructor.call(this);
11024 };
11025
11026 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11027     /**
11028      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11029      */
11030     /**
11031      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11032      * extra parameters to each request made by this object. (defaults to undefined)
11033      */
11034     /**
11035      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11036      *  to each request made by this object. (defaults to undefined)
11037      */
11038     /**
11039      * @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)
11040      */
11041     /**
11042      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11043      */
11044     timeout : 30000,
11045     /**
11046      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11047      * @type Boolean
11048      */
11049     autoAbort:false,
11050
11051     /**
11052      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11053      * @type Boolean
11054      */
11055     disableCaching: true,
11056
11057     /**
11058      * Sends an HTTP request to a remote server.
11059      * @param {Object} options An object which may contain the following properties:<ul>
11060      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11061      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11062      * request, a url encoded string or a function to call to get either.</li>
11063      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11064      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11065      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11066      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11067      * <li>options {Object} The parameter to the request call.</li>
11068      * <li>success {Boolean} True if the request succeeded.</li>
11069      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11070      * </ul></li>
11071      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11072      * The callback is passed the following parameters:<ul>
11073      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11074      * <li>options {Object} The parameter to the request call.</li>
11075      * </ul></li>
11076      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11077      * The callback is passed the following parameters:<ul>
11078      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11079      * <li>options {Object} The parameter to the request call.</li>
11080      * </ul></li>
11081      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11082      * for the callback function. Defaults to the browser window.</li>
11083      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11084      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11085      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11086      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11087      * params for the post data. Any params will be appended to the URL.</li>
11088      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11089      * </ul>
11090      * @return {Number} transactionId
11091      */
11092     request : function(o){
11093         if(this.fireEvent("beforerequest", this, o) !== false){
11094             var p = o.params;
11095
11096             if(typeof p == "function"){
11097                 p = p.call(o.scope||window, o);
11098             }
11099             if(typeof p == "object"){
11100                 p = Roo.urlEncode(o.params);
11101             }
11102             if(this.extraParams){
11103                 var extras = Roo.urlEncode(this.extraParams);
11104                 p = p ? (p + '&' + extras) : extras;
11105             }
11106
11107             var url = o.url || this.url;
11108             if(typeof url == 'function'){
11109                 url = url.call(o.scope||window, o);
11110             }
11111
11112             if(o.form){
11113                 var form = Roo.getDom(o.form);
11114                 url = url || form.action;
11115
11116                 var enctype = form.getAttribute("enctype");
11117                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11118                     return this.doFormUpload(o, p, url);
11119                 }
11120                 var f = Roo.lib.Ajax.serializeForm(form);
11121                 p = p ? (p + '&' + f) : f;
11122             }
11123
11124             var hs = o.headers;
11125             if(this.defaultHeaders){
11126                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11127                 if(!o.headers){
11128                     o.headers = hs;
11129                 }
11130             }
11131
11132             var cb = {
11133                 success: this.handleResponse,
11134                 failure: this.handleFailure,
11135                 scope: this,
11136                 argument: {options: o},
11137                 timeout : this.timeout
11138             };
11139
11140             var method = o.method||this.method||(p ? "POST" : "GET");
11141
11142             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11143                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11144             }
11145
11146             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11147                 if(o.autoAbort){
11148                     this.abort();
11149                 }
11150             }else if(this.autoAbort !== false){
11151                 this.abort();
11152             }
11153
11154             if((method == 'GET' && p) || o.xmlData){
11155                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11156                 p = '';
11157             }
11158             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11159             return this.transId;
11160         }else{
11161             Roo.callback(o.callback, o.scope, [o, null, null]);
11162             return null;
11163         }
11164     },
11165
11166     /**
11167      * Determine whether this object has a request outstanding.
11168      * @param {Number} transactionId (Optional) defaults to the last transaction
11169      * @return {Boolean} True if there is an outstanding request.
11170      */
11171     isLoading : function(transId){
11172         if(transId){
11173             return Roo.lib.Ajax.isCallInProgress(transId);
11174         }else{
11175             return this.transId ? true : false;
11176         }
11177     },
11178
11179     /**
11180      * Aborts any outstanding request.
11181      * @param {Number} transactionId (Optional) defaults to the last transaction
11182      */
11183     abort : function(transId){
11184         if(transId || this.isLoading()){
11185             Roo.lib.Ajax.abort(transId || this.transId);
11186         }
11187     },
11188
11189     // private
11190     handleResponse : function(response){
11191         this.transId = false;
11192         var options = response.argument.options;
11193         response.argument = options ? options.argument : null;
11194         this.fireEvent("requestcomplete", this, response, options);
11195         Roo.callback(options.success, options.scope, [response, options]);
11196         Roo.callback(options.callback, options.scope, [options, true, response]);
11197     },
11198
11199     // private
11200     handleFailure : function(response, e){
11201         this.transId = false;
11202         var options = response.argument.options;
11203         response.argument = options ? options.argument : null;
11204         this.fireEvent("requestexception", this, response, options, e);
11205         Roo.callback(options.failure, options.scope, [response, options]);
11206         Roo.callback(options.callback, options.scope, [options, false, response]);
11207     },
11208
11209     // private
11210     doFormUpload : function(o, ps, url){
11211         var id = Roo.id();
11212         var frame = document.createElement('iframe');
11213         frame.id = id;
11214         frame.name = id;
11215         frame.className = 'x-hidden';
11216         if(Roo.isIE){
11217             frame.src = Roo.SSL_SECURE_URL;
11218         }
11219         document.body.appendChild(frame);
11220
11221         if(Roo.isIE){
11222            document.frames[id].name = id;
11223         }
11224
11225         var form = Roo.getDom(o.form);
11226         form.target = id;
11227         form.method = 'POST';
11228         form.enctype = form.encoding = 'multipart/form-data';
11229         if(url){
11230             form.action = url;
11231         }
11232
11233         var hiddens, hd;
11234         if(ps){ // add dynamic params
11235             hiddens = [];
11236             ps = Roo.urlDecode(ps, false);
11237             for(var k in ps){
11238                 if(ps.hasOwnProperty(k)){
11239                     hd = document.createElement('input');
11240                     hd.type = 'hidden';
11241                     hd.name = k;
11242                     hd.value = ps[k];
11243                     form.appendChild(hd);
11244                     hiddens.push(hd);
11245                 }
11246             }
11247         }
11248
11249         function cb(){
11250             var r = {  // bogus response object
11251                 responseText : '',
11252                 responseXML : null
11253             };
11254
11255             r.argument = o ? o.argument : null;
11256
11257             try { //
11258                 var doc;
11259                 if(Roo.isIE){
11260                     doc = frame.contentWindow.document;
11261                 }else {
11262                     doc = (frame.contentDocument || window.frames[id].document);
11263                 }
11264                 if(doc && doc.body){
11265                     r.responseText = doc.body.innerHTML;
11266                 }
11267                 if(doc && doc.XMLDocument){
11268                     r.responseXML = doc.XMLDocument;
11269                 }else {
11270                     r.responseXML = doc;
11271                 }
11272             }
11273             catch(e) {
11274                 // ignore
11275             }
11276
11277             Roo.EventManager.removeListener(frame, 'load', cb, this);
11278
11279             this.fireEvent("requestcomplete", this, r, o);
11280             Roo.callback(o.success, o.scope, [r, o]);
11281             Roo.callback(o.callback, o.scope, [o, true, r]);
11282
11283             setTimeout(function(){document.body.removeChild(frame);}, 100);
11284         }
11285
11286         Roo.EventManager.on(frame, 'load', cb, this);
11287         form.submit();
11288
11289         if(hiddens){ // remove dynamic params
11290             for(var i = 0, len = hiddens.length; i < len; i++){
11291                 form.removeChild(hiddens[i]);
11292             }
11293         }
11294     }
11295 });
11296
11297 /**
11298  * @class Roo.Ajax
11299  * @extends Roo.data.Connection
11300  * Global Ajax request class.
11301  *
11302  * @singleton
11303  */
11304 Roo.Ajax = new Roo.data.Connection({
11305     // fix up the docs
11306    /**
11307      * @cfg {String} url @hide
11308      */
11309     /**
11310      * @cfg {Object} extraParams @hide
11311      */
11312     /**
11313      * @cfg {Object} defaultHeaders @hide
11314      */
11315     /**
11316      * @cfg {String} method (Optional) @hide
11317      */
11318     /**
11319      * @cfg {Number} timeout (Optional) @hide
11320      */
11321     /**
11322      * @cfg {Boolean} autoAbort (Optional) @hide
11323      */
11324
11325     /**
11326      * @cfg {Boolean} disableCaching (Optional) @hide
11327      */
11328
11329     /**
11330      * @property  disableCaching
11331      * True to add a unique cache-buster param to GET requests. (defaults to true)
11332      * @type Boolean
11333      */
11334     /**
11335      * @property  url
11336      * The default URL to be used for requests to the server. (defaults to undefined)
11337      * @type String
11338      */
11339     /**
11340      * @property  extraParams
11341      * An object containing properties which are used as
11342      * extra parameters to each request made by this object. (defaults to undefined)
11343      * @type Object
11344      */
11345     /**
11346      * @property  defaultHeaders
11347      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11348      * @type Object
11349      */
11350     /**
11351      * @property  method
11352      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11353      * @type String
11354      */
11355     /**
11356      * @property  timeout
11357      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11358      * @type Number
11359      */
11360
11361     /**
11362      * @property  autoAbort
11363      * Whether a new request should abort any pending requests. (defaults to false)
11364      * @type Boolean
11365      */
11366     autoAbort : false,
11367
11368     /**
11369      * Serialize the passed form into a url encoded string
11370      * @param {String/HTMLElement} form
11371      * @return {String}
11372      */
11373     serializeForm : function(form){
11374         return Roo.lib.Ajax.serializeForm(form);
11375     }
11376 });/*
11377  * Based on:
11378  * Ext JS Library 1.1.1
11379  * Copyright(c) 2006-2007, Ext JS, LLC.
11380  *
11381  * Originally Released Under LGPL - original licence link has changed is not relivant.
11382  *
11383  * Fork - LGPL
11384  * <script type="text/javascript">
11385  */
11386  
11387 /**
11388  * @class Roo.Ajax
11389  * @extends Roo.data.Connection
11390  * Global Ajax request class.
11391  *
11392  * @instanceOf  Roo.data.Connection
11393  */
11394 Roo.Ajax = new Roo.data.Connection({
11395     // fix up the docs
11396     
11397     /**
11398      * fix up scoping
11399      * @scope Roo.Ajax
11400      */
11401     
11402    /**
11403      * @cfg {String} url @hide
11404      */
11405     /**
11406      * @cfg {Object} extraParams @hide
11407      */
11408     /**
11409      * @cfg {Object} defaultHeaders @hide
11410      */
11411     /**
11412      * @cfg {String} method (Optional) @hide
11413      */
11414     /**
11415      * @cfg {Number} timeout (Optional) @hide
11416      */
11417     /**
11418      * @cfg {Boolean} autoAbort (Optional) @hide
11419      */
11420
11421     /**
11422      * @cfg {Boolean} disableCaching (Optional) @hide
11423      */
11424
11425     /**
11426      * @property  disableCaching
11427      * True to add a unique cache-buster param to GET requests. (defaults to true)
11428      * @type Boolean
11429      */
11430     /**
11431      * @property  url
11432      * The default URL to be used for requests to the server. (defaults to undefined)
11433      * @type String
11434      */
11435     /**
11436      * @property  extraParams
11437      * An object containing properties which are used as
11438      * extra parameters to each request made by this object. (defaults to undefined)
11439      * @type Object
11440      */
11441     /**
11442      * @property  defaultHeaders
11443      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11444      * @type Object
11445      */
11446     /**
11447      * @property  method
11448      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11449      * @type String
11450      */
11451     /**
11452      * @property  timeout
11453      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11454      * @type Number
11455      */
11456
11457     /**
11458      * @property  autoAbort
11459      * Whether a new request should abort any pending requests. (defaults to false)
11460      * @type Boolean
11461      */
11462     autoAbort : false,
11463
11464     /**
11465      * Serialize the passed form into a url encoded string
11466      * @param {String/HTMLElement} form
11467      * @return {String}
11468      */
11469     serializeForm : function(form){
11470         return Roo.lib.Ajax.serializeForm(form);
11471     }
11472 });/*
11473  * Based on:
11474  * Ext JS Library 1.1.1
11475  * Copyright(c) 2006-2007, Ext JS, LLC.
11476  *
11477  * Originally Released Under LGPL - original licence link has changed is not relivant.
11478  *
11479  * Fork - LGPL
11480  * <script type="text/javascript">
11481  */
11482
11483  
11484 /**
11485  * @class Roo.UpdateManager
11486  * @extends Roo.util.Observable
11487  * Provides AJAX-style update for Element object.<br><br>
11488  * Usage:<br>
11489  * <pre><code>
11490  * // Get it from a Roo.Element object
11491  * var el = Roo.get("foo");
11492  * var mgr = el.getUpdateManager();
11493  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11494  * ...
11495  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11496  * <br>
11497  * // or directly (returns the same UpdateManager instance)
11498  * var mgr = new Roo.UpdateManager("myElementId");
11499  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11500  * mgr.on("update", myFcnNeedsToKnow);
11501  * <br>
11502    // short handed call directly from the element object
11503    Roo.get("foo").load({
11504         url: "bar.php",
11505         scripts:true,
11506         params: "for=bar",
11507         text: "Loading Foo..."
11508    });
11509  * </code></pre>
11510  * @constructor
11511  * Create new UpdateManager directly.
11512  * @param {String/HTMLElement/Roo.Element} el The element to update
11513  * @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).
11514  */
11515 Roo.UpdateManager = function(el, forceNew){
11516     el = Roo.get(el);
11517     if(!forceNew && el.updateManager){
11518         return el.updateManager;
11519     }
11520     /**
11521      * The Element object
11522      * @type Roo.Element
11523      */
11524     this.el = el;
11525     /**
11526      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11527      * @type String
11528      */
11529     this.defaultUrl = null;
11530
11531     this.addEvents({
11532         /**
11533          * @event beforeupdate
11534          * Fired before an update is made, return false from your handler and the update is cancelled.
11535          * @param {Roo.Element} el
11536          * @param {String/Object/Function} url
11537          * @param {String/Object} params
11538          */
11539         "beforeupdate": true,
11540         /**
11541          * @event update
11542          * Fired after successful update is made.
11543          * @param {Roo.Element} el
11544          * @param {Object} oResponseObject The response Object
11545          */
11546         "update": true,
11547         /**
11548          * @event failure
11549          * Fired on update failure.
11550          * @param {Roo.Element} el
11551          * @param {Object} oResponseObject The response Object
11552          */
11553         "failure": true
11554     });
11555     var d = Roo.UpdateManager.defaults;
11556     /**
11557      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11558      * @type String
11559      */
11560     this.sslBlankUrl = d.sslBlankUrl;
11561     /**
11562      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11563      * @type Boolean
11564      */
11565     this.disableCaching = d.disableCaching;
11566     /**
11567      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11568      * @type String
11569      */
11570     this.indicatorText = d.indicatorText;
11571     /**
11572      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11573      * @type String
11574      */
11575     this.showLoadIndicator = d.showLoadIndicator;
11576     /**
11577      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11578      * @type Number
11579      */
11580     this.timeout = d.timeout;
11581
11582     /**
11583      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11584      * @type Boolean
11585      */
11586     this.loadScripts = d.loadScripts;
11587
11588     /**
11589      * Transaction object of current executing transaction
11590      */
11591     this.transaction = null;
11592
11593     /**
11594      * @private
11595      */
11596     this.autoRefreshProcId = null;
11597     /**
11598      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11599      * @type Function
11600      */
11601     this.refreshDelegate = this.refresh.createDelegate(this);
11602     /**
11603      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11604      * @type Function
11605      */
11606     this.updateDelegate = this.update.createDelegate(this);
11607     /**
11608      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11609      * @type Function
11610      */
11611     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11612     /**
11613      * @private
11614      */
11615     this.successDelegate = this.processSuccess.createDelegate(this);
11616     /**
11617      * @private
11618      */
11619     this.failureDelegate = this.processFailure.createDelegate(this);
11620
11621     if(!this.renderer){
11622      /**
11623       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11624       */
11625     this.renderer = new Roo.UpdateManager.BasicRenderer();
11626     }
11627     
11628     Roo.UpdateManager.superclass.constructor.call(this);
11629 };
11630
11631 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11632     /**
11633      * Get the Element this UpdateManager is bound to
11634      * @return {Roo.Element} The element
11635      */
11636     getEl : function(){
11637         return this.el;
11638     },
11639     /**
11640      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11641      * @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:
11642 <pre><code>
11643 um.update({<br/>
11644     url: "your-url.php",<br/>
11645     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11646     callback: yourFunction,<br/>
11647     scope: yourObject, //(optional scope)  <br/>
11648     discardUrl: false, <br/>
11649     nocache: false,<br/>
11650     text: "Loading...",<br/>
11651     timeout: 30,<br/>
11652     scripts: false<br/>
11653 });
11654 </code></pre>
11655      * The only required property is url. The optional properties nocache, text and scripts
11656      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11657      * @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}
11658      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11659      * @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.
11660      */
11661     update : function(url, params, callback, discardUrl){
11662         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11663             var method = this.method, cfg;
11664             if(typeof url == "object"){ // must be config object
11665                 cfg = url;
11666                 url = cfg.url;
11667                 params = params || cfg.params;
11668                 callback = callback || cfg.callback;
11669                 discardUrl = discardUrl || cfg.discardUrl;
11670                 if(callback && cfg.scope){
11671                     callback = callback.createDelegate(cfg.scope);
11672                 }
11673                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11674                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11675                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11676                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11677                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11678             }
11679             this.showLoading();
11680             if(!discardUrl){
11681                 this.defaultUrl = url;
11682             }
11683             if(typeof url == "function"){
11684                 url = url.call(this);
11685             }
11686
11687             method = method || (params ? "POST" : "GET");
11688             if(method == "GET"){
11689                 url = this.prepareUrl(url);
11690             }
11691
11692             var o = Roo.apply(cfg ||{}, {
11693                 url : url,
11694                 params: params,
11695                 success: this.successDelegate,
11696                 failure: this.failureDelegate,
11697                 callback: undefined,
11698                 timeout: (this.timeout*1000),
11699                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11700             });
11701
11702             this.transaction = Roo.Ajax.request(o);
11703         }
11704     },
11705
11706     /**
11707      * 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.
11708      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11709      * @param {String/HTMLElement} form The form Id or form element
11710      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11711      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11712      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11713      */
11714     formUpdate : function(form, url, reset, callback){
11715         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11716             if(typeof url == "function"){
11717                 url = url.call(this);
11718             }
11719             form = Roo.getDom(form);
11720             this.transaction = Roo.Ajax.request({
11721                 form: form,
11722                 url:url,
11723                 success: this.successDelegate,
11724                 failure: this.failureDelegate,
11725                 timeout: (this.timeout*1000),
11726                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11727             });
11728             this.showLoading.defer(1, this);
11729         }
11730     },
11731
11732     /**
11733      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11734      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11735      */
11736     refresh : function(callback){
11737         if(this.defaultUrl == null){
11738             return;
11739         }
11740         this.update(this.defaultUrl, null, callback, true);
11741     },
11742
11743     /**
11744      * Set this element to auto refresh.
11745      * @param {Number} interval How often to update (in seconds).
11746      * @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)
11747      * @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}
11748      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11749      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11750      */
11751     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11752         if(refreshNow){
11753             this.update(url || this.defaultUrl, params, callback, true);
11754         }
11755         if(this.autoRefreshProcId){
11756             clearInterval(this.autoRefreshProcId);
11757         }
11758         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11759     },
11760
11761     /**
11762      * Stop auto refresh on this element.
11763      */
11764      stopAutoRefresh : function(){
11765         if(this.autoRefreshProcId){
11766             clearInterval(this.autoRefreshProcId);
11767             delete this.autoRefreshProcId;
11768         }
11769     },
11770
11771     isAutoRefreshing : function(){
11772        return this.autoRefreshProcId ? true : false;
11773     },
11774     /**
11775      * Called to update the element to "Loading" state. Override to perform custom action.
11776      */
11777     showLoading : function(){
11778         if(this.showLoadIndicator){
11779             this.el.update(this.indicatorText);
11780         }
11781     },
11782
11783     /**
11784      * Adds unique parameter to query string if disableCaching = true
11785      * @private
11786      */
11787     prepareUrl : function(url){
11788         if(this.disableCaching){
11789             var append = "_dc=" + (new Date().getTime());
11790             if(url.indexOf("?") !== -1){
11791                 url += "&" + append;
11792             }else{
11793                 url += "?" + append;
11794             }
11795         }
11796         return url;
11797     },
11798
11799     /**
11800      * @private
11801      */
11802     processSuccess : function(response){
11803         this.transaction = null;
11804         if(response.argument.form && response.argument.reset){
11805             try{ // put in try/catch since some older FF releases had problems with this
11806                 response.argument.form.reset();
11807             }catch(e){}
11808         }
11809         if(this.loadScripts){
11810             this.renderer.render(this.el, response, this,
11811                 this.updateComplete.createDelegate(this, [response]));
11812         }else{
11813             this.renderer.render(this.el, response, this);
11814             this.updateComplete(response);
11815         }
11816     },
11817
11818     updateComplete : function(response){
11819         this.fireEvent("update", this.el, response);
11820         if(typeof response.argument.callback == "function"){
11821             response.argument.callback(this.el, true, response);
11822         }
11823     },
11824
11825     /**
11826      * @private
11827      */
11828     processFailure : function(response){
11829         this.transaction = null;
11830         this.fireEvent("failure", this.el, response);
11831         if(typeof response.argument.callback == "function"){
11832             response.argument.callback(this.el, false, response);
11833         }
11834     },
11835
11836     /**
11837      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11838      * @param {Object} renderer The object implementing the render() method
11839      */
11840     setRenderer : function(renderer){
11841         this.renderer = renderer;
11842     },
11843
11844     getRenderer : function(){
11845        return this.renderer;
11846     },
11847
11848     /**
11849      * Set the defaultUrl used for updates
11850      * @param {String/Function} defaultUrl The url or a function to call to get the url
11851      */
11852     setDefaultUrl : function(defaultUrl){
11853         this.defaultUrl = defaultUrl;
11854     },
11855
11856     /**
11857      * Aborts the executing transaction
11858      */
11859     abort : function(){
11860         if(this.transaction){
11861             Roo.Ajax.abort(this.transaction);
11862         }
11863     },
11864
11865     /**
11866      * Returns true if an update is in progress
11867      * @return {Boolean}
11868      */
11869     isUpdating : function(){
11870         if(this.transaction){
11871             return Roo.Ajax.isLoading(this.transaction);
11872         }
11873         return false;
11874     }
11875 });
11876
11877 /**
11878  * @class Roo.UpdateManager.defaults
11879  * @static (not really - but it helps the doc tool)
11880  * The defaults collection enables customizing the default properties of UpdateManager
11881  */
11882    Roo.UpdateManager.defaults = {
11883        /**
11884          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11885          * @type Number
11886          */
11887          timeout : 30,
11888
11889          /**
11890          * True to process scripts by default (Defaults to false).
11891          * @type Boolean
11892          */
11893         loadScripts : false,
11894
11895         /**
11896         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11897         * @type String
11898         */
11899         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11900         /**
11901          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11902          * @type Boolean
11903          */
11904         disableCaching : false,
11905         /**
11906          * Whether to show indicatorText when loading (Defaults to true).
11907          * @type Boolean
11908          */
11909         showLoadIndicator : true,
11910         /**
11911          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11912          * @type String
11913          */
11914         indicatorText : '<div class="loading-indicator">Loading...</div>'
11915    };
11916
11917 /**
11918  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11919  *Usage:
11920  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11921  * @param {String/HTMLElement/Roo.Element} el The element to update
11922  * @param {String} url The url
11923  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11924  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11925  * @static
11926  * @deprecated
11927  * @member Roo.UpdateManager
11928  */
11929 Roo.UpdateManager.updateElement = function(el, url, params, options){
11930     var um = Roo.get(el, true).getUpdateManager();
11931     Roo.apply(um, options);
11932     um.update(url, params, options ? options.callback : null);
11933 };
11934 // alias for backwards compat
11935 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11936 /**
11937  * @class Roo.UpdateManager.BasicRenderer
11938  * Default Content renderer. Updates the elements innerHTML with the responseText.
11939  */
11940 Roo.UpdateManager.BasicRenderer = function(){};
11941
11942 Roo.UpdateManager.BasicRenderer.prototype = {
11943     /**
11944      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11945      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11946      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11947      * @param {Roo.Element} el The element being rendered
11948      * @param {Object} response The YUI Connect response object
11949      * @param {UpdateManager} updateManager The calling update manager
11950      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11951      */
11952      render : function(el, response, updateManager, callback){
11953         el.update(response.responseText, updateManager.loadScripts, callback);
11954     }
11955 };
11956 /*
11957  * Based on:
11958  * Ext JS Library 1.1.1
11959  * Copyright(c) 2006-2007, Ext JS, LLC.
11960  *
11961  * Originally Released Under LGPL - original licence link has changed is not relivant.
11962  *
11963  * Fork - LGPL
11964  * <script type="text/javascript">
11965  */
11966
11967 /**
11968  * @class Roo.util.DelayedTask
11969  * Provides a convenient method of performing setTimeout where a new
11970  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11971  * You can use this class to buffer
11972  * the keypress events for a certain number of milliseconds, and perform only if they stop
11973  * for that amount of time.
11974  * @constructor The parameters to this constructor serve as defaults and are not required.
11975  * @param {Function} fn (optional) The default function to timeout
11976  * @param {Object} scope (optional) The default scope of that timeout
11977  * @param {Array} args (optional) The default Array of arguments
11978  */
11979 Roo.util.DelayedTask = function(fn, scope, args){
11980     var id = null, d, t;
11981
11982     var call = function(){
11983         var now = new Date().getTime();
11984         if(now - t >= d){
11985             clearInterval(id);
11986             id = null;
11987             fn.apply(scope, args || []);
11988         }
11989     };
11990     /**
11991      * Cancels any pending timeout and queues a new one
11992      * @param {Number} delay The milliseconds to delay
11993      * @param {Function} newFn (optional) Overrides function passed to constructor
11994      * @param {Object} newScope (optional) Overrides scope passed to constructor
11995      * @param {Array} newArgs (optional) Overrides args passed to constructor
11996      */
11997     this.delay = function(delay, newFn, newScope, newArgs){
11998         if(id && delay != d){
11999             this.cancel();
12000         }
12001         d = delay;
12002         t = new Date().getTime();
12003         fn = newFn || fn;
12004         scope = newScope || scope;
12005         args = newArgs || args;
12006         if(!id){
12007             id = setInterval(call, d);
12008         }
12009     };
12010
12011     /**
12012      * Cancel the last queued timeout
12013      */
12014     this.cancel = function(){
12015         if(id){
12016             clearInterval(id);
12017             id = null;
12018         }
12019     };
12020 };/*
12021  * Based on:
12022  * Ext JS Library 1.1.1
12023  * Copyright(c) 2006-2007, Ext JS, LLC.
12024  *
12025  * Originally Released Under LGPL - original licence link has changed is not relivant.
12026  *
12027  * Fork - LGPL
12028  * <script type="text/javascript">
12029  */
12030  
12031  
12032 Roo.util.TaskRunner = function(interval){
12033     interval = interval || 10;
12034     var tasks = [], removeQueue = [];
12035     var id = 0;
12036     var running = false;
12037
12038     var stopThread = function(){
12039         running = false;
12040         clearInterval(id);
12041         id = 0;
12042     };
12043
12044     var startThread = function(){
12045         if(!running){
12046             running = true;
12047             id = setInterval(runTasks, interval);
12048         }
12049     };
12050
12051     var removeTask = function(task){
12052         removeQueue.push(task);
12053         if(task.onStop){
12054             task.onStop();
12055         }
12056     };
12057
12058     var runTasks = function(){
12059         if(removeQueue.length > 0){
12060             for(var i = 0, len = removeQueue.length; i < len; i++){
12061                 tasks.remove(removeQueue[i]);
12062             }
12063             removeQueue = [];
12064             if(tasks.length < 1){
12065                 stopThread();
12066                 return;
12067             }
12068         }
12069         var now = new Date().getTime();
12070         for(var i = 0, len = tasks.length; i < len; ++i){
12071             var t = tasks[i];
12072             var itime = now - t.taskRunTime;
12073             if(t.interval <= itime){
12074                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12075                 t.taskRunTime = now;
12076                 if(rt === false || t.taskRunCount === t.repeat){
12077                     removeTask(t);
12078                     return;
12079                 }
12080             }
12081             if(t.duration && t.duration <= (now - t.taskStartTime)){
12082                 removeTask(t);
12083             }
12084         }
12085     };
12086
12087     /**
12088      * Queues a new task.
12089      * @param {Object} task
12090      */
12091     this.start = function(task){
12092         tasks.push(task);
12093         task.taskStartTime = new Date().getTime();
12094         task.taskRunTime = 0;
12095         task.taskRunCount = 0;
12096         startThread();
12097         return task;
12098     };
12099
12100     this.stop = function(task){
12101         removeTask(task);
12102         return task;
12103     };
12104
12105     this.stopAll = function(){
12106         stopThread();
12107         for(var i = 0, len = tasks.length; i < len; i++){
12108             if(tasks[i].onStop){
12109                 tasks[i].onStop();
12110             }
12111         }
12112         tasks = [];
12113         removeQueue = [];
12114     };
12115 };
12116
12117 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127
12128  
12129 /**
12130  * @class Roo.util.MixedCollection
12131  * @extends Roo.util.Observable
12132  * A Collection class that maintains both numeric indexes and keys and exposes events.
12133  * @constructor
12134  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12135  * collection (defaults to false)
12136  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12137  * and return the key value for that item.  This is used when available to look up the key on items that
12138  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12139  * equivalent to providing an implementation for the {@link #getKey} method.
12140  */
12141 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12142     this.items = [];
12143     this.map = {};
12144     this.keys = [];
12145     this.length = 0;
12146     this.addEvents({
12147         /**
12148          * @event clear
12149          * Fires when the collection is cleared.
12150          */
12151         "clear" : true,
12152         /**
12153          * @event add
12154          * Fires when an item is added to the collection.
12155          * @param {Number} index The index at which the item was added.
12156          * @param {Object} o The item added.
12157          * @param {String} key The key associated with the added item.
12158          */
12159         "add" : true,
12160         /**
12161          * @event replace
12162          * Fires when an item is replaced in the collection.
12163          * @param {String} key he key associated with the new added.
12164          * @param {Object} old The item being replaced.
12165          * @param {Object} new The new item.
12166          */
12167         "replace" : true,
12168         /**
12169          * @event remove
12170          * Fires when an item is removed from the collection.
12171          * @param {Object} o The item being removed.
12172          * @param {String} key (optional) The key associated with the removed item.
12173          */
12174         "remove" : true,
12175         "sort" : true
12176     });
12177     this.allowFunctions = allowFunctions === true;
12178     if(keyFn){
12179         this.getKey = keyFn;
12180     }
12181     Roo.util.MixedCollection.superclass.constructor.call(this);
12182 };
12183
12184 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12185     allowFunctions : false,
12186     
12187 /**
12188  * Adds an item to the collection.
12189  * @param {String} key The key to associate with the item
12190  * @param {Object} o The item to add.
12191  * @return {Object} The item added.
12192  */
12193     add : function(key, o){
12194         if(arguments.length == 1){
12195             o = arguments[0];
12196             key = this.getKey(o);
12197         }
12198         if(typeof key == "undefined" || key === null){
12199             this.length++;
12200             this.items.push(o);
12201             this.keys.push(null);
12202         }else{
12203             var old = this.map[key];
12204             if(old){
12205                 return this.replace(key, o);
12206             }
12207             this.length++;
12208             this.items.push(o);
12209             this.map[key] = o;
12210             this.keys.push(key);
12211         }
12212         this.fireEvent("add", this.length-1, o, key);
12213         return o;
12214     },
12215    
12216 /**
12217   * MixedCollection has a generic way to fetch keys if you implement getKey.
12218 <pre><code>
12219 // normal way
12220 var mc = new Roo.util.MixedCollection();
12221 mc.add(someEl.dom.id, someEl);
12222 mc.add(otherEl.dom.id, otherEl);
12223 //and so on
12224
12225 // using getKey
12226 var mc = new Roo.util.MixedCollection();
12227 mc.getKey = function(el){
12228    return el.dom.id;
12229 };
12230 mc.add(someEl);
12231 mc.add(otherEl);
12232
12233 // or via the constructor
12234 var mc = new Roo.util.MixedCollection(false, function(el){
12235    return el.dom.id;
12236 });
12237 mc.add(someEl);
12238 mc.add(otherEl);
12239 </code></pre>
12240  * @param o {Object} The item for which to find the key.
12241  * @return {Object} The key for the passed item.
12242  */
12243     getKey : function(o){
12244          return o.id; 
12245     },
12246    
12247 /**
12248  * Replaces an item in the collection.
12249  * @param {String} key The key associated with the item to replace, or the item to replace.
12250  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12251  * @return {Object}  The new item.
12252  */
12253     replace : function(key, o){
12254         if(arguments.length == 1){
12255             o = arguments[0];
12256             key = this.getKey(o);
12257         }
12258         var old = this.item(key);
12259         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12260              return this.add(key, o);
12261         }
12262         var index = this.indexOfKey(key);
12263         this.items[index] = o;
12264         this.map[key] = o;
12265         this.fireEvent("replace", key, old, o);
12266         return o;
12267     },
12268    
12269 /**
12270  * Adds all elements of an Array or an Object to the collection.
12271  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12272  * an Array of values, each of which are added to the collection.
12273  */
12274     addAll : function(objs){
12275         if(arguments.length > 1 || objs instanceof Array){
12276             var args = arguments.length > 1 ? arguments : objs;
12277             for(var i = 0, len = args.length; i < len; i++){
12278                 this.add(args[i]);
12279             }
12280         }else{
12281             for(var key in objs){
12282                 if(this.allowFunctions || typeof objs[key] != "function"){
12283                     this.add(key, objs[key]);
12284                 }
12285             }
12286         }
12287     },
12288    
12289 /**
12290  * Executes the specified function once for every item in the collection, passing each
12291  * item as the first and only parameter. returning false from the function will stop the iteration.
12292  * @param {Function} fn The function to execute for each item.
12293  * @param {Object} scope (optional) The scope in which to execute the function.
12294  */
12295     each : function(fn, scope){
12296         var items = [].concat(this.items); // each safe for removal
12297         for(var i = 0, len = items.length; i < len; i++){
12298             if(fn.call(scope || items[i], items[i], i, len) === false){
12299                 break;
12300             }
12301         }
12302     },
12303    
12304 /**
12305  * Executes the specified function once for every key in the collection, passing each
12306  * key, and its associated item as the first two parameters.
12307  * @param {Function} fn The function to execute for each item.
12308  * @param {Object} scope (optional) The scope in which to execute the function.
12309  */
12310     eachKey : function(fn, scope){
12311         for(var i = 0, len = this.keys.length; i < len; i++){
12312             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12313         }
12314     },
12315    
12316 /**
12317  * Returns the first item in the collection which elicits a true return value from the
12318  * passed selection function.
12319  * @param {Function} fn The selection function to execute for each item.
12320  * @param {Object} scope (optional) The scope in which to execute the function.
12321  * @return {Object} The first item in the collection which returned true from the selection function.
12322  */
12323     find : function(fn, scope){
12324         for(var i = 0, len = this.items.length; i < len; i++){
12325             if(fn.call(scope || window, this.items[i], this.keys[i])){
12326                 return this.items[i];
12327             }
12328         }
12329         return null;
12330     },
12331    
12332 /**
12333  * Inserts an item at the specified index in the collection.
12334  * @param {Number} index The index to insert the item at.
12335  * @param {String} key The key to associate with the new item, or the item itself.
12336  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12337  * @return {Object} The item inserted.
12338  */
12339     insert : function(index, key, o){
12340         if(arguments.length == 2){
12341             o = arguments[1];
12342             key = this.getKey(o);
12343         }
12344         if(index >= this.length){
12345             return this.add(key, o);
12346         }
12347         this.length++;
12348         this.items.splice(index, 0, o);
12349         if(typeof key != "undefined" && key != null){
12350             this.map[key] = o;
12351         }
12352         this.keys.splice(index, 0, key);
12353         this.fireEvent("add", index, o, key);
12354         return o;
12355     },
12356    
12357 /**
12358  * Removed an item from the collection.
12359  * @param {Object} o The item to remove.
12360  * @return {Object} The item removed.
12361  */
12362     remove : function(o){
12363         return this.removeAt(this.indexOf(o));
12364     },
12365    
12366 /**
12367  * Remove an item from a specified index in the collection.
12368  * @param {Number} index The index within the collection of the item to remove.
12369  */
12370     removeAt : function(index){
12371         if(index < this.length && index >= 0){
12372             this.length--;
12373             var o = this.items[index];
12374             this.items.splice(index, 1);
12375             var key = this.keys[index];
12376             if(typeof key != "undefined"){
12377                 delete this.map[key];
12378             }
12379             this.keys.splice(index, 1);
12380             this.fireEvent("remove", o, key);
12381         }
12382     },
12383    
12384 /**
12385  * Removed an item associated with the passed key fom the collection.
12386  * @param {String} key The key of the item to remove.
12387  */
12388     removeKey : function(key){
12389         return this.removeAt(this.indexOfKey(key));
12390     },
12391    
12392 /**
12393  * Returns the number of items in the collection.
12394  * @return {Number} the number of items in the collection.
12395  */
12396     getCount : function(){
12397         return this.length; 
12398     },
12399    
12400 /**
12401  * Returns index within the collection of the passed Object.
12402  * @param {Object} o The item to find the index of.
12403  * @return {Number} index of the item.
12404  */
12405     indexOf : function(o){
12406         if(!this.items.indexOf){
12407             for(var i = 0, len = this.items.length; i < len; i++){
12408                 if(this.items[i] == o) return i;
12409             }
12410             return -1;
12411         }else{
12412             return this.items.indexOf(o);
12413         }
12414     },
12415    
12416 /**
12417  * Returns index within the collection of the passed key.
12418  * @param {String} key The key to find the index of.
12419  * @return {Number} index of the key.
12420  */
12421     indexOfKey : function(key){
12422         if(!this.keys.indexOf){
12423             for(var i = 0, len = this.keys.length; i < len; i++){
12424                 if(this.keys[i] == key) return i;
12425             }
12426             return -1;
12427         }else{
12428             return this.keys.indexOf(key);
12429         }
12430     },
12431    
12432 /**
12433  * Returns the item associated with the passed key OR index. Key has priority over index.
12434  * @param {String/Number} key The key or index of the item.
12435  * @return {Object} The item associated with the passed key.
12436  */
12437     item : function(key){
12438         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12439         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12440     },
12441     
12442 /**
12443  * Returns the item at the specified index.
12444  * @param {Number} index The index of the item.
12445  * @return {Object}
12446  */
12447     itemAt : function(index){
12448         return this.items[index];
12449     },
12450     
12451 /**
12452  * Returns the item associated with the passed key.
12453  * @param {String/Number} key The key of the item.
12454  * @return {Object} The item associated with the passed key.
12455  */
12456     key : function(key){
12457         return this.map[key];
12458     },
12459    
12460 /**
12461  * Returns true if the collection contains the passed Object as an item.
12462  * @param {Object} o  The Object to look for in the collection.
12463  * @return {Boolean} True if the collection contains the Object as an item.
12464  */
12465     contains : function(o){
12466         return this.indexOf(o) != -1;
12467     },
12468    
12469 /**
12470  * Returns true if the collection contains the passed Object as a key.
12471  * @param {String} key The key to look for in the collection.
12472  * @return {Boolean} True if the collection contains the Object as a key.
12473  */
12474     containsKey : function(key){
12475         return typeof this.map[key] != "undefined";
12476     },
12477    
12478 /**
12479  * Removes all items from the collection.
12480  */
12481     clear : function(){
12482         this.length = 0;
12483         this.items = [];
12484         this.keys = [];
12485         this.map = {};
12486         this.fireEvent("clear");
12487     },
12488    
12489 /**
12490  * Returns the first item in the collection.
12491  * @return {Object} the first item in the collection..
12492  */
12493     first : function(){
12494         return this.items[0]; 
12495     },
12496    
12497 /**
12498  * Returns the last item in the collection.
12499  * @return {Object} the last item in the collection..
12500  */
12501     last : function(){
12502         return this.items[this.length-1];   
12503     },
12504     
12505     _sort : function(property, dir, fn){
12506         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12507         fn = fn || function(a, b){
12508             return a-b;
12509         };
12510         var c = [], k = this.keys, items = this.items;
12511         for(var i = 0, len = items.length; i < len; i++){
12512             c[c.length] = {key: k[i], value: items[i], index: i};
12513         }
12514         c.sort(function(a, b){
12515             var v = fn(a[property], b[property]) * dsc;
12516             if(v == 0){
12517                 v = (a.index < b.index ? -1 : 1);
12518             }
12519             return v;
12520         });
12521         for(var i = 0, len = c.length; i < len; i++){
12522             items[i] = c[i].value;
12523             k[i] = c[i].key;
12524         }
12525         this.fireEvent("sort", this);
12526     },
12527     
12528     /**
12529      * Sorts this collection with the passed comparison function
12530      * @param {String} direction (optional) "ASC" or "DESC"
12531      * @param {Function} fn (optional) comparison function
12532      */
12533     sort : function(dir, fn){
12534         this._sort("value", dir, fn);
12535     },
12536     
12537     /**
12538      * Sorts this collection by keys
12539      * @param {String} direction (optional) "ASC" or "DESC"
12540      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12541      */
12542     keySort : function(dir, fn){
12543         this._sort("key", dir, fn || function(a, b){
12544             return String(a).toUpperCase()-String(b).toUpperCase();
12545         });
12546     },
12547     
12548     /**
12549      * Returns a range of items in this collection
12550      * @param {Number} startIndex (optional) defaults to 0
12551      * @param {Number} endIndex (optional) default to the last item
12552      * @return {Array} An array of items
12553      */
12554     getRange : function(start, end){
12555         var items = this.items;
12556         if(items.length < 1){
12557             return [];
12558         }
12559         start = start || 0;
12560         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12561         var r = [];
12562         if(start <= end){
12563             for(var i = start; i <= end; i++) {
12564                     r[r.length] = items[i];
12565             }
12566         }else{
12567             for(var i = start; i >= end; i--) {
12568                     r[r.length] = items[i];
12569             }
12570         }
12571         return r;
12572     },
12573         
12574     /**
12575      * Filter the <i>objects</i> in this collection by a specific property. 
12576      * Returns a new collection that has been filtered.
12577      * @param {String} property A property on your objects
12578      * @param {String/RegExp} value Either string that the property values 
12579      * should start with or a RegExp to test against the property
12580      * @return {MixedCollection} The new filtered collection
12581      */
12582     filter : function(property, value){
12583         if(!value.exec){ // not a regex
12584             value = String(value);
12585             if(value.length == 0){
12586                 return this.clone();
12587             }
12588             value = new RegExp("^" + Roo.escapeRe(value), "i");
12589         }
12590         return this.filterBy(function(o){
12591             return o && value.test(o[property]);
12592         });
12593         },
12594     
12595     /**
12596      * Filter by a function. * Returns a new collection that has been filtered.
12597      * The passed function will be called with each 
12598      * object in the collection. If the function returns true, the value is included 
12599      * otherwise it is filtered.
12600      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12601      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12602      * @return {MixedCollection} The new filtered collection
12603      */
12604     filterBy : function(fn, scope){
12605         var r = new Roo.util.MixedCollection();
12606         r.getKey = this.getKey;
12607         var k = this.keys, it = this.items;
12608         for(var i = 0, len = it.length; i < len; i++){
12609             if(fn.call(scope||this, it[i], k[i])){
12610                                 r.add(k[i], it[i]);
12611                         }
12612         }
12613         return r;
12614     },
12615     
12616     /**
12617      * Creates a duplicate of this collection
12618      * @return {MixedCollection}
12619      */
12620     clone : function(){
12621         var r = new Roo.util.MixedCollection();
12622         var k = this.keys, it = this.items;
12623         for(var i = 0, len = it.length; i < len; i++){
12624             r.add(k[i], it[i]);
12625         }
12626         r.getKey = this.getKey;
12627         return r;
12628     }
12629 });
12630 /**
12631  * Returns the item associated with the passed key or index.
12632  * @method
12633  * @param {String/Number} key The key or index of the item.
12634  * @return {Object} The item associated with the passed key.
12635  */
12636 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12637  * Based on:
12638  * Ext JS Library 1.1.1
12639  * Copyright(c) 2006-2007, Ext JS, LLC.
12640  *
12641  * Originally Released Under LGPL - original licence link has changed is not relivant.
12642  *
12643  * Fork - LGPL
12644  * <script type="text/javascript">
12645  */
12646 /**
12647  * @class Roo.util.JSON
12648  * Modified version of Douglas Crockford"s json.js that doesn"t
12649  * mess with the Object prototype 
12650  * http://www.json.org/js.html
12651  * @singleton
12652  */
12653 Roo.util.JSON = new (function(){
12654     var useHasOwn = {}.hasOwnProperty ? true : false;
12655     
12656     // crashes Safari in some instances
12657     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12658     
12659     var pad = function(n) {
12660         return n < 10 ? "0" + n : n;
12661     };
12662     
12663     var m = {
12664         "\b": '\\b',
12665         "\t": '\\t',
12666         "\n": '\\n',
12667         "\f": '\\f',
12668         "\r": '\\r',
12669         '"' : '\\"',
12670         "\\": '\\\\'
12671     };
12672
12673     var encodeString = function(s){
12674         if (/["\\\x00-\x1f]/.test(s)) {
12675             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12676                 var c = m[b];
12677                 if(c){
12678                     return c;
12679                 }
12680                 c = b.charCodeAt();
12681                 return "\\u00" +
12682                     Math.floor(c / 16).toString(16) +
12683                     (c % 16).toString(16);
12684             }) + '"';
12685         }
12686         return '"' + s + '"';
12687     };
12688     
12689     var encodeArray = function(o){
12690         var a = ["["], b, i, l = o.length, v;
12691             for (i = 0; i < l; i += 1) {
12692                 v = o[i];
12693                 switch (typeof v) {
12694                     case "undefined":
12695                     case "function":
12696                     case "unknown":
12697                         break;
12698                     default:
12699                         if (b) {
12700                             a.push(',');
12701                         }
12702                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12703                         b = true;
12704                 }
12705             }
12706             a.push("]");
12707             return a.join("");
12708     };
12709     
12710     var encodeDate = function(o){
12711         return '"' + o.getFullYear() + "-" +
12712                 pad(o.getMonth() + 1) + "-" +
12713                 pad(o.getDate()) + "T" +
12714                 pad(o.getHours()) + ":" +
12715                 pad(o.getMinutes()) + ":" +
12716                 pad(o.getSeconds()) + '"';
12717     };
12718     
12719     /**
12720      * Encodes an Object, Array or other value
12721      * @param {Mixed} o The variable to encode
12722      * @return {String} The JSON string
12723      */
12724     this.encode = function(o){
12725         if(typeof o == "undefined" || o === null){
12726             return "null";
12727         }else if(o instanceof Array){
12728             return encodeArray(o);
12729         }else if(o instanceof Date){
12730             return encodeDate(o);
12731         }else if(typeof o == "string"){
12732             return encodeString(o);
12733         }else if(typeof o == "number"){
12734             return isFinite(o) ? String(o) : "null";
12735         }else if(typeof o == "boolean"){
12736             return String(o);
12737         }else {
12738             var a = ["{"], b, i, v;
12739             for (i in o) {
12740                 if(!useHasOwn || o.hasOwnProperty(i)) {
12741                     v = o[i];
12742                     switch (typeof v) {
12743                     case "undefined":
12744                     case "function":
12745                     case "unknown":
12746                         break;
12747                     default:
12748                         if(b){
12749                             a.push(',');
12750                         }
12751                         a.push(this.encode(i), ":",
12752                                 v === null ? "null" : this.encode(v));
12753                         b = true;
12754                     }
12755                 }
12756             }
12757             a.push("}");
12758             return a.join("");
12759         }
12760     };
12761     
12762     /**
12763      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12764      * @param {String} json The JSON string
12765      * @return {Object} The resulting object
12766      */
12767     this.decode = function(json){
12768         /**
12769          * eval:var:json
12770          */
12771         return eval("(" + json + ')');
12772     };
12773 })();
12774 /** 
12775  * Shorthand for {@link Roo.util.JSON#encode}
12776  * @member Roo encode 
12777  * @method */
12778 Roo.encode = Roo.util.JSON.encode;
12779 /** 
12780  * Shorthand for {@link Roo.util.JSON#decode}
12781  * @member Roo decode 
12782  * @method */
12783 Roo.decode = Roo.util.JSON.decode;
12784 /*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794  
12795 /**
12796  * @class Roo.util.Format
12797  * Reusable data formatting functions
12798  * @singleton
12799  */
12800 Roo.util.Format = function(){
12801     var trimRe = /^\s+|\s+$/g;
12802     return {
12803         /**
12804          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12805          * @param {String} value The string to truncate
12806          * @param {Number} length The maximum length to allow before truncating
12807          * @return {String} The converted text
12808          */
12809         ellipsis : function(value, len){
12810             if(value && value.length > len){
12811                 return value.substr(0, len-3)+"...";
12812             }
12813             return value;
12814         },
12815
12816         /**
12817          * Checks a reference and converts it to empty string if it is undefined
12818          * @param {Mixed} value Reference to check
12819          * @return {Mixed} Empty string if converted, otherwise the original value
12820          */
12821         undef : function(value){
12822             return typeof value != "undefined" ? value : "";
12823         },
12824
12825         /**
12826          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12827          * @param {String} value The string to encode
12828          * @return {String} The encoded text
12829          */
12830         htmlEncode : function(value){
12831             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12832         },
12833
12834         /**
12835          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12836          * @param {String} value The string to decode
12837          * @return {String} The decoded text
12838          */
12839         htmlDecode : function(value){
12840             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12841         },
12842
12843         /**
12844          * Trims any whitespace from either side of a string
12845          * @param {String} value The text to trim
12846          * @return {String} The trimmed text
12847          */
12848         trim : function(value){
12849             return String(value).replace(trimRe, "");
12850         },
12851
12852         /**
12853          * Returns a substring from within an original string
12854          * @param {String} value The original text
12855          * @param {Number} start The start index of the substring
12856          * @param {Number} length The length of the substring
12857          * @return {String} The substring
12858          */
12859         substr : function(value, start, length){
12860             return String(value).substr(start, length);
12861         },
12862
12863         /**
12864          * Converts a string to all lower case letters
12865          * @param {String} value The text to convert
12866          * @return {String} The converted text
12867          */
12868         lowercase : function(value){
12869             return String(value).toLowerCase();
12870         },
12871
12872         /**
12873          * Converts a string to all upper case letters
12874          * @param {String} value The text to convert
12875          * @return {String} The converted text
12876          */
12877         uppercase : function(value){
12878             return String(value).toUpperCase();
12879         },
12880
12881         /**
12882          * Converts the first character only of a string to upper case
12883          * @param {String} value The text to convert
12884          * @return {String} The converted text
12885          */
12886         capitalize : function(value){
12887             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12888         },
12889
12890         // private
12891         call : function(value, fn){
12892             if(arguments.length > 2){
12893                 var args = Array.prototype.slice.call(arguments, 2);
12894                 args.unshift(value);
12895                  
12896                 return /** eval:var:value */  eval(fn).apply(window, args);
12897             }else{
12898                 /** eval:var:value */
12899                 return /** eval:var:value */ eval(fn).call(window, value);
12900             }
12901         },
12902
12903         /**
12904          * Format a number as US currency
12905          * @param {Number/String} value The numeric value to format
12906          * @return {String} The formatted currency string
12907          */
12908         usMoney : function(v){
12909             v = (Math.round((v-0)*100))/100;
12910             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12911             v = String(v);
12912             var ps = v.split('.');
12913             var whole = ps[0];
12914             var sub = ps[1] ? '.'+ ps[1] : '.00';
12915             var r = /(\d+)(\d{3})/;
12916             while (r.test(whole)) {
12917                 whole = whole.replace(r, '$1' + ',' + '$2');
12918             }
12919             return "$" + whole + sub ;
12920         },
12921
12922         /**
12923          * Parse a value into a formatted date using the specified format pattern.
12924          * @param {Mixed} value The value to format
12925          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12926          * @return {String} The formatted date string
12927          */
12928         date : function(v, format){
12929             if(!v){
12930                 return "";
12931             }
12932             if(!(v instanceof Date)){
12933                 v = new Date(Date.parse(v));
12934             }
12935             return v.dateFormat(format || "m/d/Y");
12936         },
12937
12938         /**
12939          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12940          * @param {String} format Any valid date format string
12941          * @return {Function} The date formatting function
12942          */
12943         dateRenderer : function(format){
12944             return function(v){
12945                 return Roo.util.Format.date(v, format);  
12946             };
12947         },
12948
12949         // private
12950         stripTagsRE : /<\/?[^>]+>/gi,
12951         
12952         /**
12953          * Strips all HTML tags
12954          * @param {Mixed} value The text from which to strip tags
12955          * @return {String} The stripped text
12956          */
12957         stripTags : function(v){
12958             return !v ? v : String(v).replace(this.stripTagsRE, "");
12959         }
12960     };
12961 }();/*
12962  * Based on:
12963  * Ext JS Library 1.1.1
12964  * Copyright(c) 2006-2007, Ext JS, LLC.
12965  *
12966  * Originally Released Under LGPL - original licence link has changed is not relivant.
12967  *
12968  * Fork - LGPL
12969  * <script type="text/javascript">
12970  */
12971
12972
12973  
12974
12975 /**
12976  * @class Roo.MasterTemplate
12977  * @extends Roo.Template
12978  * Provides a template that can have child templates. The syntax is:
12979 <pre><code>
12980 var t = new Roo.MasterTemplate(
12981         '&lt;select name="{name}"&gt;',
12982                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12983         '&lt;/select&gt;'
12984 );
12985 t.add('options', {value: 'foo', text: 'bar'});
12986 // or you can add multiple child elements in one shot
12987 t.addAll('options', [
12988     {value: 'foo', text: 'bar'},
12989     {value: 'foo2', text: 'bar2'},
12990     {value: 'foo3', text: 'bar3'}
12991 ]);
12992 // then append, applying the master template values
12993 t.append('my-form', {name: 'my-select'});
12994 </code></pre>
12995 * A name attribute for the child template is not required if you have only one child
12996 * template or you want to refer to them by index.
12997  */
12998 Roo.MasterTemplate = function(){
12999     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13000     this.originalHtml = this.html;
13001     var st = {};
13002     var m, re = this.subTemplateRe;
13003     re.lastIndex = 0;
13004     var subIndex = 0;
13005     while(m = re.exec(this.html)){
13006         var name = m[1], content = m[2];
13007         st[subIndex] = {
13008             name: name,
13009             index: subIndex,
13010             buffer: [],
13011             tpl : new Roo.Template(content)
13012         };
13013         if(name){
13014             st[name] = st[subIndex];
13015         }
13016         st[subIndex].tpl.compile();
13017         st[subIndex].tpl.call = this.call.createDelegate(this);
13018         subIndex++;
13019     }
13020     this.subCount = subIndex;
13021     this.subs = st;
13022 };
13023 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13024     /**
13025     * The regular expression used to match sub templates
13026     * @type RegExp
13027     * @property
13028     */
13029     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13030
13031     /**
13032      * Applies the passed values to a child template.
13033      * @param {String/Number} name (optional) The name or index of the child template
13034      * @param {Array/Object} values The values to be applied to the template
13035      * @return {MasterTemplate} this
13036      */
13037      add : function(name, values){
13038         if(arguments.length == 1){
13039             values = arguments[0];
13040             name = 0;
13041         }
13042         var s = this.subs[name];
13043         s.buffer[s.buffer.length] = s.tpl.apply(values);
13044         return this;
13045     },
13046
13047     /**
13048      * Applies all the passed values to a child template.
13049      * @param {String/Number} name (optional) The name or index of the child template
13050      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13051      * @param {Boolean} reset (optional) True to reset the template first
13052      * @return {MasterTemplate} this
13053      */
13054     fill : function(name, values, reset){
13055         var a = arguments;
13056         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13057             values = a[0];
13058             name = 0;
13059             reset = a[1];
13060         }
13061         if(reset){
13062             this.reset();
13063         }
13064         for(var i = 0, len = values.length; i < len; i++){
13065             this.add(name, values[i]);
13066         }
13067         return this;
13068     },
13069
13070     /**
13071      * Resets the template for reuse
13072      * @return {MasterTemplate} this
13073      */
13074      reset : function(){
13075         var s = this.subs;
13076         for(var i = 0; i < this.subCount; i++){
13077             s[i].buffer = [];
13078         }
13079         return this;
13080     },
13081
13082     applyTemplate : function(values){
13083         var s = this.subs;
13084         var replaceIndex = -1;
13085         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13086             return s[++replaceIndex].buffer.join("");
13087         });
13088         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13089     },
13090
13091     apply : function(){
13092         return this.applyTemplate.apply(this, arguments);
13093     },
13094
13095     compile : function(){return this;}
13096 });
13097
13098 /**
13099  * Alias for fill().
13100  * @method
13101  */
13102 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13103  /**
13104  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13105  * var tpl = Roo.MasterTemplate.from('element-id');
13106  * @param {String/HTMLElement} el
13107  * @param {Object} config
13108  * @static
13109  */
13110 Roo.MasterTemplate.from = function(el, config){
13111     el = Roo.getDom(el);
13112     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13113 };/*
13114  * Based on:
13115  * Ext JS Library 1.1.1
13116  * Copyright(c) 2006-2007, Ext JS, LLC.
13117  *
13118  * Originally Released Under LGPL - original licence link has changed is not relivant.
13119  *
13120  * Fork - LGPL
13121  * <script type="text/javascript">
13122  */
13123
13124  
13125 /**
13126  * @class Roo.util.CSS
13127  * Utility class for manipulating CSS rules
13128  * @singleton
13129  */
13130 Roo.util.CSS = function(){
13131         var rules = null;
13132         var doc = document;
13133
13134     var camelRe = /(-[a-z])/gi;
13135     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13136
13137    return {
13138    /**
13139     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13140     * tag and appended to the HEAD of the document.
13141     * @param {String} cssText The text containing the css rules
13142     * @param {String} id An id to add to the stylesheet for later removal
13143     * @return {StyleSheet}
13144     */
13145    createStyleSheet : function(cssText, id){
13146        var ss;
13147        var head = doc.getElementsByTagName("head")[0];
13148        var rules = doc.createElement("style");
13149        rules.setAttribute("type", "text/css");
13150        if(id){
13151            rules.setAttribute("id", id);
13152        }
13153        if(Roo.isIE){
13154            head.appendChild(rules);
13155            ss = rules.styleSheet;
13156            ss.cssText = cssText;
13157        }else{
13158            try{
13159                 rules.appendChild(doc.createTextNode(cssText));
13160            }catch(e){
13161                rules.cssText = cssText; 
13162            }
13163            head.appendChild(rules);
13164            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13165        }
13166        this.cacheStyleSheet(ss);
13167        return ss;
13168    },
13169
13170    /**
13171     * Removes a style or link tag by id
13172     * @param {String} id The id of the tag
13173     */
13174    removeStyleSheet : function(id){
13175        var existing = doc.getElementById(id);
13176        if(existing){
13177            existing.parentNode.removeChild(existing);
13178        }
13179    },
13180
13181    /**
13182     * Dynamically swaps an existing stylesheet reference for a new one
13183     * @param {String} id The id of an existing link tag to remove
13184     * @param {String} url The href of the new stylesheet to include
13185     */
13186    swapStyleSheet : function(id, url){
13187        this.removeStyleSheet(id);
13188        var ss = doc.createElement("link");
13189        ss.setAttribute("rel", "stylesheet");
13190        ss.setAttribute("type", "text/css");
13191        ss.setAttribute("id", id);
13192        ss.setAttribute("href", url);
13193        doc.getElementsByTagName("head")[0].appendChild(ss);
13194    },
13195    
13196    /**
13197     * Refresh the rule cache if you have dynamically added stylesheets
13198     * @return {Object} An object (hash) of rules indexed by selector
13199     */
13200    refreshCache : function(){
13201        return this.getRules(true);
13202    },
13203
13204    // private
13205    cacheStyleSheet : function(ss){
13206        if(!rules){
13207            rules = {};
13208        }
13209        try{// try catch for cross domain access issue
13210            var ssRules = ss.cssRules || ss.rules;
13211            for(var j = ssRules.length-1; j >= 0; --j){
13212                rules[ssRules[j].selectorText] = ssRules[j];
13213            }
13214        }catch(e){}
13215    },
13216    
13217    /**
13218     * Gets all css rules for the document
13219     * @param {Boolean} refreshCache true to refresh the internal cache
13220     * @return {Object} An object (hash) of rules indexed by selector
13221     */
13222    getRules : function(refreshCache){
13223                 if(rules == null || refreshCache){
13224                         rules = {};
13225                         var ds = doc.styleSheets;
13226                         for(var i =0, len = ds.length; i < len; i++){
13227                             try{
13228                         this.cacheStyleSheet(ds[i]);
13229                     }catch(e){} 
13230                 }
13231                 }
13232                 return rules;
13233         },
13234         
13235         /**
13236     * Gets an an individual CSS rule by selector(s)
13237     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13238     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13239     * @return {CSSRule} The CSS rule or null if one is not found
13240     */
13241    getRule : function(selector, refreshCache){
13242                 var rs = this.getRules(refreshCache);
13243                 if(!(selector instanceof Array)){
13244                     return rs[selector];
13245                 }
13246                 for(var i = 0; i < selector.length; i++){
13247                         if(rs[selector[i]]){
13248                                 return rs[selector[i]];
13249                         }
13250                 }
13251                 return null;
13252         },
13253         
13254         
13255         /**
13256     * Updates a rule property
13257     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13258     * @param {String} property The css property
13259     * @param {String} value The new value for the property
13260     * @return {Boolean} true If a rule was found and updated
13261     */
13262    updateRule : function(selector, property, value){
13263                 if(!(selector instanceof Array)){
13264                         var rule = this.getRule(selector);
13265                         if(rule){
13266                                 rule.style[property.replace(camelRe, camelFn)] = value;
13267                                 return true;
13268                         }
13269                 }else{
13270                         for(var i = 0; i < selector.length; i++){
13271                                 if(this.updateRule(selector[i], property, value)){
13272                                         return true;
13273                                 }
13274                         }
13275                 }
13276                 return false;
13277         }
13278    };   
13279 }();/*
13280  * Based on:
13281  * Ext JS Library 1.1.1
13282  * Copyright(c) 2006-2007, Ext JS, LLC.
13283  *
13284  * Originally Released Under LGPL - original licence link has changed is not relivant.
13285  *
13286  * Fork - LGPL
13287  * <script type="text/javascript">
13288  */
13289
13290  
13291
13292 /**
13293  * @class Roo.util.ClickRepeater
13294  * @extends Roo.util.Observable
13295  * 
13296  * A wrapper class which can be applied to any element. Fires a "click" event while the
13297  * mouse is pressed. The interval between firings may be specified in the config but
13298  * defaults to 10 milliseconds.
13299  * 
13300  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13301  * 
13302  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13303  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13304  * Similar to an autorepeat key delay.
13305  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13306  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13307  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13308  *           "interval" and "delay" are ignored. "immediate" is honored.
13309  * @cfg {Boolean} preventDefault True to prevent the default click event
13310  * @cfg {Boolean} stopDefault True to stop the default click event
13311  * 
13312  * @history
13313  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13314  *     2007-02-02 jvs Renamed to ClickRepeater
13315  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13316  *
13317  *  @constructor
13318  * @param {String/HTMLElement/Element} el The element to listen on
13319  * @param {Object} config
13320  **/
13321 Roo.util.ClickRepeater = function(el, config)
13322 {
13323     this.el = Roo.get(el);
13324     this.el.unselectable();
13325
13326     Roo.apply(this, config);
13327
13328     this.addEvents({
13329     /**
13330      * @event mousedown
13331      * Fires when the mouse button is depressed.
13332      * @param {Roo.util.ClickRepeater} this
13333      */
13334         "mousedown" : true,
13335     /**
13336      * @event click
13337      * Fires on a specified interval during the time the element is pressed.
13338      * @param {Roo.util.ClickRepeater} this
13339      */
13340         "click" : true,
13341     /**
13342      * @event mouseup
13343      * Fires when the mouse key is released.
13344      * @param {Roo.util.ClickRepeater} this
13345      */
13346         "mouseup" : true
13347     });
13348
13349     this.el.on("mousedown", this.handleMouseDown, this);
13350     if(this.preventDefault || this.stopDefault){
13351         this.el.on("click", function(e){
13352             if(this.preventDefault){
13353                 e.preventDefault();
13354             }
13355             if(this.stopDefault){
13356                 e.stopEvent();
13357             }
13358         }, this);
13359     }
13360
13361     // allow inline handler
13362     if(this.handler){
13363         this.on("click", this.handler,  this.scope || this);
13364     }
13365
13366     Roo.util.ClickRepeater.superclass.constructor.call(this);
13367 };
13368
13369 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13370     interval : 20,
13371     delay: 250,
13372     preventDefault : true,
13373     stopDefault : false,
13374     timer : 0,
13375
13376     // private
13377     handleMouseDown : function(){
13378         clearTimeout(this.timer);
13379         this.el.blur();
13380         if(this.pressClass){
13381             this.el.addClass(this.pressClass);
13382         }
13383         this.mousedownTime = new Date();
13384
13385         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13386         this.el.on("mouseout", this.handleMouseOut, this);
13387
13388         this.fireEvent("mousedown", this);
13389         this.fireEvent("click", this);
13390         
13391         this.timer = this.click.defer(this.delay || this.interval, this);
13392     },
13393
13394     // private
13395     click : function(){
13396         this.fireEvent("click", this);
13397         this.timer = this.click.defer(this.getInterval(), this);
13398     },
13399
13400     // private
13401     getInterval: function(){
13402         if(!this.accelerate){
13403             return this.interval;
13404         }
13405         var pressTime = this.mousedownTime.getElapsed();
13406         if(pressTime < 500){
13407             return 400;
13408         }else if(pressTime < 1700){
13409             return 320;
13410         }else if(pressTime < 2600){
13411             return 250;
13412         }else if(pressTime < 3500){
13413             return 180;
13414         }else if(pressTime < 4400){
13415             return 140;
13416         }else if(pressTime < 5300){
13417             return 80;
13418         }else if(pressTime < 6200){
13419             return 50;
13420         }else{
13421             return 10;
13422         }
13423     },
13424
13425     // private
13426     handleMouseOut : function(){
13427         clearTimeout(this.timer);
13428         if(this.pressClass){
13429             this.el.removeClass(this.pressClass);
13430         }
13431         this.el.on("mouseover", this.handleMouseReturn, this);
13432     },
13433
13434     // private
13435     handleMouseReturn : function(){
13436         this.el.un("mouseover", this.handleMouseReturn);
13437         if(this.pressClass){
13438             this.el.addClass(this.pressClass);
13439         }
13440         this.click();
13441     },
13442
13443     // private
13444     handleMouseUp : function(){
13445         clearTimeout(this.timer);
13446         this.el.un("mouseover", this.handleMouseReturn);
13447         this.el.un("mouseout", this.handleMouseOut);
13448         Roo.get(document).un("mouseup", this.handleMouseUp);
13449         this.el.removeClass(this.pressClass);
13450         this.fireEvent("mouseup", this);
13451     }
13452 });/*
13453  * Based on:
13454  * Ext JS Library 1.1.1
13455  * Copyright(c) 2006-2007, Ext JS, LLC.
13456  *
13457  * Originally Released Under LGPL - original licence link has changed is not relivant.
13458  *
13459  * Fork - LGPL
13460  * <script type="text/javascript">
13461  */
13462
13463  
13464 /**
13465  * @class Roo.KeyNav
13466  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13467  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13468  * way to implement custom navigation schemes for any UI component.</p>
13469  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13470  * pageUp, pageDown, del, home, end.  Usage:</p>
13471  <pre><code>
13472 var nav = new Roo.KeyNav("my-element", {
13473     "left" : function(e){
13474         this.moveLeft(e.ctrlKey);
13475     },
13476     "right" : function(e){
13477         this.moveRight(e.ctrlKey);
13478     },
13479     "enter" : function(e){
13480         this.save();
13481     },
13482     scope : this
13483 });
13484 </code></pre>
13485  * @constructor
13486  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13487  * @param {Object} config The config
13488  */
13489 Roo.KeyNav = function(el, config){
13490     this.el = Roo.get(el);
13491     Roo.apply(this, config);
13492     if(!this.disabled){
13493         this.disabled = true;
13494         this.enable();
13495     }
13496 };
13497
13498 Roo.KeyNav.prototype = {
13499     /**
13500      * @cfg {Boolean} disabled
13501      * True to disable this KeyNav instance (defaults to false)
13502      */
13503     disabled : false,
13504     /**
13505      * @cfg {String} defaultEventAction
13506      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13507      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13508      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13509      */
13510     defaultEventAction: "stopEvent",
13511     /**
13512      * @cfg {Boolean} forceKeyDown
13513      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13514      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13515      * handle keydown instead of keypress.
13516      */
13517     forceKeyDown : false,
13518
13519     // private
13520     prepareEvent : function(e){
13521         var k = e.getKey();
13522         var h = this.keyToHandler[k];
13523         //if(h && this[h]){
13524         //    e.stopPropagation();
13525         //}
13526         if(Roo.isSafari && h && k >= 37 && k <= 40){
13527             e.stopEvent();
13528         }
13529     },
13530
13531     // private
13532     relay : function(e){
13533         var k = e.getKey();
13534         var h = this.keyToHandler[k];
13535         if(h && this[h]){
13536             if(this.doRelay(e, this[h], h) !== true){
13537                 e[this.defaultEventAction]();
13538             }
13539         }
13540     },
13541
13542     // private
13543     doRelay : function(e, h, hname){
13544         return h.call(this.scope || this, e);
13545     },
13546
13547     // possible handlers
13548     enter : false,
13549     left : false,
13550     right : false,
13551     up : false,
13552     down : false,
13553     tab : false,
13554     esc : false,
13555     pageUp : false,
13556     pageDown : false,
13557     del : false,
13558     home : false,
13559     end : false,
13560
13561     // quick lookup hash
13562     keyToHandler : {
13563         37 : "left",
13564         39 : "right",
13565         38 : "up",
13566         40 : "down",
13567         33 : "pageUp",
13568         34 : "pageDown",
13569         46 : "del",
13570         36 : "home",
13571         35 : "end",
13572         13 : "enter",
13573         27 : "esc",
13574         9  : "tab"
13575     },
13576
13577         /**
13578          * Enable this KeyNav
13579          */
13580         enable: function(){
13581                 if(this.disabled){
13582             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13583             // the EventObject will normalize Safari automatically
13584             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13585                 this.el.on("keydown", this.relay,  this);
13586             }else{
13587                 this.el.on("keydown", this.prepareEvent,  this);
13588                 this.el.on("keypress", this.relay,  this);
13589             }
13590                     this.disabled = false;
13591                 }
13592         },
13593
13594         /**
13595          * Disable this KeyNav
13596          */
13597         disable: function(){
13598                 if(!this.disabled){
13599                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13600                 this.el.un("keydown", this.relay);
13601             }else{
13602                 this.el.un("keydown", this.prepareEvent);
13603                 this.el.un("keypress", this.relay);
13604             }
13605                     this.disabled = true;
13606                 }
13607         }
13608 };/*
13609  * Based on:
13610  * Ext JS Library 1.1.1
13611  * Copyright(c) 2006-2007, Ext JS, LLC.
13612  *
13613  * Originally Released Under LGPL - original licence link has changed is not relivant.
13614  *
13615  * Fork - LGPL
13616  * <script type="text/javascript">
13617  */
13618
13619  
13620 /**
13621  * @class Roo.KeyMap
13622  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13623  * The constructor accepts the same config object as defined by {@link #addBinding}.
13624  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13625  * combination it will call the function with this signature (if the match is a multi-key
13626  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13627  * A KeyMap can also handle a string representation of keys.<br />
13628  * Usage:
13629  <pre><code>
13630 // map one key by key code
13631 var map = new Roo.KeyMap("my-element", {
13632     key: 13, // or Roo.EventObject.ENTER
13633     fn: myHandler,
13634     scope: myObject
13635 });
13636
13637 // map multiple keys to one action by string
13638 var map = new Roo.KeyMap("my-element", {
13639     key: "a\r\n\t",
13640     fn: myHandler,
13641     scope: myObject
13642 });
13643
13644 // map multiple keys to multiple actions by strings and array of codes
13645 var map = new Roo.KeyMap("my-element", [
13646     {
13647         key: [10,13],
13648         fn: function(){ alert("Return was pressed"); }
13649     }, {
13650         key: "abc",
13651         fn: function(){ alert('a, b or c was pressed'); }
13652     }, {
13653         key: "\t",
13654         ctrl:true,
13655         shift:true,
13656         fn: function(){ alert('Control + shift + tab was pressed.'); }
13657     }
13658 ]);
13659 </code></pre>
13660  * <b>Note: A KeyMap starts enabled</b>
13661  * @constructor
13662  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13663  * @param {Object} config The config (see {@link #addBinding})
13664  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13665  */
13666 Roo.KeyMap = function(el, config, eventName){
13667     this.el  = Roo.get(el);
13668     this.eventName = eventName || "keydown";
13669     this.bindings = [];
13670     if(config){
13671         this.addBinding(config);
13672     }
13673     this.enable();
13674 };
13675
13676 Roo.KeyMap.prototype = {
13677     /**
13678      * True to stop the event from bubbling and prevent the default browser action if the
13679      * key was handled by the KeyMap (defaults to false)
13680      * @type Boolean
13681      */
13682     stopEvent : false,
13683
13684     /**
13685      * Add a new binding to this KeyMap. The following config object properties are supported:
13686      * <pre>
13687 Property    Type             Description
13688 ----------  ---------------  ----------------------------------------------------------------------
13689 key         String/Array     A single keycode or an array of keycodes to handle
13690 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13691 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13692 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13693 fn          Function         The function to call when KeyMap finds the expected key combination
13694 scope       Object           The scope of the callback function
13695 </pre>
13696      *
13697      * Usage:
13698      * <pre><code>
13699 // Create a KeyMap
13700 var map = new Roo.KeyMap(document, {
13701     key: Roo.EventObject.ENTER,
13702     fn: handleKey,
13703     scope: this
13704 });
13705
13706 //Add a new binding to the existing KeyMap later
13707 map.addBinding({
13708     key: 'abc',
13709     shift: true,
13710     fn: handleKey,
13711     scope: this
13712 });
13713 </code></pre>
13714      * @param {Object/Array} config A single KeyMap config or an array of configs
13715      */
13716         addBinding : function(config){
13717         if(config instanceof Array){
13718             for(var i = 0, len = config.length; i < len; i++){
13719                 this.addBinding(config[i]);
13720             }
13721             return;
13722         }
13723         var keyCode = config.key,
13724             shift = config.shift, 
13725             ctrl = config.ctrl, 
13726             alt = config.alt,
13727             fn = config.fn,
13728             scope = config.scope;
13729         if(typeof keyCode == "string"){
13730             var ks = [];
13731             var keyString = keyCode.toUpperCase();
13732             for(var j = 0, len = keyString.length; j < len; j++){
13733                 ks.push(keyString.charCodeAt(j));
13734             }
13735             keyCode = ks;
13736         }
13737         var keyArray = keyCode instanceof Array;
13738         var handler = function(e){
13739             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13740                 var k = e.getKey();
13741                 if(keyArray){
13742                     for(var i = 0, len = keyCode.length; i < len; i++){
13743                         if(keyCode[i] == k){
13744                           if(this.stopEvent){
13745                               e.stopEvent();
13746                           }
13747                           fn.call(scope || window, k, e);
13748                           return;
13749                         }
13750                     }
13751                 }else{
13752                     if(k == keyCode){
13753                         if(this.stopEvent){
13754                            e.stopEvent();
13755                         }
13756                         fn.call(scope || window, k, e);
13757                     }
13758                 }
13759             }
13760         };
13761         this.bindings.push(handler);  
13762         },
13763
13764     /**
13765      * Shorthand for adding a single key listener
13766      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13767      * following options:
13768      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13769      * @param {Function} fn The function to call
13770      * @param {Object} scope (optional) The scope of the function
13771      */
13772     on : function(key, fn, scope){
13773         var keyCode, shift, ctrl, alt;
13774         if(typeof key == "object" && !(key instanceof Array)){
13775             keyCode = key.key;
13776             shift = key.shift;
13777             ctrl = key.ctrl;
13778             alt = key.alt;
13779         }else{
13780             keyCode = key;
13781         }
13782         this.addBinding({
13783             key: keyCode,
13784             shift: shift,
13785             ctrl: ctrl,
13786             alt: alt,
13787             fn: fn,
13788             scope: scope
13789         })
13790     },
13791
13792     // private
13793     handleKeyDown : function(e){
13794             if(this.enabled){ //just in case
13795             var b = this.bindings;
13796             for(var i = 0, len = b.length; i < len; i++){
13797                 b[i].call(this, e);
13798             }
13799             }
13800         },
13801         
13802         /**
13803          * Returns true if this KeyMap is enabled
13804          * @return {Boolean} 
13805          */
13806         isEnabled : function(){
13807             return this.enabled;  
13808         },
13809         
13810         /**
13811          * Enables this KeyMap
13812          */
13813         enable: function(){
13814                 if(!this.enabled){
13815                     this.el.on(this.eventName, this.handleKeyDown, this);
13816                     this.enabled = true;
13817                 }
13818         },
13819
13820         /**
13821          * Disable this KeyMap
13822          */
13823         disable: function(){
13824                 if(this.enabled){
13825                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13826                     this.enabled = false;
13827                 }
13828         }
13829 };/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840  
13841 /**
13842  * @class Roo.util.TextMetrics
13843  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13844  * wide, in pixels, a given block of text will be.
13845  * @singleton
13846  */
13847 Roo.util.TextMetrics = function(){
13848     var shared;
13849     return {
13850         /**
13851          * Measures the size of the specified text
13852          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13853          * that can affect the size of the rendered text
13854          * @param {String} text The text to measure
13855          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13856          * in order to accurately measure the text height
13857          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13858          */
13859         measure : function(el, text, fixedWidth){
13860             if(!shared){
13861                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13862             }
13863             shared.bind(el);
13864             shared.setFixedWidth(fixedWidth || 'auto');
13865             return shared.getSize(text);
13866         },
13867
13868         /**
13869          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13870          * the overhead of multiple calls to initialize the style properties on each measurement.
13871          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13872          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13873          * in order to accurately measure the text height
13874          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13875          */
13876         createInstance : function(el, fixedWidth){
13877             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13878         }
13879     };
13880 }();
13881
13882  
13883
13884 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13885     var ml = new Roo.Element(document.createElement('div'));
13886     document.body.appendChild(ml.dom);
13887     ml.position('absolute');
13888     ml.setLeftTop(-1000, -1000);
13889     ml.hide();
13890
13891     if(fixedWidth){
13892         ml.setWidth(fixedWidth);
13893     }
13894      
13895     var instance = {
13896         /**
13897          * Returns the size of the specified text based on the internal element's style and width properties
13898          * @memberOf Roo.util.TextMetrics.Instance#
13899          * @param {String} text The text to measure
13900          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13901          */
13902         getSize : function(text){
13903             ml.update(text);
13904             var s = ml.getSize();
13905             ml.update('');
13906             return s;
13907         },
13908
13909         /**
13910          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13911          * that can affect the size of the rendered text
13912          * @memberOf Roo.util.TextMetrics.Instance#
13913          * @param {String/HTMLElement} el The element, dom node or id
13914          */
13915         bind : function(el){
13916             ml.setStyle(
13917                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13918             );
13919         },
13920
13921         /**
13922          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13923          * to set a fixed width in order to accurately measure the text height.
13924          * @memberOf Roo.util.TextMetrics.Instance#
13925          * @param {Number} width The width to set on the element
13926          */
13927         setFixedWidth : function(width){
13928             ml.setWidth(width);
13929         },
13930
13931         /**
13932          * Returns the measured width of the specified text
13933          * @memberOf Roo.util.TextMetrics.Instance#
13934          * @param {String} text The text to measure
13935          * @return {Number} width The width in pixels
13936          */
13937         getWidth : function(text){
13938             ml.dom.style.width = 'auto';
13939             return this.getSize(text).width;
13940         },
13941
13942         /**
13943          * Returns the measured height of the specified text.  For multiline text, be sure to call
13944          * {@link #setFixedWidth} if necessary.
13945          * @memberOf Roo.util.TextMetrics.Instance#
13946          * @param {String} text The text to measure
13947          * @return {Number} height The height in pixels
13948          */
13949         getHeight : function(text){
13950             return this.getSize(text).height;
13951         }
13952     };
13953
13954     instance.bind(bindTo);
13955
13956     return instance;
13957 };
13958
13959 // backwards compat
13960 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13961  * Based on:
13962  * Ext JS Library 1.1.1
13963  * Copyright(c) 2006-2007, Ext JS, LLC.
13964  *
13965  * Originally Released Under LGPL - original licence link has changed is not relivant.
13966  *
13967  * Fork - LGPL
13968  * <script type="text/javascript">
13969  */
13970
13971 /**
13972  * @class Roo.state.Provider
13973  * Abstract base class for state provider implementations. This class provides methods
13974  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13975  * Provider interface.
13976  */
13977 Roo.state.Provider = function(){
13978     /**
13979      * @event statechange
13980      * Fires when a state change occurs.
13981      * @param {Provider} this This state provider
13982      * @param {String} key The state key which was changed
13983      * @param {String} value The encoded value for the state
13984      */
13985     this.addEvents({
13986         "statechange": true
13987     });
13988     this.state = {};
13989     Roo.state.Provider.superclass.constructor.call(this);
13990 };
13991 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13992     /**
13993      * Returns the current value for a key
13994      * @param {String} name The key name
13995      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13996      * @return {Mixed} The state data
13997      */
13998     get : function(name, defaultValue){
13999         return typeof this.state[name] == "undefined" ?
14000             defaultValue : this.state[name];
14001     },
14002     
14003     /**
14004      * Clears a value from the state
14005      * @param {String} name The key name
14006      */
14007     clear : function(name){
14008         delete this.state[name];
14009         this.fireEvent("statechange", this, name, null);
14010     },
14011     
14012     /**
14013      * Sets the value for a key
14014      * @param {String} name The key name
14015      * @param {Mixed} value The value to set
14016      */
14017     set : function(name, value){
14018         this.state[name] = value;
14019         this.fireEvent("statechange", this, name, value);
14020     },
14021     
14022     /**
14023      * Decodes a string previously encoded with {@link #encodeValue}.
14024      * @param {String} value The value to decode
14025      * @return {Mixed} The decoded value
14026      */
14027     decodeValue : function(cookie){
14028         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14029         var matches = re.exec(unescape(cookie));
14030         if(!matches || !matches[1]) return; // non state cookie
14031         var type = matches[1];
14032         var v = matches[2];
14033         switch(type){
14034             case "n":
14035                 return parseFloat(v);
14036             case "d":
14037                 return new Date(Date.parse(v));
14038             case "b":
14039                 return (v == "1");
14040             case "a":
14041                 var all = [];
14042                 var values = v.split("^");
14043                 for(var i = 0, len = values.length; i < len; i++){
14044                     all.push(this.decodeValue(values[i]));
14045                 }
14046                 return all;
14047            case "o":
14048                 var all = {};
14049                 var values = v.split("^");
14050                 for(var i = 0, len = values.length; i < len; i++){
14051                     var kv = values[i].split("=");
14052                     all[kv[0]] = this.decodeValue(kv[1]);
14053                 }
14054                 return all;
14055            default:
14056                 return v;
14057         }
14058     },
14059     
14060     /**
14061      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14062      * @param {Mixed} value The value to encode
14063      * @return {String} The encoded value
14064      */
14065     encodeValue : function(v){
14066         var enc;
14067         if(typeof v == "number"){
14068             enc = "n:" + v;
14069         }else if(typeof v == "boolean"){
14070             enc = "b:" + (v ? "1" : "0");
14071         }else if(v instanceof Date){
14072             enc = "d:" + v.toGMTString();
14073         }else if(v instanceof Array){
14074             var flat = "";
14075             for(var i = 0, len = v.length; i < len; i++){
14076                 flat += this.encodeValue(v[i]);
14077                 if(i != len-1) flat += "^";
14078             }
14079             enc = "a:" + flat;
14080         }else if(typeof v == "object"){
14081             var flat = "";
14082             for(var key in v){
14083                 if(typeof v[key] != "function"){
14084                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14085                 }
14086             }
14087             enc = "o:" + flat.substring(0, flat.length-1);
14088         }else{
14089             enc = "s:" + v;
14090         }
14091         return escape(enc);        
14092     }
14093 });
14094
14095 /*
14096  * Based on:
14097  * Ext JS Library 1.1.1
14098  * Copyright(c) 2006-2007, Ext JS, LLC.
14099  *
14100  * Originally Released Under LGPL - original licence link has changed is not relivant.
14101  *
14102  * Fork - LGPL
14103  * <script type="text/javascript">
14104  */
14105 /**
14106  * @class Roo.state.Manager
14107  * This is the global state manager. By default all components that are "state aware" check this class
14108  * for state information if you don't pass them a custom state provider. In order for this class
14109  * to be useful, it must be initialized with a provider when your application initializes.
14110  <pre><code>
14111 // in your initialization function
14112 init : function(){
14113    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14114    ...
14115    // supposed you have a {@link Roo.BorderLayout}
14116    var layout = new Roo.BorderLayout(...);
14117    layout.restoreState();
14118    // or a {Roo.BasicDialog}
14119    var dialog = new Roo.BasicDialog(...);
14120    dialog.restoreState();
14121  </code></pre>
14122  * @singleton
14123  */
14124 Roo.state.Manager = function(){
14125     var provider = new Roo.state.Provider();
14126     
14127     return {
14128         /**
14129          * Configures the default state provider for your application
14130          * @param {Provider} stateProvider The state provider to set
14131          */
14132         setProvider : function(stateProvider){
14133             provider = stateProvider;
14134         },
14135         
14136         /**
14137          * Returns the current value for a key
14138          * @param {String} name The key name
14139          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14140          * @return {Mixed} The state data
14141          */
14142         get : function(key, defaultValue){
14143             return provider.get(key, defaultValue);
14144         },
14145         
14146         /**
14147          * Sets the value for a key
14148          * @param {String} name The key name
14149          * @param {Mixed} value The state data
14150          */
14151          set : function(key, value){
14152             provider.set(key, value);
14153         },
14154         
14155         /**
14156          * Clears a value from the state
14157          * @param {String} name The key name
14158          */
14159         clear : function(key){
14160             provider.clear(key);
14161         },
14162         
14163         /**
14164          * Gets the currently configured state provider
14165          * @return {Provider} The state provider
14166          */
14167         getProvider : function(){
14168             return provider;
14169         }
14170     };
14171 }();
14172 /*
14173  * Based on:
14174  * Ext JS Library 1.1.1
14175  * Copyright(c) 2006-2007, Ext JS, LLC.
14176  *
14177  * Originally Released Under LGPL - original licence link has changed is not relivant.
14178  *
14179  * Fork - LGPL
14180  * <script type="text/javascript">
14181  */
14182 /**
14183  * @class Roo.state.CookieProvider
14184  * @extends Roo.state.Provider
14185  * The default Provider implementation which saves state via cookies.
14186  * <br />Usage:
14187  <pre><code>
14188    var cp = new Roo.state.CookieProvider({
14189        path: "/cgi-bin/",
14190        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14191        domain: "roojs.com"
14192    })
14193    Roo.state.Manager.setProvider(cp);
14194  </code></pre>
14195  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14196  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14197  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14198  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14199  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14200  * domain the page is running on including the 'www' like 'www.roojs.com')
14201  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14202  * @constructor
14203  * Create a new CookieProvider
14204  * @param {Object} config The configuration object
14205  */
14206 Roo.state.CookieProvider = function(config){
14207     Roo.state.CookieProvider.superclass.constructor.call(this);
14208     this.path = "/";
14209     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14210     this.domain = null;
14211     this.secure = false;
14212     Roo.apply(this, config);
14213     this.state = this.readCookies();
14214 };
14215
14216 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14217     // private
14218     set : function(name, value){
14219         if(typeof value == "undefined" || value === null){
14220             this.clear(name);
14221             return;
14222         }
14223         this.setCookie(name, value);
14224         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14225     },
14226
14227     // private
14228     clear : function(name){
14229         this.clearCookie(name);
14230         Roo.state.CookieProvider.superclass.clear.call(this, name);
14231     },
14232
14233     // private
14234     readCookies : function(){
14235         var cookies = {};
14236         var c = document.cookie + ";";
14237         var re = /\s?(.*?)=(.*?);/g;
14238         var matches;
14239         while((matches = re.exec(c)) != null){
14240             var name = matches[1];
14241             var value = matches[2];
14242             if(name && name.substring(0,3) == "ys-"){
14243                 cookies[name.substr(3)] = this.decodeValue(value);
14244             }
14245         }
14246         return cookies;
14247     },
14248
14249     // private
14250     setCookie : function(name, value){
14251         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14252            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14253            ((this.path == null) ? "" : ("; path=" + this.path)) +
14254            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14255            ((this.secure == true) ? "; secure" : "");
14256     },
14257
14258     // private
14259     clearCookie : function(name){
14260         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14261            ((this.path == null) ? "" : ("; path=" + this.path)) +
14262            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14263            ((this.secure == true) ? "; secure" : "");
14264     }
14265 });/*
14266  * Based on:
14267  * Ext JS Library 1.1.1
14268  * Copyright(c) 2006-2007, Ext JS, LLC.
14269  *
14270  * Originally Released Under LGPL - original licence link has changed is not relivant.
14271  *
14272  * Fork - LGPL
14273  * <script type="text/javascript">
14274  */
14275
14276
14277
14278 /*
14279  * These classes are derivatives of the similarly named classes in the YUI Library.
14280  * The original license:
14281  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14282  * Code licensed under the BSD License:
14283  * http://developer.yahoo.net/yui/license.txt
14284  */
14285
14286 (function() {
14287
14288 var Event=Roo.EventManager;
14289 var Dom=Roo.lib.Dom;
14290
14291 /**
14292  * @class Roo.dd.DragDrop
14293  * Defines the interface and base operation of items that that can be
14294  * dragged or can be drop targets.  It was designed to be extended, overriding
14295  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14296  * Up to three html elements can be associated with a DragDrop instance:
14297  * <ul>
14298  * <li>linked element: the element that is passed into the constructor.
14299  * This is the element which defines the boundaries for interaction with
14300  * other DragDrop objects.</li>
14301  * <li>handle element(s): The drag operation only occurs if the element that
14302  * was clicked matches a handle element.  By default this is the linked
14303  * element, but there are times that you will want only a portion of the
14304  * linked element to initiate the drag operation, and the setHandleElId()
14305  * method provides a way to define this.</li>
14306  * <li>drag element: this represents the element that would be moved along
14307  * with the cursor during a drag operation.  By default, this is the linked
14308  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14309  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14310  * </li>
14311  * </ul>
14312  * This class should not be instantiated until the onload event to ensure that
14313  * the associated elements are available.
14314  * The following would define a DragDrop obj that would interact with any
14315  * other DragDrop obj in the "group1" group:
14316  * <pre>
14317  *  dd = new Roo.dd.DragDrop("div1", "group1");
14318  * </pre>
14319  * Since none of the event handlers have been implemented, nothing would
14320  * actually happen if you were to run the code above.  Normally you would
14321  * override this class or one of the default implementations, but you can
14322  * also override the methods you want on an instance of the class...
14323  * <pre>
14324  *  dd.onDragDrop = function(e, id) {
14325  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14326  *  }
14327  * </pre>
14328  * @constructor
14329  * @param {String} id of the element that is linked to this instance
14330  * @param {String} sGroup the group of related DragDrop objects
14331  * @param {object} config an object containing configurable attributes
14332  *                Valid properties for DragDrop:
14333  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14334  */
14335 Roo.dd.DragDrop = function(id, sGroup, config) {
14336     if (id) {
14337         this.init(id, sGroup, config);
14338     }
14339 };
14340
14341 Roo.dd.DragDrop.prototype = {
14342
14343     /**
14344      * The id of the element associated with this object.  This is what we
14345      * refer to as the "linked element" because the size and position of
14346      * this element is used to determine when the drag and drop objects have
14347      * interacted.
14348      * @property id
14349      * @type String
14350      */
14351     id: null,
14352
14353     /**
14354      * Configuration attributes passed into the constructor
14355      * @property config
14356      * @type object
14357      */
14358     config: null,
14359
14360     /**
14361      * The id of the element that will be dragged.  By default this is same
14362      * as the linked element , but could be changed to another element. Ex:
14363      * Roo.dd.DDProxy
14364      * @property dragElId
14365      * @type String
14366      * @private
14367      */
14368     dragElId: null,
14369
14370     /**
14371      * the id of the element that initiates the drag operation.  By default
14372      * this is the linked element, but could be changed to be a child of this
14373      * element.  This lets us do things like only starting the drag when the
14374      * header element within the linked html element is clicked.
14375      * @property handleElId
14376      * @type String
14377      * @private
14378      */
14379     handleElId: null,
14380
14381     /**
14382      * An associative array of HTML tags that will be ignored if clicked.
14383      * @property invalidHandleTypes
14384      * @type {string: string}
14385      */
14386     invalidHandleTypes: null,
14387
14388     /**
14389      * An associative array of ids for elements that will be ignored if clicked
14390      * @property invalidHandleIds
14391      * @type {string: string}
14392      */
14393     invalidHandleIds: null,
14394
14395     /**
14396      * An indexted array of css class names for elements that will be ignored
14397      * if clicked.
14398      * @property invalidHandleClasses
14399      * @type string[]
14400      */
14401     invalidHandleClasses: null,
14402
14403     /**
14404      * The linked element's absolute X position at the time the drag was
14405      * started
14406      * @property startPageX
14407      * @type int
14408      * @private
14409      */
14410     startPageX: 0,
14411
14412     /**
14413      * The linked element's absolute X position at the time the drag was
14414      * started
14415      * @property startPageY
14416      * @type int
14417      * @private
14418      */
14419     startPageY: 0,
14420
14421     /**
14422      * The group defines a logical collection of DragDrop objects that are
14423      * related.  Instances only get events when interacting with other
14424      * DragDrop object in the same group.  This lets us define multiple
14425      * groups using a single DragDrop subclass if we want.
14426      * @property groups
14427      * @type {string: string}
14428      */
14429     groups: null,
14430
14431     /**
14432      * Individual drag/drop instances can be locked.  This will prevent
14433      * onmousedown start drag.
14434      * @property locked
14435      * @type boolean
14436      * @private
14437      */
14438     locked: false,
14439
14440     /**
14441      * Lock this instance
14442      * @method lock
14443      */
14444     lock: function() { this.locked = true; },
14445
14446     /**
14447      * Unlock this instace
14448      * @method unlock
14449      */
14450     unlock: function() { this.locked = false; },
14451
14452     /**
14453      * By default, all insances can be a drop target.  This can be disabled by
14454      * setting isTarget to false.
14455      * @method isTarget
14456      * @type boolean
14457      */
14458     isTarget: true,
14459
14460     /**
14461      * The padding configured for this drag and drop object for calculating
14462      * the drop zone intersection with this object.
14463      * @method padding
14464      * @type int[]
14465      */
14466     padding: null,
14467
14468     /**
14469      * Cached reference to the linked element
14470      * @property _domRef
14471      * @private
14472      */
14473     _domRef: null,
14474
14475     /**
14476      * Internal typeof flag
14477      * @property __ygDragDrop
14478      * @private
14479      */
14480     __ygDragDrop: true,
14481
14482     /**
14483      * Set to true when horizontal contraints are applied
14484      * @property constrainX
14485      * @type boolean
14486      * @private
14487      */
14488     constrainX: false,
14489
14490     /**
14491      * Set to true when vertical contraints are applied
14492      * @property constrainY
14493      * @type boolean
14494      * @private
14495      */
14496     constrainY: false,
14497
14498     /**
14499      * The left constraint
14500      * @property minX
14501      * @type int
14502      * @private
14503      */
14504     minX: 0,
14505
14506     /**
14507      * The right constraint
14508      * @property maxX
14509      * @type int
14510      * @private
14511      */
14512     maxX: 0,
14513
14514     /**
14515      * The up constraint
14516      * @property minY
14517      * @type int
14518      * @type int
14519      * @private
14520      */
14521     minY: 0,
14522
14523     /**
14524      * The down constraint
14525      * @property maxY
14526      * @type int
14527      * @private
14528      */
14529     maxY: 0,
14530
14531     /**
14532      * Maintain offsets when we resetconstraints.  Set to true when you want
14533      * the position of the element relative to its parent to stay the same
14534      * when the page changes
14535      *
14536      * @property maintainOffset
14537      * @type boolean
14538      */
14539     maintainOffset: false,
14540
14541     /**
14542      * Array of pixel locations the element will snap to if we specified a
14543      * horizontal graduation/interval.  This array is generated automatically
14544      * when you define a tick interval.
14545      * @property xTicks
14546      * @type int[]
14547      */
14548     xTicks: null,
14549
14550     /**
14551      * Array of pixel locations the element will snap to if we specified a
14552      * vertical graduation/interval.  This array is generated automatically
14553      * when you define a tick interval.
14554      * @property yTicks
14555      * @type int[]
14556      */
14557     yTicks: null,
14558
14559     /**
14560      * By default the drag and drop instance will only respond to the primary
14561      * button click (left button for a right-handed mouse).  Set to true to
14562      * allow drag and drop to start with any mouse click that is propogated
14563      * by the browser
14564      * @property primaryButtonOnly
14565      * @type boolean
14566      */
14567     primaryButtonOnly: true,
14568
14569     /**
14570      * The availabe property is false until the linked dom element is accessible.
14571      * @property available
14572      * @type boolean
14573      */
14574     available: false,
14575
14576     /**
14577      * By default, drags can only be initiated if the mousedown occurs in the
14578      * region the linked element is.  This is done in part to work around a
14579      * bug in some browsers that mis-report the mousedown if the previous
14580      * mouseup happened outside of the window.  This property is set to true
14581      * if outer handles are defined.
14582      *
14583      * @property hasOuterHandles
14584      * @type boolean
14585      * @default false
14586      */
14587     hasOuterHandles: false,
14588
14589     /**
14590      * Code that executes immediately before the startDrag event
14591      * @method b4StartDrag
14592      * @private
14593      */
14594     b4StartDrag: function(x, y) { },
14595
14596     /**
14597      * Abstract method called after a drag/drop object is clicked
14598      * and the drag or mousedown time thresholds have beeen met.
14599      * @method startDrag
14600      * @param {int} X click location
14601      * @param {int} Y click location
14602      */
14603     startDrag: function(x, y) { /* override this */ },
14604
14605     /**
14606      * Code that executes immediately before the onDrag event
14607      * @method b4Drag
14608      * @private
14609      */
14610     b4Drag: function(e) { },
14611
14612     /**
14613      * Abstract method called during the onMouseMove event while dragging an
14614      * object.
14615      * @method onDrag
14616      * @param {Event} e the mousemove event
14617      */
14618     onDrag: function(e) { /* override this */ },
14619
14620     /**
14621      * Abstract method called when this element fist begins hovering over
14622      * another DragDrop obj
14623      * @method onDragEnter
14624      * @param {Event} e the mousemove event
14625      * @param {String|DragDrop[]} id In POINT mode, the element
14626      * id this is hovering over.  In INTERSECT mode, an array of one or more
14627      * dragdrop items being hovered over.
14628      */
14629     onDragEnter: function(e, id) { /* override this */ },
14630
14631     /**
14632      * Code that executes immediately before the onDragOver event
14633      * @method b4DragOver
14634      * @private
14635      */
14636     b4DragOver: function(e) { },
14637
14638     /**
14639      * Abstract method called when this element is hovering over another
14640      * DragDrop obj
14641      * @method onDragOver
14642      * @param {Event} e the mousemove event
14643      * @param {String|DragDrop[]} id In POINT mode, the element
14644      * id this is hovering over.  In INTERSECT mode, an array of dd items
14645      * being hovered over.
14646      */
14647     onDragOver: function(e, id) { /* override this */ },
14648
14649     /**
14650      * Code that executes immediately before the onDragOut event
14651      * @method b4DragOut
14652      * @private
14653      */
14654     b4DragOut: function(e) { },
14655
14656     /**
14657      * Abstract method called when we are no longer hovering over an element
14658      * @method onDragOut
14659      * @param {Event} e the mousemove event
14660      * @param {String|DragDrop[]} id In POINT mode, the element
14661      * id this was hovering over.  In INTERSECT mode, an array of dd items
14662      * that the mouse is no longer over.
14663      */
14664     onDragOut: function(e, id) { /* override this */ },
14665
14666     /**
14667      * Code that executes immediately before the onDragDrop event
14668      * @method b4DragDrop
14669      * @private
14670      */
14671     b4DragDrop: function(e) { },
14672
14673     /**
14674      * Abstract method called when this item is dropped on another DragDrop
14675      * obj
14676      * @method onDragDrop
14677      * @param {Event} e the mouseup event
14678      * @param {String|DragDrop[]} id In POINT mode, the element
14679      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14680      * was dropped on.
14681      */
14682     onDragDrop: function(e, id) { /* override this */ },
14683
14684     /**
14685      * Abstract method called when this item is dropped on an area with no
14686      * drop target
14687      * @method onInvalidDrop
14688      * @param {Event} e the mouseup event
14689      */
14690     onInvalidDrop: function(e) { /* override this */ },
14691
14692     /**
14693      * Code that executes immediately before the endDrag event
14694      * @method b4EndDrag
14695      * @private
14696      */
14697     b4EndDrag: function(e) { },
14698
14699     /**
14700      * Fired when we are done dragging the object
14701      * @method endDrag
14702      * @param {Event} e the mouseup event
14703      */
14704     endDrag: function(e) { /* override this */ },
14705
14706     /**
14707      * Code executed immediately before the onMouseDown event
14708      * @method b4MouseDown
14709      * @param {Event} e the mousedown event
14710      * @private
14711      */
14712     b4MouseDown: function(e) {  },
14713
14714     /**
14715      * Event handler that fires when a drag/drop obj gets a mousedown
14716      * @method onMouseDown
14717      * @param {Event} e the mousedown event
14718      */
14719     onMouseDown: function(e) { /* override this */ },
14720
14721     /**
14722      * Event handler that fires when a drag/drop obj gets a mouseup
14723      * @method onMouseUp
14724      * @param {Event} e the mouseup event
14725      */
14726     onMouseUp: function(e) { /* override this */ },
14727
14728     /**
14729      * Override the onAvailable method to do what is needed after the initial
14730      * position was determined.
14731      * @method onAvailable
14732      */
14733     onAvailable: function () {
14734     },
14735
14736     /*
14737      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14738      * @type Object
14739      */
14740     defaultPadding : {left:0, right:0, top:0, bottom:0},
14741
14742     /*
14743      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14744  *
14745  * Usage:
14746  <pre><code>
14747  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14748                 { dragElId: "existingProxyDiv" });
14749  dd.startDrag = function(){
14750      this.constrainTo("parent-id");
14751  };
14752  </code></pre>
14753  * Or you can initalize it using the {@link Roo.Element} object:
14754  <pre><code>
14755  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14756      startDrag : function(){
14757          this.constrainTo("parent-id");
14758      }
14759  });
14760  </code></pre>
14761      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14762      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14763      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14764      * an object containing the sides to pad. For example: {right:10, bottom:10}
14765      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14766      */
14767     constrainTo : function(constrainTo, pad, inContent){
14768         if(typeof pad == "number"){
14769             pad = {left: pad, right:pad, top:pad, bottom:pad};
14770         }
14771         pad = pad || this.defaultPadding;
14772         var b = Roo.get(this.getEl()).getBox();
14773         var ce = Roo.get(constrainTo);
14774         var s = ce.getScroll();
14775         var c, cd = ce.dom;
14776         if(cd == document.body){
14777             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14778         }else{
14779             xy = ce.getXY();
14780             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14781         }
14782
14783
14784         var topSpace = b.y - c.y;
14785         var leftSpace = b.x - c.x;
14786
14787         this.resetConstraints();
14788         this.setXConstraint(leftSpace - (pad.left||0), // left
14789                 c.width - leftSpace - b.width - (pad.right||0) //right
14790         );
14791         this.setYConstraint(topSpace - (pad.top||0), //top
14792                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14793         );
14794     },
14795
14796     /**
14797      * Returns a reference to the linked element
14798      * @method getEl
14799      * @return {HTMLElement} the html element
14800      */
14801     getEl: function() {
14802         if (!this._domRef) {
14803             this._domRef = Roo.getDom(this.id);
14804         }
14805
14806         return this._domRef;
14807     },
14808
14809     /**
14810      * Returns a reference to the actual element to drag.  By default this is
14811      * the same as the html element, but it can be assigned to another
14812      * element. An example of this can be found in Roo.dd.DDProxy
14813      * @method getDragEl
14814      * @return {HTMLElement} the html element
14815      */
14816     getDragEl: function() {
14817         return Roo.getDom(this.dragElId);
14818     },
14819
14820     /**
14821      * Sets up the DragDrop object.  Must be called in the constructor of any
14822      * Roo.dd.DragDrop subclass
14823      * @method init
14824      * @param id the id of the linked element
14825      * @param {String} sGroup the group of related items
14826      * @param {object} config configuration attributes
14827      */
14828     init: function(id, sGroup, config) {
14829         this.initTarget(id, sGroup, config);
14830         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14831         // Event.on(this.id, "selectstart", Event.preventDefault);
14832     },
14833
14834     /**
14835      * Initializes Targeting functionality only... the object does not
14836      * get a mousedown handler.
14837      * @method initTarget
14838      * @param id the id of the linked element
14839      * @param {String} sGroup the group of related items
14840      * @param {object} config configuration attributes
14841      */
14842     initTarget: function(id, sGroup, config) {
14843
14844         // configuration attributes
14845         this.config = config || {};
14846
14847         // create a local reference to the drag and drop manager
14848         this.DDM = Roo.dd.DDM;
14849         // initialize the groups array
14850         this.groups = {};
14851
14852         // assume that we have an element reference instead of an id if the
14853         // parameter is not a string
14854         if (typeof id !== "string") {
14855             id = Roo.id(id);
14856         }
14857
14858         // set the id
14859         this.id = id;
14860
14861         // add to an interaction group
14862         this.addToGroup((sGroup) ? sGroup : "default");
14863
14864         // We don't want to register this as the handle with the manager
14865         // so we just set the id rather than calling the setter.
14866         this.handleElId = id;
14867
14868         // the linked element is the element that gets dragged by default
14869         this.setDragElId(id);
14870
14871         // by default, clicked anchors will not start drag operations.
14872         this.invalidHandleTypes = { A: "A" };
14873         this.invalidHandleIds = {};
14874         this.invalidHandleClasses = [];
14875
14876         this.applyConfig();
14877
14878         this.handleOnAvailable();
14879     },
14880
14881     /**
14882      * Applies the configuration parameters that were passed into the constructor.
14883      * This is supposed to happen at each level through the inheritance chain.  So
14884      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14885      * DragDrop in order to get all of the parameters that are available in
14886      * each object.
14887      * @method applyConfig
14888      */
14889     applyConfig: function() {
14890
14891         // configurable properties:
14892         //    padding, isTarget, maintainOffset, primaryButtonOnly
14893         this.padding           = this.config.padding || [0, 0, 0, 0];
14894         this.isTarget          = (this.config.isTarget !== false);
14895         this.maintainOffset    = (this.config.maintainOffset);
14896         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14897
14898     },
14899
14900     /**
14901      * Executed when the linked element is available
14902      * @method handleOnAvailable
14903      * @private
14904      */
14905     handleOnAvailable: function() {
14906         this.available = true;
14907         this.resetConstraints();
14908         this.onAvailable();
14909     },
14910
14911      /**
14912      * Configures the padding for the target zone in px.  Effectively expands
14913      * (or reduces) the virtual object size for targeting calculations.
14914      * Supports css-style shorthand; if only one parameter is passed, all sides
14915      * will have that padding, and if only two are passed, the top and bottom
14916      * will have the first param, the left and right the second.
14917      * @method setPadding
14918      * @param {int} iTop    Top pad
14919      * @param {int} iRight  Right pad
14920      * @param {int} iBot    Bot pad
14921      * @param {int} iLeft   Left pad
14922      */
14923     setPadding: function(iTop, iRight, iBot, iLeft) {
14924         // this.padding = [iLeft, iRight, iTop, iBot];
14925         if (!iRight && 0 !== iRight) {
14926             this.padding = [iTop, iTop, iTop, iTop];
14927         } else if (!iBot && 0 !== iBot) {
14928             this.padding = [iTop, iRight, iTop, iRight];
14929         } else {
14930             this.padding = [iTop, iRight, iBot, iLeft];
14931         }
14932     },
14933
14934     /**
14935      * Stores the initial placement of the linked element.
14936      * @method setInitialPosition
14937      * @param {int} diffX   the X offset, default 0
14938      * @param {int} diffY   the Y offset, default 0
14939      */
14940     setInitPosition: function(diffX, diffY) {
14941         var el = this.getEl();
14942
14943         if (!this.DDM.verifyEl(el)) {
14944             return;
14945         }
14946
14947         var dx = diffX || 0;
14948         var dy = diffY || 0;
14949
14950         var p = Dom.getXY( el );
14951
14952         this.initPageX = p[0] - dx;
14953         this.initPageY = p[1] - dy;
14954
14955         this.lastPageX = p[0];
14956         this.lastPageY = p[1];
14957
14958
14959         this.setStartPosition(p);
14960     },
14961
14962     /**
14963      * Sets the start position of the element.  This is set when the obj
14964      * is initialized, the reset when a drag is started.
14965      * @method setStartPosition
14966      * @param pos current position (from previous lookup)
14967      * @private
14968      */
14969     setStartPosition: function(pos) {
14970         var p = pos || Dom.getXY( this.getEl() );
14971         this.deltaSetXY = null;
14972
14973         this.startPageX = p[0];
14974         this.startPageY = p[1];
14975     },
14976
14977     /**
14978      * Add this instance to a group of related drag/drop objects.  All
14979      * instances belong to at least one group, and can belong to as many
14980      * groups as needed.
14981      * @method addToGroup
14982      * @param sGroup {string} the name of the group
14983      */
14984     addToGroup: function(sGroup) {
14985         this.groups[sGroup] = true;
14986         this.DDM.regDragDrop(this, sGroup);
14987     },
14988
14989     /**
14990      * Remove's this instance from the supplied interaction group
14991      * @method removeFromGroup
14992      * @param {string}  sGroup  The group to drop
14993      */
14994     removeFromGroup: function(sGroup) {
14995         if (this.groups[sGroup]) {
14996             delete this.groups[sGroup];
14997         }
14998
14999         this.DDM.removeDDFromGroup(this, sGroup);
15000     },
15001
15002     /**
15003      * Allows you to specify that an element other than the linked element
15004      * will be moved with the cursor during a drag
15005      * @method setDragElId
15006      * @param id {string} the id of the element that will be used to initiate the drag
15007      */
15008     setDragElId: function(id) {
15009         this.dragElId = id;
15010     },
15011
15012     /**
15013      * Allows you to specify a child of the linked element that should be
15014      * used to initiate the drag operation.  An example of this would be if
15015      * you have a content div with text and links.  Clicking anywhere in the
15016      * content area would normally start the drag operation.  Use this method
15017      * to specify that an element inside of the content div is the element
15018      * that starts the drag operation.
15019      * @method setHandleElId
15020      * @param id {string} the id of the element that will be used to
15021      * initiate the drag.
15022      */
15023     setHandleElId: function(id) {
15024         if (typeof id !== "string") {
15025             id = Roo.id(id);
15026         }
15027         this.handleElId = id;
15028         this.DDM.regHandle(this.id, id);
15029     },
15030
15031     /**
15032      * Allows you to set an element outside of the linked element as a drag
15033      * handle
15034      * @method setOuterHandleElId
15035      * @param id the id of the element that will be used to initiate the drag
15036      */
15037     setOuterHandleElId: function(id) {
15038         if (typeof id !== "string") {
15039             id = Roo.id(id);
15040         }
15041         Event.on(id, "mousedown",
15042                 this.handleMouseDown, this);
15043         this.setHandleElId(id);
15044
15045         this.hasOuterHandles = true;
15046     },
15047
15048     /**
15049      * Remove all drag and drop hooks for this element
15050      * @method unreg
15051      */
15052     unreg: function() {
15053         Event.un(this.id, "mousedown",
15054                 this.handleMouseDown);
15055         this._domRef = null;
15056         this.DDM._remove(this);
15057     },
15058
15059     destroy : function(){
15060         this.unreg();
15061     },
15062
15063     /**
15064      * Returns true if this instance is locked, or the drag drop mgr is locked
15065      * (meaning that all drag/drop is disabled on the page.)
15066      * @method isLocked
15067      * @return {boolean} true if this obj or all drag/drop is locked, else
15068      * false
15069      */
15070     isLocked: function() {
15071         return (this.DDM.isLocked() || this.locked);
15072     },
15073
15074     /**
15075      * Fired when this object is clicked
15076      * @method handleMouseDown
15077      * @param {Event} e
15078      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15079      * @private
15080      */
15081     handleMouseDown: function(e, oDD){
15082         if (this.primaryButtonOnly && e.button != 0) {
15083             return;
15084         }
15085
15086         if (this.isLocked()) {
15087             return;
15088         }
15089
15090         this.DDM.refreshCache(this.groups);
15091
15092         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15093         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15094         } else {
15095             if (this.clickValidator(e)) {
15096
15097                 // set the initial element position
15098                 this.setStartPosition();
15099
15100
15101                 this.b4MouseDown(e);
15102                 this.onMouseDown(e);
15103
15104                 this.DDM.handleMouseDown(e, this);
15105
15106                 this.DDM.stopEvent(e);
15107             } else {
15108
15109
15110             }
15111         }
15112     },
15113
15114     clickValidator: function(e) {
15115         var target = e.getTarget();
15116         return ( this.isValidHandleChild(target) &&
15117                     (this.id == this.handleElId ||
15118                         this.DDM.handleWasClicked(target, this.id)) );
15119     },
15120
15121     /**
15122      * Allows you to specify a tag name that should not start a drag operation
15123      * when clicked.  This is designed to facilitate embedding links within a
15124      * drag handle that do something other than start the drag.
15125      * @method addInvalidHandleType
15126      * @param {string} tagName the type of element to exclude
15127      */
15128     addInvalidHandleType: function(tagName) {
15129         var type = tagName.toUpperCase();
15130         this.invalidHandleTypes[type] = type;
15131     },
15132
15133     /**
15134      * Lets you to specify an element id for a child of a drag handle
15135      * that should not initiate a drag
15136      * @method addInvalidHandleId
15137      * @param {string} id the element id of the element you wish to ignore
15138      */
15139     addInvalidHandleId: function(id) {
15140         if (typeof id !== "string") {
15141             id = Roo.id(id);
15142         }
15143         this.invalidHandleIds[id] = id;
15144     },
15145
15146     /**
15147      * Lets you specify a css class of elements that will not initiate a drag
15148      * @method addInvalidHandleClass
15149      * @param {string} cssClass the class of the elements you wish to ignore
15150      */
15151     addInvalidHandleClass: function(cssClass) {
15152         this.invalidHandleClasses.push(cssClass);
15153     },
15154
15155     /**
15156      * Unsets an excluded tag name set by addInvalidHandleType
15157      * @method removeInvalidHandleType
15158      * @param {string} tagName the type of element to unexclude
15159      */
15160     removeInvalidHandleType: function(tagName) {
15161         var type = tagName.toUpperCase();
15162         // this.invalidHandleTypes[type] = null;
15163         delete this.invalidHandleTypes[type];
15164     },
15165
15166     /**
15167      * Unsets an invalid handle id
15168      * @method removeInvalidHandleId
15169      * @param {string} id the id of the element to re-enable
15170      */
15171     removeInvalidHandleId: function(id) {
15172         if (typeof id !== "string") {
15173             id = Roo.id(id);
15174         }
15175         delete this.invalidHandleIds[id];
15176     },
15177
15178     /**
15179      * Unsets an invalid css class
15180      * @method removeInvalidHandleClass
15181      * @param {string} cssClass the class of the element(s) you wish to
15182      * re-enable
15183      */
15184     removeInvalidHandleClass: function(cssClass) {
15185         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15186             if (this.invalidHandleClasses[i] == cssClass) {
15187                 delete this.invalidHandleClasses[i];
15188             }
15189         }
15190     },
15191
15192     /**
15193      * Checks the tag exclusion list to see if this click should be ignored
15194      * @method isValidHandleChild
15195      * @param {HTMLElement} node the HTMLElement to evaluate
15196      * @return {boolean} true if this is a valid tag type, false if not
15197      */
15198     isValidHandleChild: function(node) {
15199
15200         var valid = true;
15201         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15202         var nodeName;
15203         try {
15204             nodeName = node.nodeName.toUpperCase();
15205         } catch(e) {
15206             nodeName = node.nodeName;
15207         }
15208         valid = valid && !this.invalidHandleTypes[nodeName];
15209         valid = valid && !this.invalidHandleIds[node.id];
15210
15211         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15212             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15213         }
15214
15215
15216         return valid;
15217
15218     },
15219
15220     /**
15221      * Create the array of horizontal tick marks if an interval was specified
15222      * in setXConstraint().
15223      * @method setXTicks
15224      * @private
15225      */
15226     setXTicks: function(iStartX, iTickSize) {
15227         this.xTicks = [];
15228         this.xTickSize = iTickSize;
15229
15230         var tickMap = {};
15231
15232         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15233             if (!tickMap[i]) {
15234                 this.xTicks[this.xTicks.length] = i;
15235                 tickMap[i] = true;
15236             }
15237         }
15238
15239         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15240             if (!tickMap[i]) {
15241                 this.xTicks[this.xTicks.length] = i;
15242                 tickMap[i] = true;
15243             }
15244         }
15245
15246         this.xTicks.sort(this.DDM.numericSort) ;
15247     },
15248
15249     /**
15250      * Create the array of vertical tick marks if an interval was specified in
15251      * setYConstraint().
15252      * @method setYTicks
15253      * @private
15254      */
15255     setYTicks: function(iStartY, iTickSize) {
15256         this.yTicks = [];
15257         this.yTickSize = iTickSize;
15258
15259         var tickMap = {};
15260
15261         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15262             if (!tickMap[i]) {
15263                 this.yTicks[this.yTicks.length] = i;
15264                 tickMap[i] = true;
15265             }
15266         }
15267
15268         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15269             if (!tickMap[i]) {
15270                 this.yTicks[this.yTicks.length] = i;
15271                 tickMap[i] = true;
15272             }
15273         }
15274
15275         this.yTicks.sort(this.DDM.numericSort) ;
15276     },
15277
15278     /**
15279      * By default, the element can be dragged any place on the screen.  Use
15280      * this method to limit the horizontal travel of the element.  Pass in
15281      * 0,0 for the parameters if you want to lock the drag to the y axis.
15282      * @method setXConstraint
15283      * @param {int} iLeft the number of pixels the element can move to the left
15284      * @param {int} iRight the number of pixels the element can move to the
15285      * right
15286      * @param {int} iTickSize optional parameter for specifying that the
15287      * element
15288      * should move iTickSize pixels at a time.
15289      */
15290     setXConstraint: function(iLeft, iRight, iTickSize) {
15291         this.leftConstraint = iLeft;
15292         this.rightConstraint = iRight;
15293
15294         this.minX = this.initPageX - iLeft;
15295         this.maxX = this.initPageX + iRight;
15296         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15297
15298         this.constrainX = true;
15299     },
15300
15301     /**
15302      * Clears any constraints applied to this instance.  Also clears ticks
15303      * since they can't exist independent of a constraint at this time.
15304      * @method clearConstraints
15305      */
15306     clearConstraints: function() {
15307         this.constrainX = false;
15308         this.constrainY = false;
15309         this.clearTicks();
15310     },
15311
15312     /**
15313      * Clears any tick interval defined for this instance
15314      * @method clearTicks
15315      */
15316     clearTicks: function() {
15317         this.xTicks = null;
15318         this.yTicks = null;
15319         this.xTickSize = 0;
15320         this.yTickSize = 0;
15321     },
15322
15323     /**
15324      * By default, the element can be dragged any place on the screen.  Set
15325      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15326      * parameters if you want to lock the drag to the x axis.
15327      * @method setYConstraint
15328      * @param {int} iUp the number of pixels the element can move up
15329      * @param {int} iDown the number of pixels the element can move down
15330      * @param {int} iTickSize optional parameter for specifying that the
15331      * element should move iTickSize pixels at a time.
15332      */
15333     setYConstraint: function(iUp, iDown, iTickSize) {
15334         this.topConstraint = iUp;
15335         this.bottomConstraint = iDown;
15336
15337         this.minY = this.initPageY - iUp;
15338         this.maxY = this.initPageY + iDown;
15339         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15340
15341         this.constrainY = true;
15342
15343     },
15344
15345     /**
15346      * resetConstraints must be called if you manually reposition a dd element.
15347      * @method resetConstraints
15348      * @param {boolean} maintainOffset
15349      */
15350     resetConstraints: function() {
15351
15352
15353         // Maintain offsets if necessary
15354         if (this.initPageX || this.initPageX === 0) {
15355             // figure out how much this thing has moved
15356             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15357             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15358
15359             this.setInitPosition(dx, dy);
15360
15361         // This is the first time we have detected the element's position
15362         } else {
15363             this.setInitPosition();
15364         }
15365
15366         if (this.constrainX) {
15367             this.setXConstraint( this.leftConstraint,
15368                                  this.rightConstraint,
15369                                  this.xTickSize        );
15370         }
15371
15372         if (this.constrainY) {
15373             this.setYConstraint( this.topConstraint,
15374                                  this.bottomConstraint,
15375                                  this.yTickSize         );
15376         }
15377     },
15378
15379     /**
15380      * Normally the drag element is moved pixel by pixel, but we can specify
15381      * that it move a number of pixels at a time.  This method resolves the
15382      * location when we have it set up like this.
15383      * @method getTick
15384      * @param {int} val where we want to place the object
15385      * @param {int[]} tickArray sorted array of valid points
15386      * @return {int} the closest tick
15387      * @private
15388      */
15389     getTick: function(val, tickArray) {
15390
15391         if (!tickArray) {
15392             // If tick interval is not defined, it is effectively 1 pixel,
15393             // so we return the value passed to us.
15394             return val;
15395         } else if (tickArray[0] >= val) {
15396             // The value is lower than the first tick, so we return the first
15397             // tick.
15398             return tickArray[0];
15399         } else {
15400             for (var i=0, len=tickArray.length; i<len; ++i) {
15401                 var next = i + 1;
15402                 if (tickArray[next] && tickArray[next] >= val) {
15403                     var diff1 = val - tickArray[i];
15404                     var diff2 = tickArray[next] - val;
15405                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15406                 }
15407             }
15408
15409             // The value is larger than the last tick, so we return the last
15410             // tick.
15411             return tickArray[tickArray.length - 1];
15412         }
15413     },
15414
15415     /**
15416      * toString method
15417      * @method toString
15418      * @return {string} string representation of the dd obj
15419      */
15420     toString: function() {
15421         return ("DragDrop " + this.id);
15422     }
15423
15424 };
15425
15426 })();
15427 /*
15428  * Based on:
15429  * Ext JS Library 1.1.1
15430  * Copyright(c) 2006-2007, Ext JS, LLC.
15431  *
15432  * Originally Released Under LGPL - original licence link has changed is not relivant.
15433  *
15434  * Fork - LGPL
15435  * <script type="text/javascript">
15436  */
15437
15438
15439 /**
15440  * The drag and drop utility provides a framework for building drag and drop
15441  * applications.  In addition to enabling drag and drop for specific elements,
15442  * the drag and drop elements are tracked by the manager class, and the
15443  * interactions between the various elements are tracked during the drag and
15444  * the implementing code is notified about these important moments.
15445  */
15446
15447 // Only load the library once.  Rewriting the manager class would orphan
15448 // existing drag and drop instances.
15449 if (!Roo.dd.DragDropMgr) {
15450
15451 /**
15452  * @class Roo.dd.DragDropMgr
15453  * DragDropMgr is a singleton that tracks the element interaction for
15454  * all DragDrop items in the window.  Generally, you will not call
15455  * this class directly, but it does have helper methods that could
15456  * be useful in your DragDrop implementations.
15457  * @singleton
15458  */
15459 Roo.dd.DragDropMgr = function() {
15460
15461     var Event = Roo.EventManager;
15462
15463     return {
15464
15465         /**
15466          * Two dimensional Array of registered DragDrop objects.  The first
15467          * dimension is the DragDrop item group, the second the DragDrop
15468          * object.
15469          * @property ids
15470          * @type {string: string}
15471          * @private
15472          * @static
15473          */
15474         ids: {},
15475
15476         /**
15477          * Array of element ids defined as drag handles.  Used to determine
15478          * if the element that generated the mousedown event is actually the
15479          * handle and not the html element itself.
15480          * @property handleIds
15481          * @type {string: string}
15482          * @private
15483          * @static
15484          */
15485         handleIds: {},
15486
15487         /**
15488          * the DragDrop object that is currently being dragged
15489          * @property dragCurrent
15490          * @type DragDrop
15491          * @private
15492          * @static
15493          **/
15494         dragCurrent: null,
15495
15496         /**
15497          * the DragDrop object(s) that are being hovered over
15498          * @property dragOvers
15499          * @type Array
15500          * @private
15501          * @static
15502          */
15503         dragOvers: {},
15504
15505         /**
15506          * the X distance between the cursor and the object being dragged
15507          * @property deltaX
15508          * @type int
15509          * @private
15510          * @static
15511          */
15512         deltaX: 0,
15513
15514         /**
15515          * the Y distance between the cursor and the object being dragged
15516          * @property deltaY
15517          * @type int
15518          * @private
15519          * @static
15520          */
15521         deltaY: 0,
15522
15523         /**
15524          * Flag to determine if we should prevent the default behavior of the
15525          * events we define. By default this is true, but this can be set to
15526          * false if you need the default behavior (not recommended)
15527          * @property preventDefault
15528          * @type boolean
15529          * @static
15530          */
15531         preventDefault: true,
15532
15533         /**
15534          * Flag to determine if we should stop the propagation of the events
15535          * we generate. This is true by default but you may want to set it to
15536          * false if the html element contains other features that require the
15537          * mouse click.
15538          * @property stopPropagation
15539          * @type boolean
15540          * @static
15541          */
15542         stopPropagation: true,
15543
15544         /**
15545          * Internal flag that is set to true when drag and drop has been
15546          * intialized
15547          * @property initialized
15548          * @private
15549          * @static
15550          */
15551         initalized: false,
15552
15553         /**
15554          * All drag and drop can be disabled.
15555          * @property locked
15556          * @private
15557          * @static
15558          */
15559         locked: false,
15560
15561         /**
15562          * Called the first time an element is registered.
15563          * @method init
15564          * @private
15565          * @static
15566          */
15567         init: function() {
15568             this.initialized = true;
15569         },
15570
15571         /**
15572          * In point mode, drag and drop interaction is defined by the
15573          * location of the cursor during the drag/drop
15574          * @property POINT
15575          * @type int
15576          * @static
15577          */
15578         POINT: 0,
15579
15580         /**
15581          * In intersect mode, drag and drop interactio nis defined by the
15582          * overlap of two or more drag and drop objects.
15583          * @property INTERSECT
15584          * @type int
15585          * @static
15586          */
15587         INTERSECT: 1,
15588
15589         /**
15590          * The current drag and drop mode.  Default: POINT
15591          * @property mode
15592          * @type int
15593          * @static
15594          */
15595         mode: 0,
15596
15597         /**
15598          * Runs method on all drag and drop objects
15599          * @method _execOnAll
15600          * @private
15601          * @static
15602          */
15603         _execOnAll: function(sMethod, args) {
15604             for (var i in this.ids) {
15605                 for (var j in this.ids[i]) {
15606                     var oDD = this.ids[i][j];
15607                     if (! this.isTypeOfDD(oDD)) {
15608                         continue;
15609                     }
15610                     oDD[sMethod].apply(oDD, args);
15611                 }
15612             }
15613         },
15614
15615         /**
15616          * Drag and drop initialization.  Sets up the global event handlers
15617          * @method _onLoad
15618          * @private
15619          * @static
15620          */
15621         _onLoad: function() {
15622
15623             this.init();
15624
15625
15626             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15627             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15628             Event.on(window,   "unload",    this._onUnload, this, true);
15629             Event.on(window,   "resize",    this._onResize, this, true);
15630             // Event.on(window,   "mouseout",    this._test);
15631
15632         },
15633
15634         /**
15635          * Reset constraints on all drag and drop objs
15636          * @method _onResize
15637          * @private
15638          * @static
15639          */
15640         _onResize: function(e) {
15641             this._execOnAll("resetConstraints", []);
15642         },
15643
15644         /**
15645          * Lock all drag and drop functionality
15646          * @method lock
15647          * @static
15648          */
15649         lock: function() { this.locked = true; },
15650
15651         /**
15652          * Unlock all drag and drop functionality
15653          * @method unlock
15654          * @static
15655          */
15656         unlock: function() { this.locked = false; },
15657
15658         /**
15659          * Is drag and drop locked?
15660          * @method isLocked
15661          * @return {boolean} True if drag and drop is locked, false otherwise.
15662          * @static
15663          */
15664         isLocked: function() { return this.locked; },
15665
15666         /**
15667          * Location cache that is set for all drag drop objects when a drag is
15668          * initiated, cleared when the drag is finished.
15669          * @property locationCache
15670          * @private
15671          * @static
15672          */
15673         locationCache: {},
15674
15675         /**
15676          * Set useCache to false if you want to force object the lookup of each
15677          * drag and drop linked element constantly during a drag.
15678          * @property useCache
15679          * @type boolean
15680          * @static
15681          */
15682         useCache: true,
15683
15684         /**
15685          * The number of pixels that the mouse needs to move after the
15686          * mousedown before the drag is initiated.  Default=3;
15687          * @property clickPixelThresh
15688          * @type int
15689          * @static
15690          */
15691         clickPixelThresh: 3,
15692
15693         /**
15694          * The number of milliseconds after the mousedown event to initiate the
15695          * drag if we don't get a mouseup event. Default=1000
15696          * @property clickTimeThresh
15697          * @type int
15698          * @static
15699          */
15700         clickTimeThresh: 350,
15701
15702         /**
15703          * Flag that indicates that either the drag pixel threshold or the
15704          * mousdown time threshold has been met
15705          * @property dragThreshMet
15706          * @type boolean
15707          * @private
15708          * @static
15709          */
15710         dragThreshMet: false,
15711
15712         /**
15713          * Timeout used for the click time threshold
15714          * @property clickTimeout
15715          * @type Object
15716          * @private
15717          * @static
15718          */
15719         clickTimeout: null,
15720
15721         /**
15722          * The X position of the mousedown event stored for later use when a
15723          * drag threshold is met.
15724          * @property startX
15725          * @type int
15726          * @private
15727          * @static
15728          */
15729         startX: 0,
15730
15731         /**
15732          * The Y position of the mousedown event stored for later use when a
15733          * drag threshold is met.
15734          * @property startY
15735          * @type int
15736          * @private
15737          * @static
15738          */
15739         startY: 0,
15740
15741         /**
15742          * Each DragDrop instance must be registered with the DragDropMgr.
15743          * This is executed in DragDrop.init()
15744          * @method regDragDrop
15745          * @param {DragDrop} oDD the DragDrop object to register
15746          * @param {String} sGroup the name of the group this element belongs to
15747          * @static
15748          */
15749         regDragDrop: function(oDD, sGroup) {
15750             if (!this.initialized) { this.init(); }
15751
15752             if (!this.ids[sGroup]) {
15753                 this.ids[sGroup] = {};
15754             }
15755             this.ids[sGroup][oDD.id] = oDD;
15756         },
15757
15758         /**
15759          * Removes the supplied dd instance from the supplied group. Executed
15760          * by DragDrop.removeFromGroup, so don't call this function directly.
15761          * @method removeDDFromGroup
15762          * @private
15763          * @static
15764          */
15765         removeDDFromGroup: function(oDD, sGroup) {
15766             if (!this.ids[sGroup]) {
15767                 this.ids[sGroup] = {};
15768             }
15769
15770             var obj = this.ids[sGroup];
15771             if (obj && obj[oDD.id]) {
15772                 delete obj[oDD.id];
15773             }
15774         },
15775
15776         /**
15777          * Unregisters a drag and drop item.  This is executed in
15778          * DragDrop.unreg, use that method instead of calling this directly.
15779          * @method _remove
15780          * @private
15781          * @static
15782          */
15783         _remove: function(oDD) {
15784             for (var g in oDD.groups) {
15785                 if (g && this.ids[g][oDD.id]) {
15786                     delete this.ids[g][oDD.id];
15787                 }
15788             }
15789             delete this.handleIds[oDD.id];
15790         },
15791
15792         /**
15793          * Each DragDrop handle element must be registered.  This is done
15794          * automatically when executing DragDrop.setHandleElId()
15795          * @method regHandle
15796          * @param {String} sDDId the DragDrop id this element is a handle for
15797          * @param {String} sHandleId the id of the element that is the drag
15798          * handle
15799          * @static
15800          */
15801         regHandle: function(sDDId, sHandleId) {
15802             if (!this.handleIds[sDDId]) {
15803                 this.handleIds[sDDId] = {};
15804             }
15805             this.handleIds[sDDId][sHandleId] = sHandleId;
15806         },
15807
15808         /**
15809          * Utility function to determine if a given element has been
15810          * registered as a drag drop item.
15811          * @method isDragDrop
15812          * @param {String} id the element id to check
15813          * @return {boolean} true if this element is a DragDrop item,
15814          * false otherwise
15815          * @static
15816          */
15817         isDragDrop: function(id) {
15818             return ( this.getDDById(id) ) ? true : false;
15819         },
15820
15821         /**
15822          * Returns the drag and drop instances that are in all groups the
15823          * passed in instance belongs to.
15824          * @method getRelated
15825          * @param {DragDrop} p_oDD the obj to get related data for
15826          * @param {boolean} bTargetsOnly if true, only return targetable objs
15827          * @return {DragDrop[]} the related instances
15828          * @static
15829          */
15830         getRelated: function(p_oDD, bTargetsOnly) {
15831             var oDDs = [];
15832             for (var i in p_oDD.groups) {
15833                 for (j in this.ids[i]) {
15834                     var dd = this.ids[i][j];
15835                     if (! this.isTypeOfDD(dd)) {
15836                         continue;
15837                     }
15838                     if (!bTargetsOnly || dd.isTarget) {
15839                         oDDs[oDDs.length] = dd;
15840                     }
15841                 }
15842             }
15843
15844             return oDDs;
15845         },
15846
15847         /**
15848          * Returns true if the specified dd target is a legal target for
15849          * the specifice drag obj
15850          * @method isLegalTarget
15851          * @param {DragDrop} the drag obj
15852          * @param {DragDrop} the target
15853          * @return {boolean} true if the target is a legal target for the
15854          * dd obj
15855          * @static
15856          */
15857         isLegalTarget: function (oDD, oTargetDD) {
15858             var targets = this.getRelated(oDD, true);
15859             for (var i=0, len=targets.length;i<len;++i) {
15860                 if (targets[i].id == oTargetDD.id) {
15861                     return true;
15862                 }
15863             }
15864
15865             return false;
15866         },
15867
15868         /**
15869          * My goal is to be able to transparently determine if an object is
15870          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15871          * returns "object", oDD.constructor.toString() always returns
15872          * "DragDrop" and not the name of the subclass.  So for now it just
15873          * evaluates a well-known variable in DragDrop.
15874          * @method isTypeOfDD
15875          * @param {Object} the object to evaluate
15876          * @return {boolean} true if typeof oDD = DragDrop
15877          * @static
15878          */
15879         isTypeOfDD: function (oDD) {
15880             return (oDD && oDD.__ygDragDrop);
15881         },
15882
15883         /**
15884          * Utility function to determine if a given element has been
15885          * registered as a drag drop handle for the given Drag Drop object.
15886          * @method isHandle
15887          * @param {String} id the element id to check
15888          * @return {boolean} true if this element is a DragDrop handle, false
15889          * otherwise
15890          * @static
15891          */
15892         isHandle: function(sDDId, sHandleId) {
15893             return ( this.handleIds[sDDId] &&
15894                             this.handleIds[sDDId][sHandleId] );
15895         },
15896
15897         /**
15898          * Returns the DragDrop instance for a given id
15899          * @method getDDById
15900          * @param {String} id the id of the DragDrop object
15901          * @return {DragDrop} the drag drop object, null if it is not found
15902          * @static
15903          */
15904         getDDById: function(id) {
15905             for (var i in this.ids) {
15906                 if (this.ids[i][id]) {
15907                     return this.ids[i][id];
15908                 }
15909             }
15910             return null;
15911         },
15912
15913         /**
15914          * Fired after a registered DragDrop object gets the mousedown event.
15915          * Sets up the events required to track the object being dragged
15916          * @method handleMouseDown
15917          * @param {Event} e the event
15918          * @param oDD the DragDrop object being dragged
15919          * @private
15920          * @static
15921          */
15922         handleMouseDown: function(e, oDD) {
15923             if(Roo.QuickTips){
15924                 Roo.QuickTips.disable();
15925             }
15926             this.currentTarget = e.getTarget();
15927
15928             this.dragCurrent = oDD;
15929
15930             var el = oDD.getEl();
15931
15932             // track start position
15933             this.startX = e.getPageX();
15934             this.startY = e.getPageY();
15935
15936             this.deltaX = this.startX - el.offsetLeft;
15937             this.deltaY = this.startY - el.offsetTop;
15938
15939             this.dragThreshMet = false;
15940
15941             this.clickTimeout = setTimeout(
15942                     function() {
15943                         var DDM = Roo.dd.DDM;
15944                         DDM.startDrag(DDM.startX, DDM.startY);
15945                     },
15946                     this.clickTimeThresh );
15947         },
15948
15949         /**
15950          * Fired when either the drag pixel threshol or the mousedown hold
15951          * time threshold has been met.
15952          * @method startDrag
15953          * @param x {int} the X position of the original mousedown
15954          * @param y {int} the Y position of the original mousedown
15955          * @static
15956          */
15957         startDrag: function(x, y) {
15958             clearTimeout(this.clickTimeout);
15959             if (this.dragCurrent) {
15960                 this.dragCurrent.b4StartDrag(x, y);
15961                 this.dragCurrent.startDrag(x, y);
15962             }
15963             this.dragThreshMet = true;
15964         },
15965
15966         /**
15967          * Internal function to handle the mouseup event.  Will be invoked
15968          * from the context of the document.
15969          * @method handleMouseUp
15970          * @param {Event} e the event
15971          * @private
15972          * @static
15973          */
15974         handleMouseUp: function(e) {
15975
15976             if(Roo.QuickTips){
15977                 Roo.QuickTips.enable();
15978             }
15979             if (! this.dragCurrent) {
15980                 return;
15981             }
15982
15983             clearTimeout(this.clickTimeout);
15984
15985             if (this.dragThreshMet) {
15986                 this.fireEvents(e, true);
15987             } else {
15988             }
15989
15990             this.stopDrag(e);
15991
15992             this.stopEvent(e);
15993         },
15994
15995         /**
15996          * Utility to stop event propagation and event default, if these
15997          * features are turned on.
15998          * @method stopEvent
15999          * @param {Event} e the event as returned by this.getEvent()
16000          * @static
16001          */
16002         stopEvent: function(e){
16003             if(this.stopPropagation) {
16004                 e.stopPropagation();
16005             }
16006
16007             if (this.preventDefault) {
16008                 e.preventDefault();
16009             }
16010         },
16011
16012         /**
16013          * Internal function to clean up event handlers after the drag
16014          * operation is complete
16015          * @method stopDrag
16016          * @param {Event} e the event
16017          * @private
16018          * @static
16019          */
16020         stopDrag: function(e) {
16021             // Fire the drag end event for the item that was dragged
16022             if (this.dragCurrent) {
16023                 if (this.dragThreshMet) {
16024                     this.dragCurrent.b4EndDrag(e);
16025                     this.dragCurrent.endDrag(e);
16026                 }
16027
16028                 this.dragCurrent.onMouseUp(e);
16029             }
16030
16031             this.dragCurrent = null;
16032             this.dragOvers = {};
16033         },
16034
16035         /**
16036          * Internal function to handle the mousemove event.  Will be invoked
16037          * from the context of the html element.
16038          *
16039          * @TODO figure out what we can do about mouse events lost when the
16040          * user drags objects beyond the window boundary.  Currently we can
16041          * detect this in internet explorer by verifying that the mouse is
16042          * down during the mousemove event.  Firefox doesn't give us the
16043          * button state on the mousemove event.
16044          * @method handleMouseMove
16045          * @param {Event} e the event
16046          * @private
16047          * @static
16048          */
16049         handleMouseMove: function(e) {
16050             if (! this.dragCurrent) {
16051                 return true;
16052             }
16053
16054             // var button = e.which || e.button;
16055
16056             // check for IE mouseup outside of page boundary
16057             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16058                 this.stopEvent(e);
16059                 return this.handleMouseUp(e);
16060             }
16061
16062             if (!this.dragThreshMet) {
16063                 var diffX = Math.abs(this.startX - e.getPageX());
16064                 var diffY = Math.abs(this.startY - e.getPageY());
16065                 if (diffX > this.clickPixelThresh ||
16066                             diffY > this.clickPixelThresh) {
16067                     this.startDrag(this.startX, this.startY);
16068                 }
16069             }
16070
16071             if (this.dragThreshMet) {
16072                 this.dragCurrent.b4Drag(e);
16073                 this.dragCurrent.onDrag(e);
16074                 if(!this.dragCurrent.moveOnly){
16075                     this.fireEvents(e, false);
16076                 }
16077             }
16078
16079             this.stopEvent(e);
16080
16081             return true;
16082         },
16083
16084         /**
16085          * Iterates over all of the DragDrop elements to find ones we are
16086          * hovering over or dropping on
16087          * @method fireEvents
16088          * @param {Event} e the event
16089          * @param {boolean} isDrop is this a drop op or a mouseover op?
16090          * @private
16091          * @static
16092          */
16093         fireEvents: function(e, isDrop) {
16094             var dc = this.dragCurrent;
16095
16096             // If the user did the mouse up outside of the window, we could
16097             // get here even though we have ended the drag.
16098             if (!dc || dc.isLocked()) {
16099                 return;
16100             }
16101
16102             var pt = e.getPoint();
16103
16104             // cache the previous dragOver array
16105             var oldOvers = [];
16106
16107             var outEvts   = [];
16108             var overEvts  = [];
16109             var dropEvts  = [];
16110             var enterEvts = [];
16111
16112             // Check to see if the object(s) we were hovering over is no longer
16113             // being hovered over so we can fire the onDragOut event
16114             for (var i in this.dragOvers) {
16115
16116                 var ddo = this.dragOvers[i];
16117
16118                 if (! this.isTypeOfDD(ddo)) {
16119                     continue;
16120                 }
16121
16122                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16123                     outEvts.push( ddo );
16124                 }
16125
16126                 oldOvers[i] = true;
16127                 delete this.dragOvers[i];
16128             }
16129
16130             for (var sGroup in dc.groups) {
16131
16132                 if ("string" != typeof sGroup) {
16133                     continue;
16134                 }
16135
16136                 for (i in this.ids[sGroup]) {
16137                     var oDD = this.ids[sGroup][i];
16138                     if (! this.isTypeOfDD(oDD)) {
16139                         continue;
16140                     }
16141
16142                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16143                         if (this.isOverTarget(pt, oDD, this.mode)) {
16144                             // look for drop interactions
16145                             if (isDrop) {
16146                                 dropEvts.push( oDD );
16147                             // look for drag enter and drag over interactions
16148                             } else {
16149
16150                                 // initial drag over: dragEnter fires
16151                                 if (!oldOvers[oDD.id]) {
16152                                     enterEvts.push( oDD );
16153                                 // subsequent drag overs: dragOver fires
16154                                 } else {
16155                                     overEvts.push( oDD );
16156                                 }
16157
16158                                 this.dragOvers[oDD.id] = oDD;
16159                             }
16160                         }
16161                     }
16162                 }
16163             }
16164
16165             if (this.mode) {
16166                 if (outEvts.length) {
16167                     dc.b4DragOut(e, outEvts);
16168                     dc.onDragOut(e, outEvts);
16169                 }
16170
16171                 if (enterEvts.length) {
16172                     dc.onDragEnter(e, enterEvts);
16173                 }
16174
16175                 if (overEvts.length) {
16176                     dc.b4DragOver(e, overEvts);
16177                     dc.onDragOver(e, overEvts);
16178                 }
16179
16180                 if (dropEvts.length) {
16181                     dc.b4DragDrop(e, dropEvts);
16182                     dc.onDragDrop(e, dropEvts);
16183                 }
16184
16185             } else {
16186                 // fire dragout events
16187                 var len = 0;
16188                 for (i=0, len=outEvts.length; i<len; ++i) {
16189                     dc.b4DragOut(e, outEvts[i].id);
16190                     dc.onDragOut(e, outEvts[i].id);
16191                 }
16192
16193                 // fire enter events
16194                 for (i=0,len=enterEvts.length; i<len; ++i) {
16195                     // dc.b4DragEnter(e, oDD.id);
16196                     dc.onDragEnter(e, enterEvts[i].id);
16197                 }
16198
16199                 // fire over events
16200                 for (i=0,len=overEvts.length; i<len; ++i) {
16201                     dc.b4DragOver(e, overEvts[i].id);
16202                     dc.onDragOver(e, overEvts[i].id);
16203                 }
16204
16205                 // fire drop events
16206                 for (i=0, len=dropEvts.length; i<len; ++i) {
16207                     dc.b4DragDrop(e, dropEvts[i].id);
16208                     dc.onDragDrop(e, dropEvts[i].id);
16209                 }
16210
16211             }
16212
16213             // notify about a drop that did not find a target
16214             if (isDrop && !dropEvts.length) {
16215                 dc.onInvalidDrop(e);
16216             }
16217
16218         },
16219
16220         /**
16221          * Helper function for getting the best match from the list of drag
16222          * and drop objects returned by the drag and drop events when we are
16223          * in INTERSECT mode.  It returns either the first object that the
16224          * cursor is over, or the object that has the greatest overlap with
16225          * the dragged element.
16226          * @method getBestMatch
16227          * @param  {DragDrop[]} dds The array of drag and drop objects
16228          * targeted
16229          * @return {DragDrop}       The best single match
16230          * @static
16231          */
16232         getBestMatch: function(dds) {
16233             var winner = null;
16234             // Return null if the input is not what we expect
16235             //if (!dds || !dds.length || dds.length == 0) {
16236                // winner = null;
16237             // If there is only one item, it wins
16238             //} else if (dds.length == 1) {
16239
16240             var len = dds.length;
16241
16242             if (len == 1) {
16243                 winner = dds[0];
16244             } else {
16245                 // Loop through the targeted items
16246                 for (var i=0; i<len; ++i) {
16247                     var dd = dds[i];
16248                     // If the cursor is over the object, it wins.  If the
16249                     // cursor is over multiple matches, the first one we come
16250                     // to wins.
16251                     if (dd.cursorIsOver) {
16252                         winner = dd;
16253                         break;
16254                     // Otherwise the object with the most overlap wins
16255                     } else {
16256                         if (!winner ||
16257                             winner.overlap.getArea() < dd.overlap.getArea()) {
16258                             winner = dd;
16259                         }
16260                     }
16261                 }
16262             }
16263
16264             return winner;
16265         },
16266
16267         /**
16268          * Refreshes the cache of the top-left and bottom-right points of the
16269          * drag and drop objects in the specified group(s).  This is in the
16270          * format that is stored in the drag and drop instance, so typical
16271          * usage is:
16272          * <code>
16273          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16274          * </code>
16275          * Alternatively:
16276          * <code>
16277          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16278          * </code>
16279          * @TODO this really should be an indexed array.  Alternatively this
16280          * method could accept both.
16281          * @method refreshCache
16282          * @param {Object} groups an associative array of groups to refresh
16283          * @static
16284          */
16285         refreshCache: function(groups) {
16286             for (var sGroup in groups) {
16287                 if ("string" != typeof sGroup) {
16288                     continue;
16289                 }
16290                 for (var i in this.ids[sGroup]) {
16291                     var oDD = this.ids[sGroup][i];
16292
16293                     if (this.isTypeOfDD(oDD)) {
16294                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16295                         var loc = this.getLocation(oDD);
16296                         if (loc) {
16297                             this.locationCache[oDD.id] = loc;
16298                         } else {
16299                             delete this.locationCache[oDD.id];
16300                             // this will unregister the drag and drop object if
16301                             // the element is not in a usable state
16302                             // oDD.unreg();
16303                         }
16304                     }
16305                 }
16306             }
16307         },
16308
16309         /**
16310          * This checks to make sure an element exists and is in the DOM.  The
16311          * main purpose is to handle cases where innerHTML is used to remove
16312          * drag and drop objects from the DOM.  IE provides an 'unspecified
16313          * error' when trying to access the offsetParent of such an element
16314          * @method verifyEl
16315          * @param {HTMLElement} el the element to check
16316          * @return {boolean} true if the element looks usable
16317          * @static
16318          */
16319         verifyEl: function(el) {
16320             if (el) {
16321                 var parent;
16322                 if(Roo.isIE){
16323                     try{
16324                         parent = el.offsetParent;
16325                     }catch(e){}
16326                 }else{
16327                     parent = el.offsetParent;
16328                 }
16329                 if (parent) {
16330                     return true;
16331                 }
16332             }
16333
16334             return false;
16335         },
16336
16337         /**
16338          * Returns a Region object containing the drag and drop element's position
16339          * and size, including the padding configured for it
16340          * @method getLocation
16341          * @param {DragDrop} oDD the drag and drop object to get the
16342          *                       location for
16343          * @return {Roo.lib.Region} a Region object representing the total area
16344          *                             the element occupies, including any padding
16345          *                             the instance is configured for.
16346          * @static
16347          */
16348         getLocation: function(oDD) {
16349             if (! this.isTypeOfDD(oDD)) {
16350                 return null;
16351             }
16352
16353             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16354
16355             try {
16356                 pos= Roo.lib.Dom.getXY(el);
16357             } catch (e) { }
16358
16359             if (!pos) {
16360                 return null;
16361             }
16362
16363             x1 = pos[0];
16364             x2 = x1 + el.offsetWidth;
16365             y1 = pos[1];
16366             y2 = y1 + el.offsetHeight;
16367
16368             t = y1 - oDD.padding[0];
16369             r = x2 + oDD.padding[1];
16370             b = y2 + oDD.padding[2];
16371             l = x1 - oDD.padding[3];
16372
16373             return new Roo.lib.Region( t, r, b, l );
16374         },
16375
16376         /**
16377          * Checks the cursor location to see if it over the target
16378          * @method isOverTarget
16379          * @param {Roo.lib.Point} pt The point to evaluate
16380          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16381          * @return {boolean} true if the mouse is over the target
16382          * @private
16383          * @static
16384          */
16385         isOverTarget: function(pt, oTarget, intersect) {
16386             // use cache if available
16387             var loc = this.locationCache[oTarget.id];
16388             if (!loc || !this.useCache) {
16389                 loc = this.getLocation(oTarget);
16390                 this.locationCache[oTarget.id] = loc;
16391
16392             }
16393
16394             if (!loc) {
16395                 return false;
16396             }
16397
16398             oTarget.cursorIsOver = loc.contains( pt );
16399
16400             // DragDrop is using this as a sanity check for the initial mousedown
16401             // in this case we are done.  In POINT mode, if the drag obj has no
16402             // contraints, we are also done. Otherwise we need to evaluate the
16403             // location of the target as related to the actual location of the
16404             // dragged element.
16405             var dc = this.dragCurrent;
16406             if (!dc || !dc.getTargetCoord ||
16407                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16408                 return oTarget.cursorIsOver;
16409             }
16410
16411             oTarget.overlap = null;
16412
16413             // Get the current location of the drag element, this is the
16414             // location of the mouse event less the delta that represents
16415             // where the original mousedown happened on the element.  We
16416             // need to consider constraints and ticks as well.
16417             var pos = dc.getTargetCoord(pt.x, pt.y);
16418
16419             var el = dc.getDragEl();
16420             var curRegion = new Roo.lib.Region( pos.y,
16421                                                    pos.x + el.offsetWidth,
16422                                                    pos.y + el.offsetHeight,
16423                                                    pos.x );
16424
16425             var overlap = curRegion.intersect(loc);
16426
16427             if (overlap) {
16428                 oTarget.overlap = overlap;
16429                 return (intersect) ? true : oTarget.cursorIsOver;
16430             } else {
16431                 return false;
16432             }
16433         },
16434
16435         /**
16436          * unload event handler
16437          * @method _onUnload
16438          * @private
16439          * @static
16440          */
16441         _onUnload: function(e, me) {
16442             Roo.dd.DragDropMgr.unregAll();
16443         },
16444
16445         /**
16446          * Cleans up the drag and drop events and objects.
16447          * @method unregAll
16448          * @private
16449          * @static
16450          */
16451         unregAll: function() {
16452
16453             if (this.dragCurrent) {
16454                 this.stopDrag();
16455                 this.dragCurrent = null;
16456             }
16457
16458             this._execOnAll("unreg", []);
16459
16460             for (i in this.elementCache) {
16461                 delete this.elementCache[i];
16462             }
16463
16464             this.elementCache = {};
16465             this.ids = {};
16466         },
16467
16468         /**
16469          * A cache of DOM elements
16470          * @property elementCache
16471          * @private
16472          * @static
16473          */
16474         elementCache: {},
16475
16476         /**
16477          * Get the wrapper for the DOM element specified
16478          * @method getElWrapper
16479          * @param {String} id the id of the element to get
16480          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16481          * @private
16482          * @deprecated This wrapper isn't that useful
16483          * @static
16484          */
16485         getElWrapper: function(id) {
16486             var oWrapper = this.elementCache[id];
16487             if (!oWrapper || !oWrapper.el) {
16488                 oWrapper = this.elementCache[id] =
16489                     new this.ElementWrapper(Roo.getDom(id));
16490             }
16491             return oWrapper;
16492         },
16493
16494         /**
16495          * Returns the actual DOM element
16496          * @method getElement
16497          * @param {String} id the id of the elment to get
16498          * @return {Object} The element
16499          * @deprecated use Roo.getDom instead
16500          * @static
16501          */
16502         getElement: function(id) {
16503             return Roo.getDom(id);
16504         },
16505
16506         /**
16507          * Returns the style property for the DOM element (i.e.,
16508          * document.getElById(id).style)
16509          * @method getCss
16510          * @param {String} id the id of the elment to get
16511          * @return {Object} The style property of the element
16512          * @deprecated use Roo.getDom instead
16513          * @static
16514          */
16515         getCss: function(id) {
16516             var el = Roo.getDom(id);
16517             return (el) ? el.style : null;
16518         },
16519
16520         /**
16521          * Inner class for cached elements
16522          * @class DragDropMgr.ElementWrapper
16523          * @for DragDropMgr
16524          * @private
16525          * @deprecated
16526          */
16527         ElementWrapper: function(el) {
16528                 /**
16529                  * The element
16530                  * @property el
16531                  */
16532                 this.el = el || null;
16533                 /**
16534                  * The element id
16535                  * @property id
16536                  */
16537                 this.id = this.el && el.id;
16538                 /**
16539                  * A reference to the style property
16540                  * @property css
16541                  */
16542                 this.css = this.el && el.style;
16543             },
16544
16545         /**
16546          * Returns the X position of an html element
16547          * @method getPosX
16548          * @param el the element for which to get the position
16549          * @return {int} the X coordinate
16550          * @for DragDropMgr
16551          * @deprecated use Roo.lib.Dom.getX instead
16552          * @static
16553          */
16554         getPosX: function(el) {
16555             return Roo.lib.Dom.getX(el);
16556         },
16557
16558         /**
16559          * Returns the Y position of an html element
16560          * @method getPosY
16561          * @param el the element for which to get the position
16562          * @return {int} the Y coordinate
16563          * @deprecated use Roo.lib.Dom.getY instead
16564          * @static
16565          */
16566         getPosY: function(el) {
16567             return Roo.lib.Dom.getY(el);
16568         },
16569
16570         /**
16571          * Swap two nodes.  In IE, we use the native method, for others we
16572          * emulate the IE behavior
16573          * @method swapNode
16574          * @param n1 the first node to swap
16575          * @param n2 the other node to swap
16576          * @static
16577          */
16578         swapNode: function(n1, n2) {
16579             if (n1.swapNode) {
16580                 n1.swapNode(n2);
16581             } else {
16582                 var p = n2.parentNode;
16583                 var s = n2.nextSibling;
16584
16585                 if (s == n1) {
16586                     p.insertBefore(n1, n2);
16587                 } else if (n2 == n1.nextSibling) {
16588                     p.insertBefore(n2, n1);
16589                 } else {
16590                     n1.parentNode.replaceChild(n2, n1);
16591                     p.insertBefore(n1, s);
16592                 }
16593             }
16594         },
16595
16596         /**
16597          * Returns the current scroll position
16598          * @method getScroll
16599          * @private
16600          * @static
16601          */
16602         getScroll: function () {
16603             var t, l, dde=document.documentElement, db=document.body;
16604             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16605                 t = dde.scrollTop;
16606                 l = dde.scrollLeft;
16607             } else if (db) {
16608                 t = db.scrollTop;
16609                 l = db.scrollLeft;
16610             } else {
16611
16612             }
16613             return { top: t, left: l };
16614         },
16615
16616         /**
16617          * Returns the specified element style property
16618          * @method getStyle
16619          * @param {HTMLElement} el          the element
16620          * @param {string}      styleProp   the style property
16621          * @return {string} The value of the style property
16622          * @deprecated use Roo.lib.Dom.getStyle
16623          * @static
16624          */
16625         getStyle: function(el, styleProp) {
16626             return Roo.fly(el).getStyle(styleProp);
16627         },
16628
16629         /**
16630          * Gets the scrollTop
16631          * @method getScrollTop
16632          * @return {int} the document's scrollTop
16633          * @static
16634          */
16635         getScrollTop: function () { return this.getScroll().top; },
16636
16637         /**
16638          * Gets the scrollLeft
16639          * @method getScrollLeft
16640          * @return {int} the document's scrollTop
16641          * @static
16642          */
16643         getScrollLeft: function () { return this.getScroll().left; },
16644
16645         /**
16646          * Sets the x/y position of an element to the location of the
16647          * target element.
16648          * @method moveToEl
16649          * @param {HTMLElement} moveEl      The element to move
16650          * @param {HTMLElement} targetEl    The position reference element
16651          * @static
16652          */
16653         moveToEl: function (moveEl, targetEl) {
16654             var aCoord = Roo.lib.Dom.getXY(targetEl);
16655             Roo.lib.Dom.setXY(moveEl, aCoord);
16656         },
16657
16658         /**
16659          * Numeric array sort function
16660          * @method numericSort
16661          * @static
16662          */
16663         numericSort: function(a, b) { return (a - b); },
16664
16665         /**
16666          * Internal counter
16667          * @property _timeoutCount
16668          * @private
16669          * @static
16670          */
16671         _timeoutCount: 0,
16672
16673         /**
16674          * Trying to make the load order less important.  Without this we get
16675          * an error if this file is loaded before the Event Utility.
16676          * @method _addListeners
16677          * @private
16678          * @static
16679          */
16680         _addListeners: function() {
16681             var DDM = Roo.dd.DDM;
16682             if ( Roo.lib.Event && document ) {
16683                 DDM._onLoad();
16684             } else {
16685                 if (DDM._timeoutCount > 2000) {
16686                 } else {
16687                     setTimeout(DDM._addListeners, 10);
16688                     if (document && document.body) {
16689                         DDM._timeoutCount += 1;
16690                     }
16691                 }
16692             }
16693         },
16694
16695         /**
16696          * Recursively searches the immediate parent and all child nodes for
16697          * the handle element in order to determine wheter or not it was
16698          * clicked.
16699          * @method handleWasClicked
16700          * @param node the html element to inspect
16701          * @static
16702          */
16703         handleWasClicked: function(node, id) {
16704             if (this.isHandle(id, node.id)) {
16705                 return true;
16706             } else {
16707                 // check to see if this is a text node child of the one we want
16708                 var p = node.parentNode;
16709
16710                 while (p) {
16711                     if (this.isHandle(id, p.id)) {
16712                         return true;
16713                     } else {
16714                         p = p.parentNode;
16715                     }
16716                 }
16717             }
16718
16719             return false;
16720         }
16721
16722     };
16723
16724 }();
16725
16726 // shorter alias, save a few bytes
16727 Roo.dd.DDM = Roo.dd.DragDropMgr;
16728 Roo.dd.DDM._addListeners();
16729
16730 }/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.dd.DD
16743  * A DragDrop implementation where the linked element follows the
16744  * mouse cursor during a drag.
16745  * @extends Roo.dd.DragDrop
16746  * @constructor
16747  * @param {String} id the id of the linked element
16748  * @param {String} sGroup the group of related DragDrop items
16749  * @param {object} config an object containing configurable attributes
16750  *                Valid properties for DD:
16751  *                    scroll
16752  */
16753 Roo.dd.DD = function(id, sGroup, config) {
16754     if (id) {
16755         this.init(id, sGroup, config);
16756     }
16757 };
16758
16759 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16760
16761     /**
16762      * When set to true, the utility automatically tries to scroll the browser
16763      * window wehn a drag and drop element is dragged near the viewport boundary.
16764      * Defaults to true.
16765      * @property scroll
16766      * @type boolean
16767      */
16768     scroll: true,
16769
16770     /**
16771      * Sets the pointer offset to the distance between the linked element's top
16772      * left corner and the location the element was clicked
16773      * @method autoOffset
16774      * @param {int} iPageX the X coordinate of the click
16775      * @param {int} iPageY the Y coordinate of the click
16776      */
16777     autoOffset: function(iPageX, iPageY) {
16778         var x = iPageX - this.startPageX;
16779         var y = iPageY - this.startPageY;
16780         this.setDelta(x, y);
16781     },
16782
16783     /**
16784      * Sets the pointer offset.  You can call this directly to force the
16785      * offset to be in a particular location (e.g., pass in 0,0 to set it
16786      * to the center of the object)
16787      * @method setDelta
16788      * @param {int} iDeltaX the distance from the left
16789      * @param {int} iDeltaY the distance from the top
16790      */
16791     setDelta: function(iDeltaX, iDeltaY) {
16792         this.deltaX = iDeltaX;
16793         this.deltaY = iDeltaY;
16794     },
16795
16796     /**
16797      * Sets the drag element to the location of the mousedown or click event,
16798      * maintaining the cursor location relative to the location on the element
16799      * that was clicked.  Override this if you want to place the element in a
16800      * location other than where the cursor is.
16801      * @method setDragElPos
16802      * @param {int} iPageX the X coordinate of the mousedown or drag event
16803      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16804      */
16805     setDragElPos: function(iPageX, iPageY) {
16806         // the first time we do this, we are going to check to make sure
16807         // the element has css positioning
16808
16809         var el = this.getDragEl();
16810         this.alignElWithMouse(el, iPageX, iPageY);
16811     },
16812
16813     /**
16814      * Sets the element to the location of the mousedown or click event,
16815      * maintaining the cursor location relative to the location on the element
16816      * that was clicked.  Override this if you want to place the element in a
16817      * location other than where the cursor is.
16818      * @method alignElWithMouse
16819      * @param {HTMLElement} el the element to move
16820      * @param {int} iPageX the X coordinate of the mousedown or drag event
16821      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16822      */
16823     alignElWithMouse: function(el, iPageX, iPageY) {
16824         var oCoord = this.getTargetCoord(iPageX, iPageY);
16825         var fly = el.dom ? el : Roo.fly(el);
16826         if (!this.deltaSetXY) {
16827             var aCoord = [oCoord.x, oCoord.y];
16828             fly.setXY(aCoord);
16829             var newLeft = fly.getLeft(true);
16830             var newTop  = fly.getTop(true);
16831             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16832         } else {
16833             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16834         }
16835
16836         this.cachePosition(oCoord.x, oCoord.y);
16837         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16838         return oCoord;
16839     },
16840
16841     /**
16842      * Saves the most recent position so that we can reset the constraints and
16843      * tick marks on-demand.  We need to know this so that we can calculate the
16844      * number of pixels the element is offset from its original position.
16845      * @method cachePosition
16846      * @param iPageX the current x position (optional, this just makes it so we
16847      * don't have to look it up again)
16848      * @param iPageY the current y position (optional, this just makes it so we
16849      * don't have to look it up again)
16850      */
16851     cachePosition: function(iPageX, iPageY) {
16852         if (iPageX) {
16853             this.lastPageX = iPageX;
16854             this.lastPageY = iPageY;
16855         } else {
16856             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16857             this.lastPageX = aCoord[0];
16858             this.lastPageY = aCoord[1];
16859         }
16860     },
16861
16862     /**
16863      * Auto-scroll the window if the dragged object has been moved beyond the
16864      * visible window boundary.
16865      * @method autoScroll
16866      * @param {int} x the drag element's x position
16867      * @param {int} y the drag element's y position
16868      * @param {int} h the height of the drag element
16869      * @param {int} w the width of the drag element
16870      * @private
16871      */
16872     autoScroll: function(x, y, h, w) {
16873
16874         if (this.scroll) {
16875             // The client height
16876             var clientH = Roo.lib.Dom.getViewWidth();
16877
16878             // The client width
16879             var clientW = Roo.lib.Dom.getViewHeight();
16880
16881             // The amt scrolled down
16882             var st = this.DDM.getScrollTop();
16883
16884             // The amt scrolled right
16885             var sl = this.DDM.getScrollLeft();
16886
16887             // Location of the bottom of the element
16888             var bot = h + y;
16889
16890             // Location of the right of the element
16891             var right = w + x;
16892
16893             // The distance from the cursor to the bottom of the visible area,
16894             // adjusted so that we don't scroll if the cursor is beyond the
16895             // element drag constraints
16896             var toBot = (clientH + st - y - this.deltaY);
16897
16898             // The distance from the cursor to the right of the visible area
16899             var toRight = (clientW + sl - x - this.deltaX);
16900
16901
16902             // How close to the edge the cursor must be before we scroll
16903             // var thresh = (document.all) ? 100 : 40;
16904             var thresh = 40;
16905
16906             // How many pixels to scroll per autoscroll op.  This helps to reduce
16907             // clunky scrolling. IE is more sensitive about this ... it needs this
16908             // value to be higher.
16909             var scrAmt = (document.all) ? 80 : 30;
16910
16911             // Scroll down if we are near the bottom of the visible page and the
16912             // obj extends below the crease
16913             if ( bot > clientH && toBot < thresh ) {
16914                 window.scrollTo(sl, st + scrAmt);
16915             }
16916
16917             // Scroll up if the window is scrolled down and the top of the object
16918             // goes above the top border
16919             if ( y < st && st > 0 && y - st < thresh ) {
16920                 window.scrollTo(sl, st - scrAmt);
16921             }
16922
16923             // Scroll right if the obj is beyond the right border and the cursor is
16924             // near the border.
16925             if ( right > clientW && toRight < thresh ) {
16926                 window.scrollTo(sl + scrAmt, st);
16927             }
16928
16929             // Scroll left if the window has been scrolled to the right and the obj
16930             // extends past the left border
16931             if ( x < sl && sl > 0 && x - sl < thresh ) {
16932                 window.scrollTo(sl - scrAmt, st);
16933             }
16934         }
16935     },
16936
16937     /**
16938      * Finds the location the element should be placed if we want to move
16939      * it to where the mouse location less the click offset would place us.
16940      * @method getTargetCoord
16941      * @param {int} iPageX the X coordinate of the click
16942      * @param {int} iPageY the Y coordinate of the click
16943      * @return an object that contains the coordinates (Object.x and Object.y)
16944      * @private
16945      */
16946     getTargetCoord: function(iPageX, iPageY) {
16947
16948
16949         var x = iPageX - this.deltaX;
16950         var y = iPageY - this.deltaY;
16951
16952         if (this.constrainX) {
16953             if (x < this.minX) { x = this.minX; }
16954             if (x > this.maxX) { x = this.maxX; }
16955         }
16956
16957         if (this.constrainY) {
16958             if (y < this.minY) { y = this.minY; }
16959             if (y > this.maxY) { y = this.maxY; }
16960         }
16961
16962         x = this.getTick(x, this.xTicks);
16963         y = this.getTick(y, this.yTicks);
16964
16965
16966         return {x:x, y:y};
16967     },
16968
16969     /*
16970      * Sets up config options specific to this class. Overrides
16971      * Roo.dd.DragDrop, but all versions of this method through the
16972      * inheritance chain are called
16973      */
16974     applyConfig: function() {
16975         Roo.dd.DD.superclass.applyConfig.call(this);
16976         this.scroll = (this.config.scroll !== false);
16977     },
16978
16979     /*
16980      * Event that fires prior to the onMouseDown event.  Overrides
16981      * Roo.dd.DragDrop.
16982      */
16983     b4MouseDown: function(e) {
16984         // this.resetConstraints();
16985         this.autoOffset(e.getPageX(),
16986                             e.getPageY());
16987     },
16988
16989     /*
16990      * Event that fires prior to the onDrag event.  Overrides
16991      * Roo.dd.DragDrop.
16992      */
16993     b4Drag: function(e) {
16994         this.setDragElPos(e.getPageX(),
16995                             e.getPageY());
16996     },
16997
16998     toString: function() {
16999         return ("DD " + this.id);
17000     }
17001
17002     //////////////////////////////////////////////////////////////////////////
17003     // Debugging ygDragDrop events that can be overridden
17004     //////////////////////////////////////////////////////////////////////////
17005     /*
17006     startDrag: function(x, y) {
17007     },
17008
17009     onDrag: function(e) {
17010     },
17011
17012     onDragEnter: function(e, id) {
17013     },
17014
17015     onDragOver: function(e, id) {
17016     },
17017
17018     onDragOut: function(e, id) {
17019     },
17020
17021     onDragDrop: function(e, id) {
17022     },
17023
17024     endDrag: function(e) {
17025     }
17026
17027     */
17028
17029 });/*
17030  * Based on:
17031  * Ext JS Library 1.1.1
17032  * Copyright(c) 2006-2007, Ext JS, LLC.
17033  *
17034  * Originally Released Under LGPL - original licence link has changed is not relivant.
17035  *
17036  * Fork - LGPL
17037  * <script type="text/javascript">
17038  */
17039
17040 /**
17041  * @class Roo.dd.DDProxy
17042  * A DragDrop implementation that inserts an empty, bordered div into
17043  * the document that follows the cursor during drag operations.  At the time of
17044  * the click, the frame div is resized to the dimensions of the linked html
17045  * element, and moved to the exact location of the linked element.
17046  *
17047  * References to the "frame" element refer to the single proxy element that
17048  * was created to be dragged in place of all DDProxy elements on the
17049  * page.
17050  *
17051  * @extends Roo.dd.DD
17052  * @constructor
17053  * @param {String} id the id of the linked html element
17054  * @param {String} sGroup the group of related DragDrop objects
17055  * @param {object} config an object containing configurable attributes
17056  *                Valid properties for DDProxy in addition to those in DragDrop:
17057  *                   resizeFrame, centerFrame, dragElId
17058  */
17059 Roo.dd.DDProxy = function(id, sGroup, config) {
17060     if (id) {
17061         this.init(id, sGroup, config);
17062         this.initFrame();
17063     }
17064 };
17065
17066 /**
17067  * The default drag frame div id
17068  * @property Roo.dd.DDProxy.dragElId
17069  * @type String
17070  * @static
17071  */
17072 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17073
17074 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17075
17076     /**
17077      * By default we resize the drag frame to be the same size as the element
17078      * we want to drag (this is to get the frame effect).  We can turn it off
17079      * if we want a different behavior.
17080      * @property resizeFrame
17081      * @type boolean
17082      */
17083     resizeFrame: true,
17084
17085     /**
17086      * By default the frame is positioned exactly where the drag element is, so
17087      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17088      * you do not have constraints on the obj is to have the drag frame centered
17089      * around the cursor.  Set centerFrame to true for this effect.
17090      * @property centerFrame
17091      * @type boolean
17092      */
17093     centerFrame: false,
17094
17095     /**
17096      * Creates the proxy element if it does not yet exist
17097      * @method createFrame
17098      */
17099     createFrame: function() {
17100         var self = this;
17101         var body = document.body;
17102
17103         if (!body || !body.firstChild) {
17104             setTimeout( function() { self.createFrame(); }, 50 );
17105             return;
17106         }
17107
17108         var div = this.getDragEl();
17109
17110         if (!div) {
17111             div    = document.createElement("div");
17112             div.id = this.dragElId;
17113             var s  = div.style;
17114
17115             s.position   = "absolute";
17116             s.visibility = "hidden";
17117             s.cursor     = "move";
17118             s.border     = "2px solid #aaa";
17119             s.zIndex     = 999;
17120
17121             // appendChild can blow up IE if invoked prior to the window load event
17122             // while rendering a table.  It is possible there are other scenarios
17123             // that would cause this to happen as well.
17124             body.insertBefore(div, body.firstChild);
17125         }
17126     },
17127
17128     /**
17129      * Initialization for the drag frame element.  Must be called in the
17130      * constructor of all subclasses
17131      * @method initFrame
17132      */
17133     initFrame: function() {
17134         this.createFrame();
17135     },
17136
17137     applyConfig: function() {
17138         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17139
17140         this.resizeFrame = (this.config.resizeFrame !== false);
17141         this.centerFrame = (this.config.centerFrame);
17142         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17143     },
17144
17145     /**
17146      * Resizes the drag frame to the dimensions of the clicked object, positions
17147      * it over the object, and finally displays it
17148      * @method showFrame
17149      * @param {int} iPageX X click position
17150      * @param {int} iPageY Y click position
17151      * @private
17152      */
17153     showFrame: function(iPageX, iPageY) {
17154         var el = this.getEl();
17155         var dragEl = this.getDragEl();
17156         var s = dragEl.style;
17157
17158         this._resizeProxy();
17159
17160         if (this.centerFrame) {
17161             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17162                            Math.round(parseInt(s.height, 10)/2) );
17163         }
17164
17165         this.setDragElPos(iPageX, iPageY);
17166
17167         Roo.fly(dragEl).show();
17168     },
17169
17170     /**
17171      * The proxy is automatically resized to the dimensions of the linked
17172      * element when a drag is initiated, unless resizeFrame is set to false
17173      * @method _resizeProxy
17174      * @private
17175      */
17176     _resizeProxy: function() {
17177         if (this.resizeFrame) {
17178             var el = this.getEl();
17179             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17180         }
17181     },
17182
17183     // overrides Roo.dd.DragDrop
17184     b4MouseDown: function(e) {
17185         var x = e.getPageX();
17186         var y = e.getPageY();
17187         this.autoOffset(x, y);
17188         this.setDragElPos(x, y);
17189     },
17190
17191     // overrides Roo.dd.DragDrop
17192     b4StartDrag: function(x, y) {
17193         // show the drag frame
17194         this.showFrame(x, y);
17195     },
17196
17197     // overrides Roo.dd.DragDrop
17198     b4EndDrag: function(e) {
17199         Roo.fly(this.getDragEl()).hide();
17200     },
17201
17202     // overrides Roo.dd.DragDrop
17203     // By default we try to move the element to the last location of the frame.
17204     // This is so that the default behavior mirrors that of Roo.dd.DD.
17205     endDrag: function(e) {
17206
17207         var lel = this.getEl();
17208         var del = this.getDragEl();
17209
17210         // Show the drag frame briefly so we can get its position
17211         del.style.visibility = "";
17212
17213         this.beforeMove();
17214         // Hide the linked element before the move to get around a Safari
17215         // rendering bug.
17216         lel.style.visibility = "hidden";
17217         Roo.dd.DDM.moveToEl(lel, del);
17218         del.style.visibility = "hidden";
17219         lel.style.visibility = "";
17220
17221         this.afterDrag();
17222     },
17223
17224     beforeMove : function(){
17225
17226     },
17227
17228     afterDrag : function(){
17229
17230     },
17231
17232     toString: function() {
17233         return ("DDProxy " + this.id);
17234     }
17235
17236 });
17237 /*
17238  * Based on:
17239  * Ext JS Library 1.1.1
17240  * Copyright(c) 2006-2007, Ext JS, LLC.
17241  *
17242  * Originally Released Under LGPL - original licence link has changed is not relivant.
17243  *
17244  * Fork - LGPL
17245  * <script type="text/javascript">
17246  */
17247
17248  /**
17249  * @class Roo.dd.DDTarget
17250  * A DragDrop implementation that does not move, but can be a drop
17251  * target.  You would get the same result by simply omitting implementation
17252  * for the event callbacks, but this way we reduce the processing cost of the
17253  * event listener and the callbacks.
17254  * @extends Roo.dd.DragDrop
17255  * @constructor
17256  * @param {String} id the id of the element that is a drop target
17257  * @param {String} sGroup the group of related DragDrop objects
17258  * @param {object} config an object containing configurable attributes
17259  *                 Valid properties for DDTarget in addition to those in
17260  *                 DragDrop:
17261  *                    none
17262  */
17263 Roo.dd.DDTarget = function(id, sGroup, config) {
17264     if (id) {
17265         this.initTarget(id, sGroup, config);
17266     }
17267 };
17268
17269 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17270 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17271     toString: function() {
17272         return ("DDTarget " + this.id);
17273     }
17274 });
17275 /*
17276  * Based on:
17277  * Ext JS Library 1.1.1
17278  * Copyright(c) 2006-2007, Ext JS, LLC.
17279  *
17280  * Originally Released Under LGPL - original licence link has changed is not relivant.
17281  *
17282  * Fork - LGPL
17283  * <script type="text/javascript">
17284  */
17285  
17286
17287 /**
17288  * @class Roo.dd.ScrollManager
17289  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17290  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17291  * @singleton
17292  */
17293 Roo.dd.ScrollManager = function(){
17294     var ddm = Roo.dd.DragDropMgr;
17295     var els = {};
17296     var dragEl = null;
17297     var proc = {};
17298     
17299     var onStop = function(e){
17300         dragEl = null;
17301         clearProc();
17302     };
17303     
17304     var triggerRefresh = function(){
17305         if(ddm.dragCurrent){
17306              ddm.refreshCache(ddm.dragCurrent.groups);
17307         }
17308     };
17309     
17310     var doScroll = function(){
17311         if(ddm.dragCurrent){
17312             var dds = Roo.dd.ScrollManager;
17313             if(!dds.animate){
17314                 if(proc.el.scroll(proc.dir, dds.increment)){
17315                     triggerRefresh();
17316                 }
17317             }else{
17318                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17319             }
17320         }
17321     };
17322     
17323     var clearProc = function(){
17324         if(proc.id){
17325             clearInterval(proc.id);
17326         }
17327         proc.id = 0;
17328         proc.el = null;
17329         proc.dir = "";
17330     };
17331     
17332     var startProc = function(el, dir){
17333         clearProc();
17334         proc.el = el;
17335         proc.dir = dir;
17336         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17337     };
17338     
17339     var onFire = function(e, isDrop){
17340         if(isDrop || !ddm.dragCurrent){ return; }
17341         var dds = Roo.dd.ScrollManager;
17342         if(!dragEl || dragEl != ddm.dragCurrent){
17343             dragEl = ddm.dragCurrent;
17344             // refresh regions on drag start
17345             dds.refreshCache();
17346         }
17347         
17348         var xy = Roo.lib.Event.getXY(e);
17349         var pt = new Roo.lib.Point(xy[0], xy[1]);
17350         for(var id in els){
17351             var el = els[id], r = el._region;
17352             if(r && r.contains(pt) && el.isScrollable()){
17353                 if(r.bottom - pt.y <= dds.thresh){
17354                     if(proc.el != el){
17355                         startProc(el, "down");
17356                     }
17357                     return;
17358                 }else if(r.right - pt.x <= dds.thresh){
17359                     if(proc.el != el){
17360                         startProc(el, "left");
17361                     }
17362                     return;
17363                 }else if(pt.y - r.top <= dds.thresh){
17364                     if(proc.el != el){
17365                         startProc(el, "up");
17366                     }
17367                     return;
17368                 }else if(pt.x - r.left <= dds.thresh){
17369                     if(proc.el != el){
17370                         startProc(el, "right");
17371                     }
17372                     return;
17373                 }
17374             }
17375         }
17376         clearProc();
17377     };
17378     
17379     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17380     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17381     
17382     return {
17383         /**
17384          * Registers new overflow element(s) to auto scroll
17385          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17386          */
17387         register : function(el){
17388             if(el instanceof Array){
17389                 for(var i = 0, len = el.length; i < len; i++) {
17390                         this.register(el[i]);
17391                 }
17392             }else{
17393                 el = Roo.get(el);
17394                 els[el.id] = el;
17395             }
17396         },
17397         
17398         /**
17399          * Unregisters overflow element(s) so they are no longer scrolled
17400          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17401          */
17402         unregister : function(el){
17403             if(el instanceof Array){
17404                 for(var i = 0, len = el.length; i < len; i++) {
17405                         this.unregister(el[i]);
17406                 }
17407             }else{
17408                 el = Roo.get(el);
17409                 delete els[el.id];
17410             }
17411         },
17412         
17413         /**
17414          * The number of pixels from the edge of a container the pointer needs to be to 
17415          * trigger scrolling (defaults to 25)
17416          * @type Number
17417          */
17418         thresh : 25,
17419         
17420         /**
17421          * The number of pixels to scroll in each scroll increment (defaults to 50)
17422          * @type Number
17423          */
17424         increment : 100,
17425         
17426         /**
17427          * The frequency of scrolls in milliseconds (defaults to 500)
17428          * @type Number
17429          */
17430         frequency : 500,
17431         
17432         /**
17433          * True to animate the scroll (defaults to true)
17434          * @type Boolean
17435          */
17436         animate: true,
17437         
17438         /**
17439          * The animation duration in seconds - 
17440          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17441          * @type Number
17442          */
17443         animDuration: .4,
17444         
17445         /**
17446          * Manually trigger a cache refresh.
17447          */
17448         refreshCache : function(){
17449             for(var id in els){
17450                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17451                     els[id]._region = els[id].getRegion();
17452                 }
17453             }
17454         }
17455     };
17456 }();/*
17457  * Based on:
17458  * Ext JS Library 1.1.1
17459  * Copyright(c) 2006-2007, Ext JS, LLC.
17460  *
17461  * Originally Released Under LGPL - original licence link has changed is not relivant.
17462  *
17463  * Fork - LGPL
17464  * <script type="text/javascript">
17465  */
17466  
17467
17468 /**
17469  * @class Roo.dd.Registry
17470  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17471  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17472  * @singleton
17473  */
17474 Roo.dd.Registry = function(){
17475     var elements = {}; 
17476     var handles = {}; 
17477     var autoIdSeed = 0;
17478
17479     var getId = function(el, autogen){
17480         if(typeof el == "string"){
17481             return el;
17482         }
17483         var id = el.id;
17484         if(!id && autogen !== false){
17485             id = "roodd-" + (++autoIdSeed);
17486             el.id = id;
17487         }
17488         return id;
17489     };
17490     
17491     return {
17492     /**
17493      * Register a drag drop element
17494      * @param {String|HTMLElement} element The id or DOM node to register
17495      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17496      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17497      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17498      * populated in the data object (if applicable):
17499      * <pre>
17500 Value      Description<br />
17501 ---------  ------------------------------------------<br />
17502 handles    Array of DOM nodes that trigger dragging<br />
17503            for the element being registered<br />
17504 isHandle   True if the element passed in triggers<br />
17505            dragging itself, else false
17506 </pre>
17507      */
17508         register : function(el, data){
17509             data = data || {};
17510             if(typeof el == "string"){
17511                 el = document.getElementById(el);
17512             }
17513             data.ddel = el;
17514             elements[getId(el)] = data;
17515             if(data.isHandle !== false){
17516                 handles[data.ddel.id] = data;
17517             }
17518             if(data.handles){
17519                 var hs = data.handles;
17520                 for(var i = 0, len = hs.length; i < len; i++){
17521                         handles[getId(hs[i])] = data;
17522                 }
17523             }
17524         },
17525
17526     /**
17527      * Unregister a drag drop element
17528      * @param {String|HTMLElement}  element The id or DOM node to unregister
17529      */
17530         unregister : function(el){
17531             var id = getId(el, false);
17532             var data = elements[id];
17533             if(data){
17534                 delete elements[id];
17535                 if(data.handles){
17536                     var hs = data.handles;
17537                     for(var i = 0, len = hs.length; i < len; i++){
17538                         delete handles[getId(hs[i], false)];
17539                     }
17540                 }
17541             }
17542         },
17543
17544     /**
17545      * Returns the handle registered for a DOM Node by id
17546      * @param {String|HTMLElement} id The DOM node or id to look up
17547      * @return {Object} handle The custom handle data
17548      */
17549         getHandle : function(id){
17550             if(typeof id != "string"){ // must be element?
17551                 id = id.id;
17552             }
17553             return handles[id];
17554         },
17555
17556     /**
17557      * Returns the handle that is registered for the DOM node that is the target of the event
17558      * @param {Event} e The event
17559      * @return {Object} handle The custom handle data
17560      */
17561         getHandleFromEvent : function(e){
17562             var t = Roo.lib.Event.getTarget(e);
17563             return t ? handles[t.id] : null;
17564         },
17565
17566     /**
17567      * Returns a custom data object that is registered for a DOM node by id
17568      * @param {String|HTMLElement} id The DOM node or id to look up
17569      * @return {Object} data The custom data
17570      */
17571         getTarget : function(id){
17572             if(typeof id != "string"){ // must be element?
17573                 id = id.id;
17574             }
17575             return elements[id];
17576         },
17577
17578     /**
17579      * Returns a custom data object that is registered for the DOM node that is the target of the event
17580      * @param {Event} e The event
17581      * @return {Object} data The custom data
17582      */
17583         getTargetFromEvent : function(e){
17584             var t = Roo.lib.Event.getTarget(e);
17585             return t ? elements[t.id] || handles[t.id] : null;
17586         }
17587     };
17588 }();/*
17589  * Based on:
17590  * Ext JS Library 1.1.1
17591  * Copyright(c) 2006-2007, Ext JS, LLC.
17592  *
17593  * Originally Released Under LGPL - original licence link has changed is not relivant.
17594  *
17595  * Fork - LGPL
17596  * <script type="text/javascript">
17597  */
17598  
17599
17600 /**
17601  * @class Roo.dd.StatusProxy
17602  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17603  * default drag proxy used by all Roo.dd components.
17604  * @constructor
17605  * @param {Object} config
17606  */
17607 Roo.dd.StatusProxy = function(config){
17608     Roo.apply(this, config);
17609     this.id = this.id || Roo.id();
17610     this.el = new Roo.Layer({
17611         dh: {
17612             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17613                 {tag: "div", cls: "x-dd-drop-icon"},
17614                 {tag: "div", cls: "x-dd-drag-ghost"}
17615             ]
17616         }, 
17617         shadow: !config || config.shadow !== false
17618     });
17619     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17620     this.dropStatus = this.dropNotAllowed;
17621 };
17622
17623 Roo.dd.StatusProxy.prototype = {
17624     /**
17625      * @cfg {String} dropAllowed
17626      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17627      */
17628     dropAllowed : "x-dd-drop-ok",
17629     /**
17630      * @cfg {String} dropNotAllowed
17631      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17632      */
17633     dropNotAllowed : "x-dd-drop-nodrop",
17634
17635     /**
17636      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17637      * over the current target element.
17638      * @param {String} cssClass The css class for the new drop status indicator image
17639      */
17640     setStatus : function(cssClass){
17641         cssClass = cssClass || this.dropNotAllowed;
17642         if(this.dropStatus != cssClass){
17643             this.el.replaceClass(this.dropStatus, cssClass);
17644             this.dropStatus = cssClass;
17645         }
17646     },
17647
17648     /**
17649      * Resets the status indicator to the default dropNotAllowed value
17650      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17651      */
17652     reset : function(clearGhost){
17653         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17654         this.dropStatus = this.dropNotAllowed;
17655         if(clearGhost){
17656             this.ghost.update("");
17657         }
17658     },
17659
17660     /**
17661      * Updates the contents of the ghost element
17662      * @param {String} html The html that will replace the current innerHTML of the ghost element
17663      */
17664     update : function(html){
17665         if(typeof html == "string"){
17666             this.ghost.update(html);
17667         }else{
17668             this.ghost.update("");
17669             html.style.margin = "0";
17670             this.ghost.dom.appendChild(html);
17671         }
17672         // ensure float = none set?? cant remember why though.
17673         var el = this.ghost.dom.firstChild;
17674                 if(el){
17675                         Roo.fly(el).setStyle('float', 'none');
17676                 }
17677     },
17678     
17679     /**
17680      * Returns the underlying proxy {@link Roo.Layer}
17681      * @return {Roo.Layer} el
17682     */
17683     getEl : function(){
17684         return this.el;
17685     },
17686
17687     /**
17688      * Returns the ghost element
17689      * @return {Roo.Element} el
17690      */
17691     getGhost : function(){
17692         return this.ghost;
17693     },
17694
17695     /**
17696      * Hides the proxy
17697      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17698      */
17699     hide : function(clear){
17700         this.el.hide();
17701         if(clear){
17702             this.reset(true);
17703         }
17704     },
17705
17706     /**
17707      * Stops the repair animation if it's currently running
17708      */
17709     stop : function(){
17710         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17711             this.anim.stop();
17712         }
17713     },
17714
17715     /**
17716      * Displays this proxy
17717      */
17718     show : function(){
17719         this.el.show();
17720     },
17721
17722     /**
17723      * Force the Layer to sync its shadow and shim positions to the element
17724      */
17725     sync : function(){
17726         this.el.sync();
17727     },
17728
17729     /**
17730      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17731      * invalid drop operation by the item being dragged.
17732      * @param {Array} xy The XY position of the element ([x, y])
17733      * @param {Function} callback The function to call after the repair is complete
17734      * @param {Object} scope The scope in which to execute the callback
17735      */
17736     repair : function(xy, callback, scope){
17737         this.callback = callback;
17738         this.scope = scope;
17739         if(xy && this.animRepair !== false){
17740             this.el.addClass("x-dd-drag-repair");
17741             this.el.hideUnders(true);
17742             this.anim = this.el.shift({
17743                 duration: this.repairDuration || .5,
17744                 easing: 'easeOut',
17745                 xy: xy,
17746                 stopFx: true,
17747                 callback: this.afterRepair,
17748                 scope: this
17749             });
17750         }else{
17751             this.afterRepair();
17752         }
17753     },
17754
17755     // private
17756     afterRepair : function(){
17757         this.hide(true);
17758         if(typeof this.callback == "function"){
17759             this.callback.call(this.scope || this);
17760         }
17761         this.callback = null;
17762         this.scope = null;
17763     }
17764 };/*
17765  * Based on:
17766  * Ext JS Library 1.1.1
17767  * Copyright(c) 2006-2007, Ext JS, LLC.
17768  *
17769  * Originally Released Under LGPL - original licence link has changed is not relivant.
17770  *
17771  * Fork - LGPL
17772  * <script type="text/javascript">
17773  */
17774
17775 /**
17776  * @class Roo.dd.DragSource
17777  * @extends Roo.dd.DDProxy
17778  * A simple class that provides the basic implementation needed to make any element draggable.
17779  * @constructor
17780  * @param {String/HTMLElement/Element} el The container element
17781  * @param {Object} config
17782  */
17783 Roo.dd.DragSource = function(el, config){
17784     this.el = Roo.get(el);
17785     this.dragData = {};
17786     
17787     Roo.apply(this, config);
17788     
17789     if(!this.proxy){
17790         this.proxy = new Roo.dd.StatusProxy();
17791     }
17792
17793     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17794           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17795     
17796     this.dragging = false;
17797 };
17798
17799 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17800     /**
17801      * @cfg {String} dropAllowed
17802      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17803      */
17804     dropAllowed : "x-dd-drop-ok",
17805     /**
17806      * @cfg {String} dropNotAllowed
17807      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17808      */
17809     dropNotAllowed : "x-dd-drop-nodrop",
17810
17811     /**
17812      * Returns the data object associated with this drag source
17813      * @return {Object} data An object containing arbitrary data
17814      */
17815     getDragData : function(e){
17816         return this.dragData;
17817     },
17818
17819     // private
17820     onDragEnter : function(e, id){
17821         var target = Roo.dd.DragDropMgr.getDDById(id);
17822         this.cachedTarget = target;
17823         if(this.beforeDragEnter(target, e, id) !== false){
17824             if(target.isNotifyTarget){
17825                 var status = target.notifyEnter(this, e, this.dragData);
17826                 this.proxy.setStatus(status);
17827             }else{
17828                 this.proxy.setStatus(this.dropAllowed);
17829             }
17830             
17831             if(this.afterDragEnter){
17832                 /**
17833                  * An empty function by default, but provided so that you can perform a custom action
17834                  * when the dragged item enters the drop target by providing an implementation.
17835                  * @param {Roo.dd.DragDrop} target The drop target
17836                  * @param {Event} e The event object
17837                  * @param {String} id The id of the dragged element
17838                  * @method afterDragEnter
17839                  */
17840                 this.afterDragEnter(target, e, id);
17841             }
17842         }
17843     },
17844
17845     /**
17846      * An empty function by default, but provided so that you can perform a custom action
17847      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17848      * @param {Roo.dd.DragDrop} target The drop target
17849      * @param {Event} e The event object
17850      * @param {String} id The id of the dragged element
17851      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17852      */
17853     beforeDragEnter : function(target, e, id){
17854         return true;
17855     },
17856
17857     // private
17858     alignElWithMouse: function() {
17859         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17860         this.proxy.sync();
17861     },
17862
17863     // private
17864     onDragOver : function(e, id){
17865         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17866         if(this.beforeDragOver(target, e, id) !== false){
17867             if(target.isNotifyTarget){
17868                 var status = target.notifyOver(this, e, this.dragData);
17869                 this.proxy.setStatus(status);
17870             }
17871
17872             if(this.afterDragOver){
17873                 /**
17874                  * An empty function by default, but provided so that you can perform a custom action
17875                  * while the dragged item is over the drop target by providing an implementation.
17876                  * @param {Roo.dd.DragDrop} target The drop target
17877                  * @param {Event} e The event object
17878                  * @param {String} id The id of the dragged element
17879                  * @method afterDragOver
17880                  */
17881                 this.afterDragOver(target, e, id);
17882             }
17883         }
17884     },
17885
17886     /**
17887      * An empty function by default, but provided so that you can perform a custom action
17888      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17889      * @param {Roo.dd.DragDrop} target The drop target
17890      * @param {Event} e The event object
17891      * @param {String} id The id of the dragged element
17892      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17893      */
17894     beforeDragOver : function(target, e, id){
17895         return true;
17896     },
17897
17898     // private
17899     onDragOut : function(e, id){
17900         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17901         if(this.beforeDragOut(target, e, id) !== false){
17902             if(target.isNotifyTarget){
17903                 target.notifyOut(this, e, this.dragData);
17904             }
17905             this.proxy.reset();
17906             if(this.afterDragOut){
17907                 /**
17908                  * An empty function by default, but provided so that you can perform a custom action
17909                  * after the dragged item is dragged out of the target without dropping.
17910                  * @param {Roo.dd.DragDrop} target The drop target
17911                  * @param {Event} e The event object
17912                  * @param {String} id The id of the dragged element
17913                  * @method afterDragOut
17914                  */
17915                 this.afterDragOut(target, e, id);
17916             }
17917         }
17918         this.cachedTarget = null;
17919     },
17920
17921     /**
17922      * An empty function by default, but provided so that you can perform a custom action before the dragged
17923      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17924      * @param {Roo.dd.DragDrop} target The drop target
17925      * @param {Event} e The event object
17926      * @param {String} id The id of the dragged element
17927      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17928      */
17929     beforeDragOut : function(target, e, id){
17930         return true;
17931     },
17932     
17933     // private
17934     onDragDrop : function(e, id){
17935         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17936         if(this.beforeDragDrop(target, e, id) !== false){
17937             if(target.isNotifyTarget){
17938                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17939                     this.onValidDrop(target, e, id);
17940                 }else{
17941                     this.onInvalidDrop(target, e, id);
17942                 }
17943             }else{
17944                 this.onValidDrop(target, e, id);
17945             }
17946             
17947             if(this.afterDragDrop){
17948                 /**
17949                  * An empty function by default, but provided so that you can perform a custom action
17950                  * after a valid drag drop has occurred by providing an implementation.
17951                  * @param {Roo.dd.DragDrop} target The drop target
17952                  * @param {Event} e The event object
17953                  * @param {String} id The id of the dropped element
17954                  * @method afterDragDrop
17955                  */
17956                 this.afterDragDrop(target, e, id);
17957             }
17958         }
17959         delete this.cachedTarget;
17960     },
17961
17962     /**
17963      * An empty function by default, but provided so that you can perform a custom action before the dragged
17964      * item is dropped onto the target and optionally cancel the onDragDrop.
17965      * @param {Roo.dd.DragDrop} target The drop target
17966      * @param {Event} e The event object
17967      * @param {String} id The id of the dragged element
17968      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17969      */
17970     beforeDragDrop : function(target, e, id){
17971         return true;
17972     },
17973
17974     // private
17975     onValidDrop : function(target, e, id){
17976         this.hideProxy();
17977         if(this.afterValidDrop){
17978             /**
17979              * An empty function by default, but provided so that you can perform a custom action
17980              * after a valid drop has occurred by providing an implementation.
17981              * @param {Object} target The target DD 
17982              * @param {Event} e The event object
17983              * @param {String} id The id of the dropped element
17984              * @method afterInvalidDrop
17985              */
17986             this.afterValidDrop(target, e, id);
17987         }
17988     },
17989
17990     // private
17991     getRepairXY : function(e, data){
17992         return this.el.getXY();  
17993     },
17994
17995     // private
17996     onInvalidDrop : function(target, e, id){
17997         this.beforeInvalidDrop(target, e, id);
17998         if(this.cachedTarget){
17999             if(this.cachedTarget.isNotifyTarget){
18000                 this.cachedTarget.notifyOut(this, e, this.dragData);
18001             }
18002             this.cacheTarget = null;
18003         }
18004         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18005
18006         if(this.afterInvalidDrop){
18007             /**
18008              * An empty function by default, but provided so that you can perform a custom action
18009              * after an invalid drop has occurred by providing an implementation.
18010              * @param {Event} e The event object
18011              * @param {String} id The id of the dropped element
18012              * @method afterInvalidDrop
18013              */
18014             this.afterInvalidDrop(e, id);
18015         }
18016     },
18017
18018     // private
18019     afterRepair : function(){
18020         if(Roo.enableFx){
18021             this.el.highlight(this.hlColor || "c3daf9");
18022         }
18023         this.dragging = false;
18024     },
18025
18026     /**
18027      * An empty function by default, but provided so that you can perform a custom action after an invalid
18028      * drop has occurred.
18029      * @param {Roo.dd.DragDrop} target The drop target
18030      * @param {Event} e The event object
18031      * @param {String} id The id of the dragged element
18032      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18033      */
18034     beforeInvalidDrop : function(target, e, id){
18035         return true;
18036     },
18037
18038     // private
18039     handleMouseDown : function(e){
18040         if(this.dragging) {
18041             return;
18042         }
18043         var data = this.getDragData(e);
18044         if(data && this.onBeforeDrag(data, e) !== false){
18045             this.dragData = data;
18046             this.proxy.stop();
18047             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18048         } 
18049     },
18050
18051     /**
18052      * An empty function by default, but provided so that you can perform a custom action before the initial
18053      * drag event begins and optionally cancel it.
18054      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18055      * @param {Event} e The event object
18056      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18057      */
18058     onBeforeDrag : function(data, e){
18059         return true;
18060     },
18061
18062     /**
18063      * An empty function by default, but provided so that you can perform a custom action once the initial
18064      * drag event has begun.  The drag cannot be canceled from this function.
18065      * @param {Number} x The x position of the click on the dragged object
18066      * @param {Number} y The y position of the click on the dragged object
18067      */
18068     onStartDrag : Roo.emptyFn,
18069
18070     // private - YUI override
18071     startDrag : function(x, y){
18072         this.proxy.reset();
18073         this.dragging = true;
18074         this.proxy.update("");
18075         this.onInitDrag(x, y);
18076         this.proxy.show();
18077     },
18078
18079     // private
18080     onInitDrag : function(x, y){
18081         var clone = this.el.dom.cloneNode(true);
18082         clone.id = Roo.id(); // prevent duplicate ids
18083         this.proxy.update(clone);
18084         this.onStartDrag(x, y);
18085         return true;
18086     },
18087
18088     /**
18089      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18090      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18091      */
18092     getProxy : function(){
18093         return this.proxy;  
18094     },
18095
18096     /**
18097      * Hides the drag source's {@link Roo.dd.StatusProxy}
18098      */
18099     hideProxy : function(){
18100         this.proxy.hide();  
18101         this.proxy.reset(true);
18102         this.dragging = false;
18103     },
18104
18105     // private
18106     triggerCacheRefresh : function(){
18107         Roo.dd.DDM.refreshCache(this.groups);
18108     },
18109
18110     // private - override to prevent hiding
18111     b4EndDrag: function(e) {
18112     },
18113
18114     // private - override to prevent moving
18115     endDrag : function(e){
18116         this.onEndDrag(this.dragData, e);
18117     },
18118
18119     // private
18120     onEndDrag : function(data, e){
18121     },
18122     
18123     // private - pin to cursor
18124     autoOffset : function(x, y) {
18125         this.setDelta(-12, -20);
18126     }    
18127 });/*
18128  * Based on:
18129  * Ext JS Library 1.1.1
18130  * Copyright(c) 2006-2007, Ext JS, LLC.
18131  *
18132  * Originally Released Under LGPL - original licence link has changed is not relivant.
18133  *
18134  * Fork - LGPL
18135  * <script type="text/javascript">
18136  */
18137
18138
18139 /**
18140  * @class Roo.dd.DropTarget
18141  * @extends Roo.dd.DDTarget
18142  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18143  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18144  * @constructor
18145  * @param {String/HTMLElement/Element} el The container element
18146  * @param {Object} config
18147  */
18148 Roo.dd.DropTarget = function(el, config){
18149     this.el = Roo.get(el);
18150     
18151     Roo.apply(this, config);
18152     
18153     if(this.containerScroll){
18154         Roo.dd.ScrollManager.register(this.el);
18155     }
18156     
18157     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18158           {isTarget: true});
18159
18160 };
18161
18162 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18163     /**
18164      * @cfg {String} overClass
18165      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18166      */
18167     /**
18168      * @cfg {String} dropAllowed
18169      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18170      */
18171     dropAllowed : "x-dd-drop-ok",
18172     /**
18173      * @cfg {String} dropNotAllowed
18174      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18175      */
18176     dropNotAllowed : "x-dd-drop-nodrop",
18177
18178     // private
18179     isTarget : true,
18180
18181     // private
18182     isNotifyTarget : true,
18183
18184     /**
18185      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18186      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18187      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18189      * @param {Event} e The event
18190      * @param {Object} data An object containing arbitrary data supplied by the drag source
18191      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18192      * underlying {@link Roo.dd.StatusProxy} can be updated
18193      */
18194     notifyEnter : function(dd, e, data){
18195         if(this.overClass){
18196             this.el.addClass(this.overClass);
18197         }
18198         return this.dropAllowed;
18199     },
18200
18201     /**
18202      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18203      * This method will be called on every mouse movement while the drag source is over the drop target.
18204      * This default implementation simply returns the dropAllowed config value.
18205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18206      * @param {Event} e The event
18207      * @param {Object} data An object containing arbitrary data supplied by the drag source
18208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18209      * underlying {@link Roo.dd.StatusProxy} can be updated
18210      */
18211     notifyOver : function(dd, e, data){
18212         return this.dropAllowed;
18213     },
18214
18215     /**
18216      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18217      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18218      * overClass (if any) from the drop element.
18219      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18220      * @param {Event} e The event
18221      * @param {Object} data An object containing arbitrary data supplied by the drag source
18222      */
18223     notifyOut : function(dd, e, data){
18224         if(this.overClass){
18225             this.el.removeClass(this.overClass);
18226         }
18227     },
18228
18229     /**
18230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18231      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18232      * implementation that does something to process the drop event and returns true so that the drag source's
18233      * repair action does not run.
18234      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18235      * @param {Event} e The event
18236      * @param {Object} data An object containing arbitrary data supplied by the drag source
18237      * @return {Boolean} True if the drop was valid, else false
18238      */
18239     notifyDrop : function(dd, e, data){
18240         return false;
18241     }
18242 });/*
18243  * Based on:
18244  * Ext JS Library 1.1.1
18245  * Copyright(c) 2006-2007, Ext JS, LLC.
18246  *
18247  * Originally Released Under LGPL - original licence link has changed is not relivant.
18248  *
18249  * Fork - LGPL
18250  * <script type="text/javascript">
18251  */
18252
18253
18254 /**
18255  * @class Roo.dd.DragZone
18256  * @extends Roo.dd.DragSource
18257  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18258  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18259  * @constructor
18260  * @param {String/HTMLElement/Element} el The container element
18261  * @param {Object} config
18262  */
18263 Roo.dd.DragZone = function(el, config){
18264     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18265     if(this.containerScroll){
18266         Roo.dd.ScrollManager.register(this.el);
18267     }
18268 };
18269
18270 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18271     /**
18272      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18273      * for auto scrolling during drag operations.
18274      */
18275     /**
18276      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18277      * method after a failed drop (defaults to "c3daf9" - light blue)
18278      */
18279
18280     /**
18281      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18282      * for a valid target to drag based on the mouse down. Override this method
18283      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18284      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18285      * @param {EventObject} e The mouse down event
18286      * @return {Object} The dragData
18287      */
18288     getDragData : function(e){
18289         return Roo.dd.Registry.getHandleFromEvent(e);
18290     },
18291     
18292     /**
18293      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18294      * this.dragData.ddel
18295      * @param {Number} x The x position of the click on the dragged object
18296      * @param {Number} y The y position of the click on the dragged object
18297      * @return {Boolean} true to continue the drag, false to cancel
18298      */
18299     onInitDrag : function(x, y){
18300         this.proxy.update(this.dragData.ddel.cloneNode(true));
18301         this.onStartDrag(x, y);
18302         return true;
18303     },
18304     
18305     /**
18306      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18307      */
18308     afterRepair : function(){
18309         if(Roo.enableFx){
18310             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18311         }
18312         this.dragging = false;
18313     },
18314
18315     /**
18316      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18317      * the XY of this.dragData.ddel
18318      * @param {EventObject} e The mouse up event
18319      * @return {Array} The xy location (e.g. [100, 200])
18320      */
18321     getRepairXY : function(e){
18322         return Roo.Element.fly(this.dragData.ddel).getXY();  
18323     }
18324 });/*
18325  * Based on:
18326  * Ext JS Library 1.1.1
18327  * Copyright(c) 2006-2007, Ext JS, LLC.
18328  *
18329  * Originally Released Under LGPL - original licence link has changed is not relivant.
18330  *
18331  * Fork - LGPL
18332  * <script type="text/javascript">
18333  */
18334 /**
18335  * @class Roo.dd.DropZone
18336  * @extends Roo.dd.DropTarget
18337  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18338  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18339  * @constructor
18340  * @param {String/HTMLElement/Element} el The container element
18341  * @param {Object} config
18342  */
18343 Roo.dd.DropZone = function(el, config){
18344     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18345 };
18346
18347 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18348     /**
18349      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18350      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18351      * provide your own custom lookup.
18352      * @param {Event} e The event
18353      * @return {Object} data The custom data
18354      */
18355     getTargetFromEvent : function(e){
18356         return Roo.dd.Registry.getTargetFromEvent(e);
18357     },
18358
18359     /**
18360      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18361      * that it has registered.  This method has no default implementation and should be overridden to provide
18362      * node-specific processing if necessary.
18363      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18364      * {@link #getTargetFromEvent} for this node)
18365      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18366      * @param {Event} e The event
18367      * @param {Object} data An object containing arbitrary data supplied by the drag source
18368      */
18369     onNodeEnter : function(n, dd, e, data){
18370         
18371     },
18372
18373     /**
18374      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18375      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18376      * overridden to provide the proper feedback.
18377      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18378      * {@link #getTargetFromEvent} for this node)
18379      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18380      * @param {Event} e The event
18381      * @param {Object} data An object containing arbitrary data supplied by the drag source
18382      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18383      * underlying {@link Roo.dd.StatusProxy} can be updated
18384      */
18385     onNodeOver : function(n, dd, e, data){
18386         return this.dropAllowed;
18387     },
18388
18389     /**
18390      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18391      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18392      * node-specific processing if necessary.
18393      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18394      * {@link #getTargetFromEvent} for this node)
18395      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18396      * @param {Event} e The event
18397      * @param {Object} data An object containing arbitrary data supplied by the drag source
18398      */
18399     onNodeOut : function(n, dd, e, data){
18400         
18401     },
18402
18403     /**
18404      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18405      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18406      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18407      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18408      * {@link #getTargetFromEvent} for this node)
18409      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18410      * @param {Event} e The event
18411      * @param {Object} data An object containing arbitrary data supplied by the drag source
18412      * @return {Boolean} True if the drop was valid, else false
18413      */
18414     onNodeDrop : function(n, dd, e, data){
18415         return false;
18416     },
18417
18418     /**
18419      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18420      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18421      * it should be overridden to provide the proper feedback if necessary.
18422      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18423      * @param {Event} e The event
18424      * @param {Object} data An object containing arbitrary data supplied by the drag source
18425      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18426      * underlying {@link Roo.dd.StatusProxy} can be updated
18427      */
18428     onContainerOver : function(dd, e, data){
18429         return this.dropNotAllowed;
18430     },
18431
18432     /**
18433      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18434      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18435      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18436      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18437      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18438      * @param {Event} e The event
18439      * @param {Object} data An object containing arbitrary data supplied by the drag source
18440      * @return {Boolean} True if the drop was valid, else false
18441      */
18442     onContainerDrop : function(dd, e, data){
18443         return false;
18444     },
18445
18446     /**
18447      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18448      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18449      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18450      * you should override this method and provide a custom implementation.
18451      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18452      * @param {Event} e The event
18453      * @param {Object} data An object containing arbitrary data supplied by the drag source
18454      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18455      * underlying {@link Roo.dd.StatusProxy} can be updated
18456      */
18457     notifyEnter : function(dd, e, data){
18458         return this.dropNotAllowed;
18459     },
18460
18461     /**
18462      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18463      * This method will be called on every mouse movement while the drag source is over the drop zone.
18464      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18465      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18466      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18467      * registered node, it will call {@link #onContainerOver}.
18468      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18469      * @param {Event} e The event
18470      * @param {Object} data An object containing arbitrary data supplied by the drag source
18471      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18472      * underlying {@link Roo.dd.StatusProxy} can be updated
18473      */
18474     notifyOver : function(dd, e, data){
18475         var n = this.getTargetFromEvent(e);
18476         if(!n){ // not over valid drop target
18477             if(this.lastOverNode){
18478                 this.onNodeOut(this.lastOverNode, dd, e, data);
18479                 this.lastOverNode = null;
18480             }
18481             return this.onContainerOver(dd, e, data);
18482         }
18483         if(this.lastOverNode != n){
18484             if(this.lastOverNode){
18485                 this.onNodeOut(this.lastOverNode, dd, e, data);
18486             }
18487             this.onNodeEnter(n, dd, e, data);
18488             this.lastOverNode = n;
18489         }
18490         return this.onNodeOver(n, dd, e, data);
18491     },
18492
18493     /**
18494      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18495      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18496      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18497      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18498      * @param {Event} e The event
18499      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18500      */
18501     notifyOut : function(dd, e, data){
18502         if(this.lastOverNode){
18503             this.onNodeOut(this.lastOverNode, dd, e, data);
18504             this.lastOverNode = null;
18505         }
18506     },
18507
18508     /**
18509      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18510      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18511      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18512      * otherwise it will call {@link #onContainerDrop}.
18513      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18514      * @param {Event} e The event
18515      * @param {Object} data An object containing arbitrary data supplied by the drag source
18516      * @return {Boolean} True if the drop was valid, else false
18517      */
18518     notifyDrop : function(dd, e, data){
18519         if(this.lastOverNode){
18520             this.onNodeOut(this.lastOverNode, dd, e, data);
18521             this.lastOverNode = null;
18522         }
18523         var n = this.getTargetFromEvent(e);
18524         return n ?
18525             this.onNodeDrop(n, dd, e, data) :
18526             this.onContainerDrop(dd, e, data);
18527     },
18528
18529     // private
18530     triggerCacheRefresh : function(){
18531         Roo.dd.DDM.refreshCache(this.groups);
18532     }  
18533 });/*
18534  * Based on:
18535  * Ext JS Library 1.1.1
18536  * Copyright(c) 2006-2007, Ext JS, LLC.
18537  *
18538  * Originally Released Under LGPL - original licence link has changed is not relivant.
18539  *
18540  * Fork - LGPL
18541  * <script type="text/javascript">
18542  */
18543
18544
18545 /**
18546  * @class Roo.data.SortTypes
18547  * @singleton
18548  * Defines the default sorting (casting?) comparison functions used when sorting data.
18549  */
18550 Roo.data.SortTypes = {
18551     /**
18552      * Default sort that does nothing
18553      * @param {Mixed} s The value being converted
18554      * @return {Mixed} The comparison value
18555      */
18556     none : function(s){
18557         return s;
18558     },
18559     
18560     /**
18561      * The regular expression used to strip tags
18562      * @type {RegExp}
18563      * @property
18564      */
18565     stripTagsRE : /<\/?[^>]+>/gi,
18566     
18567     /**
18568      * Strips all HTML tags to sort on text only
18569      * @param {Mixed} s The value being converted
18570      * @return {String} The comparison value
18571      */
18572     asText : function(s){
18573         return String(s).replace(this.stripTagsRE, "");
18574     },
18575     
18576     /**
18577      * Strips all HTML tags to sort on text only - Case insensitive
18578      * @param {Mixed} s The value being converted
18579      * @return {String} The comparison value
18580      */
18581     asUCText : function(s){
18582         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18583     },
18584     
18585     /**
18586      * Case insensitive string
18587      * @param {Mixed} s The value being converted
18588      * @return {String} The comparison value
18589      */
18590     asUCString : function(s) {
18591         return String(s).toUpperCase();
18592     },
18593     
18594     /**
18595      * Date sorting
18596      * @param {Mixed} s The value being converted
18597      * @return {Number} The comparison value
18598      */
18599     asDate : function(s) {
18600         if(!s){
18601             return 0;
18602         }
18603         if(s instanceof Date){
18604             return s.getTime();
18605         }
18606         return Date.parse(String(s));
18607     },
18608     
18609     /**
18610      * Float sorting
18611      * @param {Mixed} s The value being converted
18612      * @return {Float} The comparison value
18613      */
18614     asFloat : function(s) {
18615         var val = parseFloat(String(s).replace(/,/g, ""));
18616         if(isNaN(val)) val = 0;
18617         return val;
18618     },
18619     
18620     /**
18621      * Integer sorting
18622      * @param {Mixed} s The value being converted
18623      * @return {Number} The comparison value
18624      */
18625     asInt : function(s) {
18626         var val = parseInt(String(s).replace(/,/g, ""));
18627         if(isNaN(val)) val = 0;
18628         return val;
18629     }
18630 };/*
18631  * Based on:
18632  * Ext JS Library 1.1.1
18633  * Copyright(c) 2006-2007, Ext JS, LLC.
18634  *
18635  * Originally Released Under LGPL - original licence link has changed is not relivant.
18636  *
18637  * Fork - LGPL
18638  * <script type="text/javascript">
18639  */
18640
18641 /**
18642 * @class Roo.data.Record
18643  * Instances of this class encapsulate both record <em>definition</em> information, and record
18644  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18645  * to access Records cached in an {@link Roo.data.Store} object.<br>
18646  * <p>
18647  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18648  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18649  * objects.<br>
18650  * <p>
18651  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18652  * @constructor
18653  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18654  * {@link #create}. The parameters are the same.
18655  * @param {Array} data An associative Array of data values keyed by the field name.
18656  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18657  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18658  * not specified an integer id is generated.
18659  */
18660 Roo.data.Record = function(data, id){
18661     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18662     this.data = data;
18663 };
18664
18665 /**
18666  * Generate a constructor for a specific record layout.
18667  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18668  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18669  * Each field definition object may contain the following properties: <ul>
18670  * <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,
18671  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18672  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18673  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18674  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18675  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18676  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18677  * this may be omitted.</p></li>
18678  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18679  * <ul><li>auto (Default, implies no conversion)</li>
18680  * <li>string</li>
18681  * <li>int</li>
18682  * <li>float</li>
18683  * <li>boolean</li>
18684  * <li>date</li></ul></p></li>
18685  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18686  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18687  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18688  * by the Reader into an object that will be stored in the Record. It is passed the
18689  * following parameters:<ul>
18690  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18691  * </ul></p></li>
18692  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18693  * </ul>
18694  * <br>usage:<br><pre><code>
18695 var TopicRecord = Roo.data.Record.create(
18696     {name: 'title', mapping: 'topic_title'},
18697     {name: 'author', mapping: 'username'},
18698     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18699     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18700     {name: 'lastPoster', mapping: 'user2'},
18701     {name: 'excerpt', mapping: 'post_text'}
18702 );
18703
18704 var myNewRecord = new TopicRecord({
18705     title: 'Do my job please',
18706     author: 'noobie',
18707     totalPosts: 1,
18708     lastPost: new Date(),
18709     lastPoster: 'Animal',
18710     excerpt: 'No way dude!'
18711 });
18712 myStore.add(myNewRecord);
18713 </code></pre>
18714  * @method create
18715  * @static
18716  */
18717 Roo.data.Record.create = function(o){
18718     var f = function(){
18719         f.superclass.constructor.apply(this, arguments);
18720     };
18721     Roo.extend(f, Roo.data.Record);
18722     var p = f.prototype;
18723     p.fields = new Roo.util.MixedCollection(false, function(field){
18724         return field.name;
18725     });
18726     for(var i = 0, len = o.length; i < len; i++){
18727         p.fields.add(new Roo.data.Field(o[i]));
18728     }
18729     f.getField = function(name){
18730         return p.fields.get(name);  
18731     };
18732     return f;
18733 };
18734
18735 Roo.data.Record.AUTO_ID = 1000;
18736 Roo.data.Record.EDIT = 'edit';
18737 Roo.data.Record.REJECT = 'reject';
18738 Roo.data.Record.COMMIT = 'commit';
18739
18740 Roo.data.Record.prototype = {
18741     /**
18742      * Readonly flag - true if this record has been modified.
18743      * @type Boolean
18744      */
18745     dirty : false,
18746     editing : false,
18747     error: null,
18748     modified: null,
18749
18750     // private
18751     join : function(store){
18752         this.store = store;
18753     },
18754
18755     /**
18756      * Set the named field to the specified value.
18757      * @param {String} name The name of the field to set.
18758      * @param {Object} value The value to set the field to.
18759      */
18760     set : function(name, value){
18761         if(this.data[name] == value){
18762             return;
18763         }
18764         this.dirty = true;
18765         if(!this.modified){
18766             this.modified = {};
18767         }
18768         if(typeof this.modified[name] == 'undefined'){
18769             this.modified[name] = this.data[name];
18770         }
18771         this.data[name] = value;
18772         if(!this.editing){
18773             this.store.afterEdit(this);
18774         }       
18775     },
18776
18777     /**
18778      * Get the value of the named field.
18779      * @param {String} name The name of the field to get the value of.
18780      * @return {Object} The value of the field.
18781      */
18782     get : function(name){
18783         return this.data[name]; 
18784     },
18785
18786     // private
18787     beginEdit : function(){
18788         this.editing = true;
18789         this.modified = {}; 
18790     },
18791
18792     // private
18793     cancelEdit : function(){
18794         this.editing = false;
18795         delete this.modified;
18796     },
18797
18798     // private
18799     endEdit : function(){
18800         this.editing = false;
18801         if(this.dirty && this.store){
18802             this.store.afterEdit(this);
18803         }
18804     },
18805
18806     /**
18807      * Usually called by the {@link Roo.data.Store} which owns the Record.
18808      * Rejects all changes made to the Record since either creation, or the last commit operation.
18809      * Modified fields are reverted to their original values.
18810      * <p>
18811      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18812      * of reject operations.
18813      */
18814     reject : function(){
18815         var m = this.modified;
18816         for(var n in m){
18817             if(typeof m[n] != "function"){
18818                 this.data[n] = m[n];
18819             }
18820         }
18821         this.dirty = false;
18822         delete this.modified;
18823         this.editing = false;
18824         if(this.store){
18825             this.store.afterReject(this);
18826         }
18827     },
18828
18829     /**
18830      * Usually called by the {@link Roo.data.Store} which owns the Record.
18831      * Commits all changes made to the Record since either creation, or the last commit operation.
18832      * <p>
18833      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18834      * of commit operations.
18835      */
18836     commit : function(){
18837         this.dirty = false;
18838         delete this.modified;
18839         this.editing = false;
18840         if(this.store){
18841             this.store.afterCommit(this);
18842         }
18843     },
18844
18845     // private
18846     hasError : function(){
18847         return this.error != null;
18848     },
18849
18850     // private
18851     clearError : function(){
18852         this.error = null;
18853     },
18854
18855     /**
18856      * Creates a copy of this record.
18857      * @param {String} id (optional) A new record id if you don't want to use this record's id
18858      * @return {Record}
18859      */
18860     copy : function(newId) {
18861         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18862     }
18863 };/*
18864  * Based on:
18865  * Ext JS Library 1.1.1
18866  * Copyright(c) 2006-2007, Ext JS, LLC.
18867  *
18868  * Originally Released Under LGPL - original licence link has changed is not relivant.
18869  *
18870  * Fork - LGPL
18871  * <script type="text/javascript">
18872  */
18873
18874
18875
18876 /**
18877  * @class Roo.data.Store
18878  * @extends Roo.util.Observable
18879  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18880  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18881  * <p>
18882  * 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
18883  * has no knowledge of the format of the data returned by the Proxy.<br>
18884  * <p>
18885  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18886  * instances from the data object. These records are cached and made available through accessor functions.
18887  * @constructor
18888  * Creates a new Store.
18889  * @param {Object} config A config object containing the objects needed for the Store to access data,
18890  * and read the data into Records.
18891  */
18892 Roo.data.Store = function(config){
18893     this.data = new Roo.util.MixedCollection(false);
18894     this.data.getKey = function(o){
18895         return o.id;
18896     };
18897     this.baseParams = {};
18898     // private
18899     this.paramNames = {
18900         "start" : "start",
18901         "limit" : "limit",
18902         "sort" : "sort",
18903         "dir" : "dir"
18904     };
18905
18906     if(config && config.data){
18907         this.inlineData = config.data;
18908         delete config.data;
18909     }
18910
18911     Roo.apply(this, config);
18912     
18913     if(this.reader){ // reader passed
18914         this.reader = Roo.factory(this.reader, Roo.data);
18915         this.reader.xmodule = this.xmodule || false;
18916         if(!this.recordType){
18917             this.recordType = this.reader.recordType;
18918         }
18919         if(this.reader.onMetaChange){
18920             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18921         }
18922     }
18923
18924     if(this.recordType){
18925         this.fields = this.recordType.prototype.fields;
18926     }
18927     this.modified = [];
18928
18929     this.addEvents({
18930         /**
18931          * @event datachanged
18932          * Fires when the data cache has changed, and a widget which is using this Store
18933          * as a Record cache should refresh its view.
18934          * @param {Store} this
18935          */
18936         datachanged : true,
18937         /**
18938          * @event metachange
18939          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18940          * @param {Store} this
18941          * @param {Object} meta The JSON metadata
18942          */
18943         metachange : true,
18944         /**
18945          * @event add
18946          * Fires when Records have been added to the Store
18947          * @param {Store} this
18948          * @param {Roo.data.Record[]} records The array of Records added
18949          * @param {Number} index The index at which the record(s) were added
18950          */
18951         add : true,
18952         /**
18953          * @event remove
18954          * Fires when a Record has been removed from the Store
18955          * @param {Store} this
18956          * @param {Roo.data.Record} record The Record that was removed
18957          * @param {Number} index The index at which the record was removed
18958          */
18959         remove : true,
18960         /**
18961          * @event update
18962          * Fires when a Record has been updated
18963          * @param {Store} this
18964          * @param {Roo.data.Record} record The Record that was updated
18965          * @param {String} operation The update operation being performed.  Value may be one of:
18966          * <pre><code>
18967  Roo.data.Record.EDIT
18968  Roo.data.Record.REJECT
18969  Roo.data.Record.COMMIT
18970          * </code></pre>
18971          */
18972         update : true,
18973         /**
18974          * @event clear
18975          * Fires when the data cache has been cleared.
18976          * @param {Store} this
18977          */
18978         clear : true,
18979         /**
18980          * @event beforeload
18981          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18982          * the load action will be canceled.
18983          * @param {Store} this
18984          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18985          */
18986         beforeload : true,
18987         /**
18988          * @event load
18989          * Fires after a new set of Records has been loaded.
18990          * @param {Store} this
18991          * @param {Roo.data.Record[]} records The Records that were loaded
18992          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18993          */
18994         load : true,
18995         /**
18996          * @event loadexception
18997          * Fires if an exception occurs in the Proxy during loading.
18998          * Called with the signature of the Proxy's "loadexception" event.
18999          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19000          * 
19001          * @param {Proxy} 
19002          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19003          * @param {Object} load options 
19004          * @param {Object} jsonData from your request (normally this contains the Exception)
19005          */
19006         loadexception : true
19007     });
19008     
19009     if(this.proxy){
19010         this.proxy = Roo.factory(this.proxy, Roo.data);
19011         this.proxy.xmodule = this.xmodule || false;
19012         this.relayEvents(this.proxy,  ["loadexception"]);
19013     }
19014     this.sortToggle = {};
19015
19016     Roo.data.Store.superclass.constructor.call(this);
19017
19018     if(this.inlineData){
19019         this.loadData(this.inlineData);
19020         delete this.inlineData;
19021     }
19022 };
19023 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19024      /**
19025     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19026     * without a remote query - used by combo/forms at present.
19027     */
19028     
19029     /**
19030     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19031     */
19032     /**
19033     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19034     */
19035     /**
19036     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19037     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19038     */
19039     /**
19040     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19041     * on any HTTP request
19042     */
19043     /**
19044     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19045     */
19046     /**
19047     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19048     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19049     */
19050     remoteSort : false,
19051
19052     /**
19053     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19054      * loaded or when a record is removed. (defaults to false).
19055     */
19056     pruneModifiedRecords : false,
19057
19058     // private
19059     lastOptions : null,
19060
19061     /**
19062      * Add Records to the Store and fires the add event.
19063      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19064      */
19065     add : function(records){
19066         records = [].concat(records);
19067         for(var i = 0, len = records.length; i < len; i++){
19068             records[i].join(this);
19069         }
19070         var index = this.data.length;
19071         this.data.addAll(records);
19072         this.fireEvent("add", this, records, index);
19073     },
19074
19075     /**
19076      * Remove a Record from the Store and fires the remove event.
19077      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19078      */
19079     remove : function(record){
19080         var index = this.data.indexOf(record);
19081         this.data.removeAt(index);
19082         if(this.pruneModifiedRecords){
19083             this.modified.remove(record);
19084         }
19085         this.fireEvent("remove", this, record, index);
19086     },
19087
19088     /**
19089      * Remove all Records from the Store and fires the clear event.
19090      */
19091     removeAll : function(){
19092         this.data.clear();
19093         if(this.pruneModifiedRecords){
19094             this.modified = [];
19095         }
19096         this.fireEvent("clear", this);
19097     },
19098
19099     /**
19100      * Inserts Records to the Store at the given index and fires the add event.
19101      * @param {Number} index The start index at which to insert the passed Records.
19102      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19103      */
19104     insert : function(index, records){
19105         records = [].concat(records);
19106         for(var i = 0, len = records.length; i < len; i++){
19107             this.data.insert(index, records[i]);
19108             records[i].join(this);
19109         }
19110         this.fireEvent("add", this, records, index);
19111     },
19112
19113     /**
19114      * Get the index within the cache of the passed Record.
19115      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19116      * @return {Number} The index of the passed Record. Returns -1 if not found.
19117      */
19118     indexOf : function(record){
19119         return this.data.indexOf(record);
19120     },
19121
19122     /**
19123      * Get the index within the cache of the Record with the passed id.
19124      * @param {String} id The id of the Record to find.
19125      * @return {Number} The index of the Record. Returns -1 if not found.
19126      */
19127     indexOfId : function(id){
19128         return this.data.indexOfKey(id);
19129     },
19130
19131     /**
19132      * Get the Record with the specified id.
19133      * @param {String} id The id of the Record to find.
19134      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19135      */
19136     getById : function(id){
19137         return this.data.key(id);
19138     },
19139
19140     /**
19141      * Get the Record at the specified index.
19142      * @param {Number} index The index of the Record to find.
19143      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19144      */
19145     getAt : function(index){
19146         return this.data.itemAt(index);
19147     },
19148
19149     /**
19150      * Returns a range of Records between specified indices.
19151      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19152      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19153      * @return {Roo.data.Record[]} An array of Records
19154      */
19155     getRange : function(start, end){
19156         return this.data.getRange(start, end);
19157     },
19158
19159     // private
19160     storeOptions : function(o){
19161         o = Roo.apply({}, o);
19162         delete o.callback;
19163         delete o.scope;
19164         this.lastOptions = o;
19165     },
19166
19167     /**
19168      * Loads the Record cache from the configured Proxy using the configured Reader.
19169      * <p>
19170      * If using remote paging, then the first load call must specify the <em>start</em>
19171      * and <em>limit</em> properties in the options.params property to establish the initial
19172      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19173      * <p>
19174      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19175      * and this call will return before the new data has been loaded. Perform any post-processing
19176      * in a callback function, or in a "load" event handler.</strong>
19177      * <p>
19178      * @param {Object} options An object containing properties which control loading options:<ul>
19179      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19180      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19181      * passed the following arguments:<ul>
19182      * <li>r : Roo.data.Record[]</li>
19183      * <li>options: Options object from the load call</li>
19184      * <li>success: Boolean success indicator</li></ul></li>
19185      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19186      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19187      * </ul>
19188      */
19189     load : function(options){
19190         options = options || {};
19191         if(this.fireEvent("beforeload", this, options) !== false){
19192             this.storeOptions(options);
19193             var p = Roo.apply(options.params || {}, this.baseParams);
19194             if(this.sortInfo && this.remoteSort){
19195                 var pn = this.paramNames;
19196                 p[pn["sort"]] = this.sortInfo.field;
19197                 p[pn["dir"]] = this.sortInfo.direction;
19198             }
19199             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19200         }
19201     },
19202
19203     /**
19204      * Reloads the Record cache from the configured Proxy using the configured Reader and
19205      * the options from the last load operation performed.
19206      * @param {Object} options (optional) An object containing properties which may override the options
19207      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19208      * the most recently used options are reused).
19209      */
19210     reload : function(options){
19211         this.load(Roo.applyIf(options||{}, this.lastOptions));
19212     },
19213
19214     // private
19215     // Called as a callback by the Reader during a load operation.
19216     loadRecords : function(o, options, success){
19217         if(!o || success === false){
19218             if(success !== false){
19219                 this.fireEvent("load", this, [], options);
19220             }
19221             if(options.callback){
19222                 options.callback.call(options.scope || this, [], options, false);
19223             }
19224             return;
19225         }
19226         // if data returned failure - throw an exception.
19227         if (o.success === false) {
19228             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19229             return;
19230         }
19231         var r = o.records, t = o.totalRecords || r.length;
19232         if(!options || options.add !== true){
19233             if(this.pruneModifiedRecords){
19234                 this.modified = [];
19235             }
19236             for(var i = 0, len = r.length; i < len; i++){
19237                 r[i].join(this);
19238             }
19239             if(this.snapshot){
19240                 this.data = this.snapshot;
19241                 delete this.snapshot;
19242             }
19243             this.data.clear();
19244             this.data.addAll(r);
19245             this.totalLength = t;
19246             this.applySort();
19247             this.fireEvent("datachanged", this);
19248         }else{
19249             this.totalLength = Math.max(t, this.data.length+r.length);
19250             this.add(r);
19251         }
19252         this.fireEvent("load", this, r, options);
19253         if(options.callback){
19254             options.callback.call(options.scope || this, r, options, true);
19255         }
19256     },
19257
19258     /**
19259      * Loads data from a passed data block. A Reader which understands the format of the data
19260      * must have been configured in the constructor.
19261      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19262      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19263      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19264      */
19265     loadData : function(o, append){
19266         var r = this.reader.readRecords(o);
19267         this.loadRecords(r, {add: append}, true);
19268     },
19269
19270     /**
19271      * Gets the number of cached records.
19272      * <p>
19273      * <em>If using paging, this may not be the total size of the dataset. If the data object
19274      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19275      * the data set size</em>
19276      */
19277     getCount : function(){
19278         return this.data.length || 0;
19279     },
19280
19281     /**
19282      * Gets the total number of records in the dataset as returned by the server.
19283      * <p>
19284      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19285      * the dataset size</em>
19286      */
19287     getTotalCount : function(){
19288         return this.totalLength || 0;
19289     },
19290
19291     /**
19292      * Returns the sort state of the Store as an object with two properties:
19293      * <pre><code>
19294  field {String} The name of the field by which the Records are sorted
19295  direction {String} The sort order, "ASC" or "DESC"
19296      * </code></pre>
19297      */
19298     getSortState : function(){
19299         return this.sortInfo;
19300     },
19301
19302     // private
19303     applySort : function(){
19304         if(this.sortInfo && !this.remoteSort){
19305             var s = this.sortInfo, f = s.field;
19306             var st = this.fields.get(f).sortType;
19307             var fn = function(r1, r2){
19308                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19309                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19310             };
19311             this.data.sort(s.direction, fn);
19312             if(this.snapshot && this.snapshot != this.data){
19313                 this.snapshot.sort(s.direction, fn);
19314             }
19315         }
19316     },
19317
19318     /**
19319      * Sets the default sort column and order to be used by the next load operation.
19320      * @param {String} fieldName The name of the field to sort by.
19321      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19322      */
19323     setDefaultSort : function(field, dir){
19324         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19325     },
19326
19327     /**
19328      * Sort the Records.
19329      * If remote sorting is used, the sort is performed on the server, and the cache is
19330      * reloaded. If local sorting is used, the cache is sorted internally.
19331      * @param {String} fieldName The name of the field to sort by.
19332      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19333      */
19334     sort : function(fieldName, dir){
19335         var f = this.fields.get(fieldName);
19336         if(!dir){
19337             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19338                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19339             }else{
19340                 dir = f.sortDir;
19341             }
19342         }
19343         this.sortToggle[f.name] = dir;
19344         this.sortInfo = {field: f.name, direction: dir};
19345         if(!this.remoteSort){
19346             this.applySort();
19347             this.fireEvent("datachanged", this);
19348         }else{
19349             this.load(this.lastOptions);
19350         }
19351     },
19352
19353     /**
19354      * Calls the specified function for each of the Records in the cache.
19355      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19356      * Returning <em>false</em> aborts and exits the iteration.
19357      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19358      */
19359     each : function(fn, scope){
19360         this.data.each(fn, scope);
19361     },
19362
19363     /**
19364      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19365      * (e.g., during paging).
19366      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19367      */
19368     getModifiedRecords : function(){
19369         return this.modified;
19370     },
19371
19372     // private
19373     createFilterFn : function(property, value, anyMatch){
19374         if(!value.exec){ // not a regex
19375             value = String(value);
19376             if(value.length == 0){
19377                 return false;
19378             }
19379             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19380         }
19381         return function(r){
19382             return value.test(r.data[property]);
19383         };
19384     },
19385
19386     /**
19387      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19388      * @param {String} property A field on your records
19389      * @param {Number} start The record index to start at (defaults to 0)
19390      * @param {Number} end The last record index to include (defaults to length - 1)
19391      * @return {Number} The sum
19392      */
19393     sum : function(property, start, end){
19394         var rs = this.data.items, v = 0;
19395         start = start || 0;
19396         end = (end || end === 0) ? end : rs.length-1;
19397
19398         for(var i = start; i <= end; i++){
19399             v += (rs[i].data[property] || 0);
19400         }
19401         return v;
19402     },
19403
19404     /**
19405      * Filter the records by a specified property.
19406      * @param {String} field A field on your records
19407      * @param {String/RegExp} value Either a string that the field
19408      * should start with or a RegExp to test against the field
19409      * @param {Boolean} anyMatch True to match any part not just the beginning
19410      */
19411     filter : function(property, value, anyMatch){
19412         var fn = this.createFilterFn(property, value, anyMatch);
19413         return fn ? this.filterBy(fn) : this.clearFilter();
19414     },
19415
19416     /**
19417      * Filter by a function. The specified function will be called with each
19418      * record in this data source. If the function returns true the record is included,
19419      * otherwise it is filtered.
19420      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19421      * @param {Object} scope (optional) The scope of the function (defaults to this)
19422      */
19423     filterBy : function(fn, scope){
19424         this.snapshot = this.snapshot || this.data;
19425         this.data = this.queryBy(fn, scope||this);
19426         this.fireEvent("datachanged", this);
19427     },
19428
19429     /**
19430      * Query the records by a specified property.
19431      * @param {String} field A field on your records
19432      * @param {String/RegExp} value Either a string that the field
19433      * should start with or a RegExp to test against the field
19434      * @param {Boolean} anyMatch True to match any part not just the beginning
19435      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19436      */
19437     query : function(property, value, anyMatch){
19438         var fn = this.createFilterFn(property, value, anyMatch);
19439         return fn ? this.queryBy(fn) : this.data.clone();
19440     },
19441
19442     /**
19443      * Query by a function. The specified function will be called with each
19444      * record in this data source. If the function returns true the record is included
19445      * in the results.
19446      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19447      * @param {Object} scope (optional) The scope of the function (defaults to this)
19448       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19449      **/
19450     queryBy : function(fn, scope){
19451         var data = this.snapshot || this.data;
19452         return data.filterBy(fn, scope||this);
19453     },
19454
19455     /**
19456      * Collects unique values for a particular dataIndex from this store.
19457      * @param {String} dataIndex The property to collect
19458      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19459      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19460      * @return {Array} An array of the unique values
19461      **/
19462     collect : function(dataIndex, allowNull, bypassFilter){
19463         var d = (bypassFilter === true && this.snapshot) ?
19464                 this.snapshot.items : this.data.items;
19465         var v, sv, r = [], l = {};
19466         for(var i = 0, len = d.length; i < len; i++){
19467             v = d[i].data[dataIndex];
19468             sv = String(v);
19469             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19470                 l[sv] = true;
19471                 r[r.length] = v;
19472             }
19473         }
19474         return r;
19475     },
19476
19477     /**
19478      * Revert to a view of the Record cache with no filtering applied.
19479      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19480      */
19481     clearFilter : function(suppressEvent){
19482         if(this.snapshot && this.snapshot != this.data){
19483             this.data = this.snapshot;
19484             delete this.snapshot;
19485             if(suppressEvent !== true){
19486                 this.fireEvent("datachanged", this);
19487             }
19488         }
19489     },
19490
19491     // private
19492     afterEdit : function(record){
19493         if(this.modified.indexOf(record) == -1){
19494             this.modified.push(record);
19495         }
19496         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19497     },
19498
19499     // private
19500     afterReject : function(record){
19501         this.modified.remove(record);
19502         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19503     },
19504
19505     // private
19506     afterCommit : function(record){
19507         this.modified.remove(record);
19508         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19509     },
19510
19511     /**
19512      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19513      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19514      */
19515     commitChanges : function(){
19516         var m = this.modified.slice(0);
19517         this.modified = [];
19518         for(var i = 0, len = m.length; i < len; i++){
19519             m[i].commit();
19520         }
19521     },
19522
19523     /**
19524      * Cancel outstanding changes on all changed records.
19525      */
19526     rejectChanges : function(){
19527         var m = this.modified.slice(0);
19528         this.modified = [];
19529         for(var i = 0, len = m.length; i < len; i++){
19530             m[i].reject();
19531         }
19532     },
19533
19534     onMetaChange : function(meta, rtype, o){
19535         this.recordType = rtype;
19536         this.fields = rtype.prototype.fields;
19537         delete this.snapshot;
19538         this.sortInfo = meta.sortInfo;
19539         this.modified = [];
19540         this.fireEvent('metachange', this, this.reader.meta);
19541     }
19542 });/*
19543  * Based on:
19544  * Ext JS Library 1.1.1
19545  * Copyright(c) 2006-2007, Ext JS, LLC.
19546  *
19547  * Originally Released Under LGPL - original licence link has changed is not relivant.
19548  *
19549  * Fork - LGPL
19550  * <script type="text/javascript">
19551  */
19552
19553 /**
19554  * @class Roo.data.SimpleStore
19555  * @extends Roo.data.Store
19556  * Small helper class to make creating Stores from Array data easier.
19557  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19558  * @cfg {Array} fields An array of field definition objects, or field name strings.
19559  * @cfg {Array} data The multi-dimensional array of data
19560  * @constructor
19561  * @param {Object} config
19562  */
19563 Roo.data.SimpleStore = function(config){
19564     Roo.data.SimpleStore.superclass.constructor.call(this, {
19565         isLocal : true,
19566         reader: new Roo.data.ArrayReader({
19567                 id: config.id
19568             },
19569             Roo.data.Record.create(config.fields)
19570         ),
19571         proxy : new Roo.data.MemoryProxy(config.data)
19572     });
19573     this.load();
19574 };
19575 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19576  * Based on:
19577  * Ext JS Library 1.1.1
19578  * Copyright(c) 2006-2007, Ext JS, LLC.
19579  *
19580  * Originally Released Under LGPL - original licence link has changed is not relivant.
19581  *
19582  * Fork - LGPL
19583  * <script type="text/javascript">
19584  */
19585
19586 /**
19587 /**
19588  * @extends Roo.data.Store
19589  * @class Roo.data.JsonStore
19590  * Small helper class to make creating Stores for JSON data easier. <br/>
19591 <pre><code>
19592 var store = new Roo.data.JsonStore({
19593     url: 'get-images.php',
19594     root: 'images',
19595     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19596 });
19597 </code></pre>
19598  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19599  * JsonReader and HttpProxy (unless inline data is provided).</b>
19600  * @cfg {Array} fields An array of field definition objects, or field name strings.
19601  * @constructor
19602  * @param {Object} config
19603  */
19604 Roo.data.JsonStore = function(c){
19605     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19606         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19607         reader: new Roo.data.JsonReader(c, c.fields)
19608     }));
19609 };
19610 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620
19621  
19622 Roo.data.Field = function(config){
19623     if(typeof config == "string"){
19624         config = {name: config};
19625     }
19626     Roo.apply(this, config);
19627     
19628     if(!this.type){
19629         this.type = "auto";
19630     }
19631     
19632     var st = Roo.data.SortTypes;
19633     // named sortTypes are supported, here we look them up
19634     if(typeof this.sortType == "string"){
19635         this.sortType = st[this.sortType];
19636     }
19637     
19638     // set default sortType for strings and dates
19639     if(!this.sortType){
19640         switch(this.type){
19641             case "string":
19642                 this.sortType = st.asUCString;
19643                 break;
19644             case "date":
19645                 this.sortType = st.asDate;
19646                 break;
19647             default:
19648                 this.sortType = st.none;
19649         }
19650     }
19651
19652     // define once
19653     var stripRe = /[\$,%]/g;
19654
19655     // prebuilt conversion function for this field, instead of
19656     // switching every time we're reading a value
19657     if(!this.convert){
19658         var cv, dateFormat = this.dateFormat;
19659         switch(this.type){
19660             case "":
19661             case "auto":
19662             case undefined:
19663                 cv = function(v){ return v; };
19664                 break;
19665             case "string":
19666                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19667                 break;
19668             case "int":
19669                 cv = function(v){
19670                     return v !== undefined && v !== null && v !== '' ?
19671                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19672                     };
19673                 break;
19674             case "float":
19675                 cv = function(v){
19676                     return v !== undefined && v !== null && v !== '' ?
19677                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19678                     };
19679                 break;
19680             case "bool":
19681             case "boolean":
19682                 cv = function(v){ return v === true || v === "true" || v == 1; };
19683                 break;
19684             case "date":
19685                 cv = function(v){
19686                     if(!v){
19687                         return '';
19688                     }
19689                     if(v instanceof Date){
19690                         return v;
19691                     }
19692                     if(dateFormat){
19693                         if(dateFormat == "timestamp"){
19694                             return new Date(v*1000);
19695                         }
19696                         return Date.parseDate(v, dateFormat);
19697                     }
19698                     var parsed = Date.parse(v);
19699                     return parsed ? new Date(parsed) : null;
19700                 };
19701              break;
19702             
19703         }
19704         this.convert = cv;
19705     }
19706 };
19707
19708 Roo.data.Field.prototype = {
19709     dateFormat: null,
19710     defaultValue: "",
19711     mapping: null,
19712     sortType : null,
19713     sortDir : "ASC"
19714 };/*
19715  * Based on:
19716  * Ext JS Library 1.1.1
19717  * Copyright(c) 2006-2007, Ext JS, LLC.
19718  *
19719  * Originally Released Under LGPL - original licence link has changed is not relivant.
19720  *
19721  * Fork - LGPL
19722  * <script type="text/javascript">
19723  */
19724  
19725 // Base class for reading structured data from a data source.  This class is intended to be
19726 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19727
19728 /**
19729  * @class Roo.data.DataReader
19730  * Base class for reading structured data from a data source.  This class is intended to be
19731  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19732  */
19733
19734 Roo.data.DataReader = function(meta, recordType){
19735     
19736     this.meta = meta;
19737     
19738     this.recordType = recordType instanceof Array ? 
19739         Roo.data.Record.create(recordType) : recordType;
19740 };
19741
19742 Roo.data.DataReader.prototype = {
19743      /**
19744      * Create an empty record
19745      * @param {Object} data (optional) - overlay some values
19746      * @return {Roo.data.Record} record created.
19747      */
19748     newRow :  function(d) {
19749         var da =  {};
19750         this.recordType.prototype.fields.each(function(c) {
19751             switch( c.type) {
19752                 case 'int' : da[c.name] = 0; break;
19753                 case 'date' : da[c.name] = new Date(); break;
19754                 case 'float' : da[c.name] = 0.0; break;
19755                 case 'boolean' : da[c.name] = false; break;
19756                 default : da[c.name] = ""; break;
19757             }
19758             
19759         });
19760         return new this.recordType(Roo.apply(da, d));
19761     }
19762     
19763 };/*
19764  * Based on:
19765  * Ext JS Library 1.1.1
19766  * Copyright(c) 2006-2007, Ext JS, LLC.
19767  *
19768  * Originally Released Under LGPL - original licence link has changed is not relivant.
19769  *
19770  * Fork - LGPL
19771  * <script type="text/javascript">
19772  */
19773
19774 /**
19775  * @class Roo.data.DataProxy
19776  * @extends Roo.data.Observable
19777  * This class is an abstract base class for implementations which provide retrieval of
19778  * unformatted data objects.<br>
19779  * <p>
19780  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19781  * (of the appropriate type which knows how to parse the data object) to provide a block of
19782  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19783  * <p>
19784  * Custom implementations must implement the load method as described in
19785  * {@link Roo.data.HttpProxy#load}.
19786  */
19787 Roo.data.DataProxy = function(){
19788     this.addEvents({
19789         /**
19790          * @event beforeload
19791          * Fires before a network request is made to retrieve a data object.
19792          * @param {Object} This DataProxy object.
19793          * @param {Object} params The params parameter to the load function.
19794          */
19795         beforeload : true,
19796         /**
19797          * @event load
19798          * Fires before the load method's callback is called.
19799          * @param {Object} This DataProxy object.
19800          * @param {Object} o The data object.
19801          * @param {Object} arg The callback argument object passed to the load function.
19802          */
19803         load : true,
19804         /**
19805          * @event loadexception
19806          * Fires if an Exception occurs during data retrieval.
19807          * @param {Object} This DataProxy object.
19808          * @param {Object} o The data object.
19809          * @param {Object} arg The callback argument object passed to the load function.
19810          * @param {Object} e The Exception.
19811          */
19812         loadexception : true
19813     });
19814     Roo.data.DataProxy.superclass.constructor.call(this);
19815 };
19816
19817 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19818
19819     /**
19820      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19821      */
19822 /*
19823  * Based on:
19824  * Ext JS Library 1.1.1
19825  * Copyright(c) 2006-2007, Ext JS, LLC.
19826  *
19827  * Originally Released Under LGPL - original licence link has changed is not relivant.
19828  *
19829  * Fork - LGPL
19830  * <script type="text/javascript">
19831  */
19832 /**
19833  * @class Roo.data.MemoryProxy
19834  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19835  * to the Reader when its load method is called.
19836  * @constructor
19837  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19838  */
19839 Roo.data.MemoryProxy = function(data){
19840     if (data.data) {
19841         data = data.data;
19842     }
19843     Roo.data.MemoryProxy.superclass.constructor.call(this);
19844     this.data = data;
19845 };
19846
19847 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19848     /**
19849      * Load data from the requested source (in this case an in-memory
19850      * data object passed to the constructor), read the data object into
19851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19852      * process that block using the passed callback.
19853      * @param {Object} params This parameter is not used by the MemoryProxy class.
19854      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19855      * object into a block of Roo.data.Records.
19856      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19857      * The function must be passed <ul>
19858      * <li>The Record block object</li>
19859      * <li>The "arg" argument from the load function</li>
19860      * <li>A boolean success indicator</li>
19861      * </ul>
19862      * @param {Object} scope The scope in which to call the callback
19863      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19864      */
19865     load : function(params, reader, callback, scope, arg){
19866         params = params || {};
19867         var result;
19868         try {
19869             result = reader.readRecords(this.data);
19870         }catch(e){
19871             this.fireEvent("loadexception", this, arg, null, e);
19872             callback.call(scope, null, arg, false);
19873             return;
19874         }
19875         callback.call(scope, result, arg, true);
19876     },
19877     
19878     // private
19879     update : function(params, records){
19880         
19881     }
19882 });/*
19883  * Based on:
19884  * Ext JS Library 1.1.1
19885  * Copyright(c) 2006-2007, Ext JS, LLC.
19886  *
19887  * Originally Released Under LGPL - original licence link has changed is not relivant.
19888  *
19889  * Fork - LGPL
19890  * <script type="text/javascript">
19891  */
19892 /**
19893  * @class Roo.data.HttpProxy
19894  * @extends Roo.data.DataProxy
19895  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19896  * configured to reference a certain URL.<br><br>
19897  * <p>
19898  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19899  * from which the running page was served.<br><br>
19900  * <p>
19901  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19902  * <p>
19903  * Be aware that to enable the browser to parse an XML document, the server must set
19904  * the Content-Type header in the HTTP response to "text/xml".
19905  * @constructor
19906  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19907  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19908  * will be used to make the request.
19909  */
19910 Roo.data.HttpProxy = function(conn){
19911     Roo.data.HttpProxy.superclass.constructor.call(this);
19912     // is conn a conn config or a real conn?
19913     this.conn = conn;
19914     this.useAjax = !conn || !conn.events;
19915   
19916 };
19917
19918 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19919     // thse are take from connection...
19920     
19921     /**
19922      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19923      */
19924     /**
19925      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19926      * extra parameters to each request made by this object. (defaults to undefined)
19927      */
19928     /**
19929      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19930      *  to each request made by this object. (defaults to undefined)
19931      */
19932     /**
19933      * @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)
19934      */
19935     /**
19936      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19937      */
19938      /**
19939      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19940      * @type Boolean
19941      */
19942   
19943
19944     /**
19945      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19946      * @type Boolean
19947      */
19948     /**
19949      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19950      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19951      * a finer-grained basis than the DataProxy events.
19952      */
19953     getConnection : function(){
19954         return this.useAjax ? Roo.Ajax : this.conn;
19955     },
19956
19957     /**
19958      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19959      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19960      * process that block using the passed callback.
19961      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19962      * for the request to the remote server.
19963      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19964      * object into a block of Roo.data.Records.
19965      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19966      * The function must be passed <ul>
19967      * <li>The Record block object</li>
19968      * <li>The "arg" argument from the load function</li>
19969      * <li>A boolean success indicator</li>
19970      * </ul>
19971      * @param {Object} scope The scope in which to call the callback
19972      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19973      */
19974     load : function(params, reader, callback, scope, arg){
19975         if(this.fireEvent("beforeload", this, params) !== false){
19976             var  o = {
19977                 params : params || {},
19978                 request: {
19979                     callback : callback,
19980                     scope : scope,
19981                     arg : arg
19982                 },
19983                 reader: reader,
19984                 callback : this.loadResponse,
19985                 scope: this
19986             };
19987             if(this.useAjax){
19988                 Roo.applyIf(o, this.conn);
19989                 if(this.activeRequest){
19990                     Roo.Ajax.abort(this.activeRequest);
19991                 }
19992                 this.activeRequest = Roo.Ajax.request(o);
19993             }else{
19994                 this.conn.request(o);
19995             }
19996         }else{
19997             callback.call(scope||this, null, arg, false);
19998         }
19999     },
20000
20001     // private
20002     loadResponse : function(o, success, response){
20003         delete this.activeRequest;
20004         if(!success){
20005             this.fireEvent("loadexception", this, o, response);
20006             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20007             return;
20008         }
20009         var result;
20010         try {
20011             result = o.reader.read(response);
20012         }catch(e){
20013             this.fireEvent("loadexception", this, o, response, e);
20014             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20015             return;
20016         }
20017         
20018         this.fireEvent("load", this, o, o.request.arg);
20019         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20020     },
20021
20022     // private
20023     update : function(dataSet){
20024
20025     },
20026
20027     // private
20028     updateResponse : function(dataSet){
20029
20030     }
20031 });/*
20032  * Based on:
20033  * Ext JS Library 1.1.1
20034  * Copyright(c) 2006-2007, Ext JS, LLC.
20035  *
20036  * Originally Released Under LGPL - original licence link has changed is not relivant.
20037  *
20038  * Fork - LGPL
20039  * <script type="text/javascript">
20040  */
20041
20042 /**
20043  * @class Roo.data.ScriptTagProxy
20044  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20045  * other than the originating domain of the running page.<br><br>
20046  * <p>
20047  * <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
20048  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20049  * <p>
20050  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20051  * source code that is used as the source inside a &lt;script> tag.<br><br>
20052  * <p>
20053  * In order for the browser to process the returned data, the server must wrap the data object
20054  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20055  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20056  * depending on whether the callback name was passed:
20057  * <p>
20058  * <pre><code>
20059 boolean scriptTag = false;
20060 String cb = request.getParameter("callback");
20061 if (cb != null) {
20062     scriptTag = true;
20063     response.setContentType("text/javascript");
20064 } else {
20065     response.setContentType("application/x-json");
20066 }
20067 Writer out = response.getWriter();
20068 if (scriptTag) {
20069     out.write(cb + "(");
20070 }
20071 out.print(dataBlock.toJsonString());
20072 if (scriptTag) {
20073     out.write(");");
20074 }
20075 </pre></code>
20076  *
20077  * @constructor
20078  * @param {Object} config A configuration object.
20079  */
20080 Roo.data.ScriptTagProxy = function(config){
20081     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20082     Roo.apply(this, config);
20083     this.head = document.getElementsByTagName("head")[0];
20084 };
20085
20086 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20087
20088 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20089     /**
20090      * @cfg {String} url The URL from which to request the data object.
20091      */
20092     /**
20093      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20094      */
20095     timeout : 30000,
20096     /**
20097      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20098      * the server the name of the callback function set up by the load call to process the returned data object.
20099      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20100      * javascript output which calls this named function passing the data object as its only parameter.
20101      */
20102     callbackParam : "callback",
20103     /**
20104      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20105      * name to the request.
20106      */
20107     nocache : true,
20108
20109     /**
20110      * Load data from the configured URL, read the data object into
20111      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20112      * process that block using the passed callback.
20113      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20114      * for the request to the remote server.
20115      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20116      * object into a block of Roo.data.Records.
20117      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20118      * The function must be passed <ul>
20119      * <li>The Record block object</li>
20120      * <li>The "arg" argument from the load function</li>
20121      * <li>A boolean success indicator</li>
20122      * </ul>
20123      * @param {Object} scope The scope in which to call the callback
20124      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20125      */
20126     load : function(params, reader, callback, scope, arg){
20127         if(this.fireEvent("beforeload", this, params) !== false){
20128
20129             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20130
20131             var url = this.url;
20132             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20133             if(this.nocache){
20134                 url += "&_dc=" + (new Date().getTime());
20135             }
20136             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20137             var trans = {
20138                 id : transId,
20139                 cb : "stcCallback"+transId,
20140                 scriptId : "stcScript"+transId,
20141                 params : params,
20142                 arg : arg,
20143                 url : url,
20144                 callback : callback,
20145                 scope : scope,
20146                 reader : reader
20147             };
20148             var conn = this;
20149
20150             window[trans.cb] = function(o){
20151                 conn.handleResponse(o, trans);
20152             };
20153
20154             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20155
20156             if(this.autoAbort !== false){
20157                 this.abort();
20158             }
20159
20160             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20161
20162             var script = document.createElement("script");
20163             script.setAttribute("src", url);
20164             script.setAttribute("type", "text/javascript");
20165             script.setAttribute("id", trans.scriptId);
20166             this.head.appendChild(script);
20167
20168             this.trans = trans;
20169         }else{
20170             callback.call(scope||this, null, arg, false);
20171         }
20172     },
20173
20174     // private
20175     isLoading : function(){
20176         return this.trans ? true : false;
20177     },
20178
20179     /**
20180      * Abort the current server request.
20181      */
20182     abort : function(){
20183         if(this.isLoading()){
20184             this.destroyTrans(this.trans);
20185         }
20186     },
20187
20188     // private
20189     destroyTrans : function(trans, isLoaded){
20190         this.head.removeChild(document.getElementById(trans.scriptId));
20191         clearTimeout(trans.timeoutId);
20192         if(isLoaded){
20193             window[trans.cb] = undefined;
20194             try{
20195                 delete window[trans.cb];
20196             }catch(e){}
20197         }else{
20198             // if hasn't been loaded, wait for load to remove it to prevent script error
20199             window[trans.cb] = function(){
20200                 window[trans.cb] = undefined;
20201                 try{
20202                     delete window[trans.cb];
20203                 }catch(e){}
20204             };
20205         }
20206     },
20207
20208     // private
20209     handleResponse : function(o, trans){
20210         this.trans = false;
20211         this.destroyTrans(trans, true);
20212         var result;
20213         try {
20214             result = trans.reader.readRecords(o);
20215         }catch(e){
20216             this.fireEvent("loadexception", this, o, trans.arg, e);
20217             trans.callback.call(trans.scope||window, null, trans.arg, false);
20218             return;
20219         }
20220         this.fireEvent("load", this, o, trans.arg);
20221         trans.callback.call(trans.scope||window, result, trans.arg, true);
20222     },
20223
20224     // private
20225     handleFailure : function(trans){
20226         this.trans = false;
20227         this.destroyTrans(trans, false);
20228         this.fireEvent("loadexception", this, null, trans.arg);
20229         trans.callback.call(trans.scope||window, null, trans.arg, false);
20230     }
20231 });/*
20232  * Based on:
20233  * Ext JS Library 1.1.1
20234  * Copyright(c) 2006-2007, Ext JS, LLC.
20235  *
20236  * Originally Released Under LGPL - original licence link has changed is not relivant.
20237  *
20238  * Fork - LGPL
20239  * <script type="text/javascript">
20240  */
20241
20242 /**
20243  * @class Roo.data.JsonReader
20244  * @extends Roo.data.DataReader
20245  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20246  * based on mappings in a provided Roo.data.Record constructor.
20247  * <p>
20248  * Example code:
20249  * <pre><code>
20250 var RecordDef = Roo.data.Record.create([
20251     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20252     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20253 ]);
20254 var myReader = new Roo.data.JsonReader({
20255     totalProperty: "results",    // The property which contains the total dataset size (optional)
20256     root: "rows",                // The property which contains an Array of row objects
20257     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20258 }, RecordDef);
20259 </code></pre>
20260  * <p>
20261  * This would consume a JSON file like this:
20262  * <pre><code>
20263 { 'results': 2, 'rows': [
20264     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20265     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20266 }
20267 </code></pre>
20268  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20269  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20270  * paged from the remote server.
20271  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20272  * @cfg {String} root name of the property which contains the Array of row objects.
20273  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20274  * @constructor
20275  * Create a new JsonReader
20276  * @param {Object} meta Metadata configuration options
20277  * @param {Object} recordType Either an Array of field definition objects,
20278  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20279  */
20280 Roo.data.JsonReader = function(meta, recordType){
20281     
20282     meta = meta || {};
20283     // set some defaults:
20284     Roo.applyIf(meta, {
20285         totalProperty: 'total',
20286         successProperty : 'success',
20287         root : 'data',
20288         id : 'id'
20289     });
20290     
20291     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20292 };
20293 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20294     /**
20295      * This method is only used by a DataProxy which has retrieved data from a remote server.
20296      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20297      * @return {Object} data A data block which is used by an Roo.data.Store object as
20298      * a cache of Roo.data.Records.
20299      */
20300     read : function(response){
20301         var json = response.responseText;
20302         /* eval:var:o */
20303         var o = eval("("+json+")");
20304         if(!o) {
20305             throw {message: "JsonReader.read: Json object not found"};
20306         }
20307         
20308         if(o.metaData){
20309             delete this.ef;
20310             this.meta = o.metaData;
20311             this.recordType = Roo.data.Record.create(o.metaData.fields);
20312             this.onMetaChange(this.meta, this.recordType, o);
20313         }
20314         return this.readRecords(o);
20315     },
20316
20317     // private function a store will implement
20318     onMetaChange : function(meta, recordType, o){
20319
20320     },
20321
20322     /**
20323          * @ignore
20324          */
20325     simpleAccess: function(obj, subsc) {
20326         return obj[subsc];
20327     },
20328
20329         /**
20330          * @ignore
20331          */
20332     getJsonAccessor: function(){
20333         var re = /[\[\.]/;
20334         return function(expr) {
20335             try {
20336                 return(re.test(expr))
20337                     ? new Function("obj", "return obj." + expr)
20338                     : function(obj){
20339                         return obj[expr];
20340                     };
20341             } catch(e){}
20342             return Roo.emptyFn;
20343         };
20344     }(),
20345
20346     /**
20347      * Create a data block containing Roo.data.Records from an XML document.
20348      * @param {Object} o An object which contains an Array of row objects in the property specified
20349      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20350      * which contains the total size of the dataset.
20351      * @return {Object} data A data block which is used by an Roo.data.Store object as
20352      * a cache of Roo.data.Records.
20353      */
20354     readRecords : function(o){
20355         /**
20356          * After any data loads, the raw JSON data is available for further custom processing.
20357          * @type Object
20358          */
20359         this.jsonData = o;
20360         var s = this.meta, Record = this.recordType,
20361             f = Record.prototype.fields, fi = f.items, fl = f.length;
20362
20363 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20364         if (!this.ef) {
20365             if(s.totalProperty) {
20366                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20367                 }
20368                 if(s.successProperty) {
20369                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20370                 }
20371                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20372                 if (s.id) {
20373                         var g = this.getJsonAccessor(s.id);
20374                         this.getId = function(rec) {
20375                                 var r = g(rec);
20376                                 return (r === undefined || r === "") ? null : r;
20377                         };
20378                 } else {
20379                         this.getId = function(){return null;};
20380                 }
20381             this.ef = [];
20382             for(var i = 0; i < fl; i++){
20383                 f = fi[i];
20384                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20385                 this.ef[i] = this.getJsonAccessor(map);
20386             }
20387         }
20388
20389         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20390         if(s.totalProperty){
20391             var v = parseInt(this.getTotal(o), 10);
20392             if(!isNaN(v)){
20393                 totalRecords = v;
20394             }
20395         }
20396         if(s.successProperty){
20397             var v = this.getSuccess(o);
20398             if(v === false || v === 'false'){
20399                 success = false;
20400             }
20401         }
20402         var records = [];
20403             for(var i = 0; i < c; i++){
20404                     var n = root[i];
20405                 var values = {};
20406                 var id = this.getId(n);
20407                 for(var j = 0; j < fl; j++){
20408                     f = fi[j];
20409                 var v = this.ef[j](n);
20410                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20411                 }
20412                 var record = new Record(values, id);
20413                 record.json = n;
20414                 records[i] = record;
20415             }
20416             return {
20417                 success : success,
20418                 records : records,
20419                 totalRecords : totalRecords
20420             };
20421     }
20422 });/*
20423  * Based on:
20424  * Ext JS Library 1.1.1
20425  * Copyright(c) 2006-2007, Ext JS, LLC.
20426  *
20427  * Originally Released Under LGPL - original licence link has changed is not relivant.
20428  *
20429  * Fork - LGPL
20430  * <script type="text/javascript">
20431  */
20432
20433 /**
20434  * @class Roo.data.XmlReader
20435  * @extends Roo.data.DataReader
20436  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20437  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20438  * <p>
20439  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20440  * header in the HTTP response must be set to "text/xml".</em>
20441  * <p>
20442  * Example code:
20443  * <pre><code>
20444 var RecordDef = Roo.data.Record.create([
20445    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20446    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20447 ]);
20448 var myReader = new Roo.data.XmlReader({
20449    totalRecords: "results", // The element which contains the total dataset size (optional)
20450    record: "row",           // The repeated element which contains row information
20451    id: "id"                 // The element within the row that provides an ID for the record (optional)
20452 }, RecordDef);
20453 </code></pre>
20454  * <p>
20455  * This would consume an XML file like this:
20456  * <pre><code>
20457 &lt;?xml?>
20458 &lt;dataset>
20459  &lt;results>2&lt;/results>
20460  &lt;row>
20461    &lt;id>1&lt;/id>
20462    &lt;name>Bill&lt;/name>
20463    &lt;occupation>Gardener&lt;/occupation>
20464  &lt;/row>
20465  &lt;row>
20466    &lt;id>2&lt;/id>
20467    &lt;name>Ben&lt;/name>
20468    &lt;occupation>Horticulturalist&lt;/occupation>
20469  &lt;/row>
20470 &lt;/dataset>
20471 </code></pre>
20472  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20473  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20474  * paged from the remote server.
20475  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20476  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20477  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20478  * a record identifier value.
20479  * @constructor
20480  * Create a new XmlReader
20481  * @param {Object} meta Metadata configuration options
20482  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20483  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20484  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20485  */
20486 Roo.data.XmlReader = function(meta, recordType){
20487     meta = meta || {};
20488     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20489 };
20490 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20491     /**
20492      * This method is only used by a DataProxy which has retrieved data from a remote server.
20493          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20494          * to contain a method called 'responseXML' that returns an XML document object.
20495      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20496      * a cache of Roo.data.Records.
20497      */
20498     read : function(response){
20499         var doc = response.responseXML;
20500         if(!doc) {
20501             throw {message: "XmlReader.read: XML Document not available"};
20502         }
20503         return this.readRecords(doc);
20504     },
20505
20506     /**
20507      * Create a data block containing Roo.data.Records from an XML document.
20508          * @param {Object} doc A parsed XML document.
20509      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20510      * a cache of Roo.data.Records.
20511      */
20512     readRecords : function(doc){
20513         /**
20514          * After any data loads/reads, the raw XML Document is available for further custom processing.
20515          * @type XMLDocument
20516          */
20517         this.xmlData = doc;
20518         var root = doc.documentElement || doc;
20519         var q = Roo.DomQuery;
20520         var recordType = this.recordType, fields = recordType.prototype.fields;
20521         var sid = this.meta.id;
20522         var totalRecords = 0, success = true;
20523         if(this.meta.totalRecords){
20524             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20525         }
20526         
20527         if(this.meta.success){
20528             var sv = q.selectValue(this.meta.success, root, true);
20529             success = sv !== false && sv !== 'false';
20530         }
20531         var records = [];
20532         var ns = q.select(this.meta.record, root);
20533         for(var i = 0, len = ns.length; i < len; i++) {
20534                 var n = ns[i];
20535                 var values = {};
20536                 var id = sid ? q.selectValue(sid, n) : undefined;
20537                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20538                     var f = fields.items[j];
20539                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20540                     v = f.convert(v);
20541                     values[f.name] = v;
20542                 }
20543                 var record = new recordType(values, id);
20544                 record.node = n;
20545                 records[records.length] = record;
20546             }
20547
20548             return {
20549                 success : success,
20550                 records : records,
20551                 totalRecords : totalRecords || records.length
20552             };
20553     }
20554 });/*
20555  * Based on:
20556  * Ext JS Library 1.1.1
20557  * Copyright(c) 2006-2007, Ext JS, LLC.
20558  *
20559  * Originally Released Under LGPL - original licence link has changed is not relivant.
20560  *
20561  * Fork - LGPL
20562  * <script type="text/javascript">
20563  */
20564
20565 /**
20566  * @class Roo.data.ArrayReader
20567  * @extends Roo.data.DataReader
20568  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20569  * Each element of that Array represents a row of data fields. The
20570  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20571  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20572  * <p>
20573  * Example code:.
20574  * <pre><code>
20575 var RecordDef = Roo.data.Record.create([
20576     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20577     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20578 ]);
20579 var myReader = new Roo.data.ArrayReader({
20580     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20581 }, RecordDef);
20582 </code></pre>
20583  * <p>
20584  * This would consume an Array like this:
20585  * <pre><code>
20586 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20587   </code></pre>
20588  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20589  * @constructor
20590  * Create a new JsonReader
20591  * @param {Object} meta Metadata configuration options.
20592  * @param {Object} recordType Either an Array of field definition objects
20593  * as specified to {@link Roo.data.Record#create},
20594  * or an {@link Roo.data.Record} object
20595  * created using {@link Roo.data.Record#create}.
20596  */
20597 Roo.data.ArrayReader = function(meta, recordType){
20598     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20599 };
20600
20601 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20602     /**
20603      * Create a data block containing Roo.data.Records from an XML document.
20604      * @param {Object} o An Array of row objects which represents the dataset.
20605      * @return {Object} data A data block which is used by an Roo.data.Store object as
20606      * a cache of Roo.data.Records.
20607      */
20608     readRecords : function(o){
20609         var sid = this.meta ? this.meta.id : null;
20610         var recordType = this.recordType, fields = recordType.prototype.fields;
20611         var records = [];
20612         var root = o;
20613             for(var i = 0; i < root.length; i++){
20614                     var n = root[i];
20615                 var values = {};
20616                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20617                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20618                 var f = fields.items[j];
20619                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20620                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20621                 v = f.convert(v);
20622                 values[f.name] = v;
20623             }
20624                 var record = new recordType(values, id);
20625                 record.json = n;
20626                 records[records.length] = record;
20627             }
20628             return {
20629                 records : records,
20630                 totalRecords : records.length
20631             };
20632     }
20633 });/*
20634  * Based on:
20635  * Ext JS Library 1.1.1
20636  * Copyright(c) 2006-2007, Ext JS, LLC.
20637  *
20638  * Originally Released Under LGPL - original licence link has changed is not relivant.
20639  *
20640  * Fork - LGPL
20641  * <script type="text/javascript">
20642  */
20643
20644
20645 /**
20646  * @class Roo.data.Tree
20647  * @extends Roo.util.Observable
20648  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20649  * in the tree have most standard DOM functionality.
20650  * @constructor
20651  * @param {Node} root (optional) The root node
20652  */
20653 Roo.data.Tree = function(root){
20654    this.nodeHash = {};
20655    /**
20656     * The root node for this tree
20657     * @type Node
20658     */
20659    this.root = null;
20660    if(root){
20661        this.setRootNode(root);
20662    }
20663    this.addEvents({
20664        /**
20665         * @event append
20666         * Fires when a new child node is appended to a node in this tree.
20667         * @param {Tree} tree The owner tree
20668         * @param {Node} parent The parent node
20669         * @param {Node} node The newly appended node
20670         * @param {Number} index The index of the newly appended node
20671         */
20672        "append" : true,
20673        /**
20674         * @event remove
20675         * Fires when a child node is removed from a node in this tree.
20676         * @param {Tree} tree The owner tree
20677         * @param {Node} parent The parent node
20678         * @param {Node} node The child node removed
20679         */
20680        "remove" : true,
20681        /**
20682         * @event move
20683         * Fires when a node is moved to a new location in the tree
20684         * @param {Tree} tree The owner tree
20685         * @param {Node} node The node moved
20686         * @param {Node} oldParent The old parent of this node
20687         * @param {Node} newParent The new parent of this node
20688         * @param {Number} index The index it was moved to
20689         */
20690        "move" : true,
20691        /**
20692         * @event insert
20693         * Fires when a new child node is inserted in a node in this tree.
20694         * @param {Tree} tree The owner tree
20695         * @param {Node} parent The parent node
20696         * @param {Node} node The child node inserted
20697         * @param {Node} refNode The child node the node was inserted before
20698         */
20699        "insert" : true,
20700        /**
20701         * @event beforeappend
20702         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20703         * @param {Tree} tree The owner tree
20704         * @param {Node} parent The parent node
20705         * @param {Node} node The child node to be appended
20706         */
20707        "beforeappend" : true,
20708        /**
20709         * @event beforeremove
20710         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20711         * @param {Tree} tree The owner tree
20712         * @param {Node} parent The parent node
20713         * @param {Node} node The child node to be removed
20714         */
20715        "beforeremove" : true,
20716        /**
20717         * @event beforemove
20718         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20719         * @param {Tree} tree The owner tree
20720         * @param {Node} node The node being moved
20721         * @param {Node} oldParent The parent of the node
20722         * @param {Node} newParent The new parent the node is moving to
20723         * @param {Number} index The index it is being moved to
20724         */
20725        "beforemove" : true,
20726        /**
20727         * @event beforeinsert
20728         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20729         * @param {Tree} tree The owner tree
20730         * @param {Node} parent The parent node
20731         * @param {Node} node The child node to be inserted
20732         * @param {Node} refNode The child node the node is being inserted before
20733         */
20734        "beforeinsert" : true
20735    });
20736
20737     Roo.data.Tree.superclass.constructor.call(this);
20738 };
20739
20740 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20741     pathSeparator: "/",
20742
20743     proxyNodeEvent : function(){
20744         return this.fireEvent.apply(this, arguments);
20745     },
20746
20747     /**
20748      * Returns the root node for this tree.
20749      * @return {Node}
20750      */
20751     getRootNode : function(){
20752         return this.root;
20753     },
20754
20755     /**
20756      * Sets the root node for this tree.
20757      * @param {Node} node
20758      * @return {Node}
20759      */
20760     setRootNode : function(node){
20761         this.root = node;
20762         node.ownerTree = this;
20763         node.isRoot = true;
20764         this.registerNode(node);
20765         return node;
20766     },
20767
20768     /**
20769      * Gets a node in this tree by its id.
20770      * @param {String} id
20771      * @return {Node}
20772      */
20773     getNodeById : function(id){
20774         return this.nodeHash[id];
20775     },
20776
20777     registerNode : function(node){
20778         this.nodeHash[node.id] = node;
20779     },
20780
20781     unregisterNode : function(node){
20782         delete this.nodeHash[node.id];
20783     },
20784
20785     toString : function(){
20786         return "[Tree"+(this.id?" "+this.id:"")+"]";
20787     }
20788 });
20789
20790 /**
20791  * @class Roo.data.Node
20792  * @extends Roo.util.Observable
20793  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20794  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20795  * @constructor
20796  * @param {Object} attributes The attributes/config for the node
20797  */
20798 Roo.data.Node = function(attributes){
20799     /**
20800      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20801      * @type {Object}
20802      */
20803     this.attributes = attributes || {};
20804     this.leaf = this.attributes.leaf;
20805     /**
20806      * The node id. @type String
20807      */
20808     this.id = this.attributes.id;
20809     if(!this.id){
20810         this.id = Roo.id(null, "ynode-");
20811         this.attributes.id = this.id;
20812     }
20813     /**
20814      * All child nodes of this node. @type Array
20815      */
20816     this.childNodes = [];
20817     if(!this.childNodes.indexOf){ // indexOf is a must
20818         this.childNodes.indexOf = function(o){
20819             for(var i = 0, len = this.length; i < len; i++){
20820                 if(this[i] == o) return i;
20821             }
20822             return -1;
20823         };
20824     }
20825     /**
20826      * The parent node for this node. @type Node
20827      */
20828     this.parentNode = null;
20829     /**
20830      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20831      */
20832     this.firstChild = null;
20833     /**
20834      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20835      */
20836     this.lastChild = null;
20837     /**
20838      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20839      */
20840     this.previousSibling = null;
20841     /**
20842      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20843      */
20844     this.nextSibling = null;
20845
20846     this.addEvents({
20847        /**
20848         * @event append
20849         * Fires when a new child node is appended
20850         * @param {Tree} tree The owner tree
20851         * @param {Node} this This node
20852         * @param {Node} node The newly appended node
20853         * @param {Number} index The index of the newly appended node
20854         */
20855        "append" : true,
20856        /**
20857         * @event remove
20858         * Fires when a child node is removed
20859         * @param {Tree} tree The owner tree
20860         * @param {Node} this This node
20861         * @param {Node} node The removed node
20862         */
20863        "remove" : true,
20864        /**
20865         * @event move
20866         * Fires when this node is moved to a new location in the tree
20867         * @param {Tree} tree The owner tree
20868         * @param {Node} this This node
20869         * @param {Node} oldParent The old parent of this node
20870         * @param {Node} newParent The new parent of this node
20871         * @param {Number} index The index it was moved to
20872         */
20873        "move" : true,
20874        /**
20875         * @event insert
20876         * Fires when a new child node is inserted.
20877         * @param {Tree} tree The owner tree
20878         * @param {Node} this This node
20879         * @param {Node} node The child node inserted
20880         * @param {Node} refNode The child node the node was inserted before
20881         */
20882        "insert" : true,
20883        /**
20884         * @event beforeappend
20885         * Fires before a new child is appended, return false to cancel the append.
20886         * @param {Tree} tree The owner tree
20887         * @param {Node} this This node
20888         * @param {Node} node The child node to be appended
20889         */
20890        "beforeappend" : true,
20891        /**
20892         * @event beforeremove
20893         * Fires before a child is removed, return false to cancel the remove.
20894         * @param {Tree} tree The owner tree
20895         * @param {Node} this This node
20896         * @param {Node} node The child node to be removed
20897         */
20898        "beforeremove" : true,
20899        /**
20900         * @event beforemove
20901         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20902         * @param {Tree} tree The owner tree
20903         * @param {Node} this This node
20904         * @param {Node} oldParent The parent of this node
20905         * @param {Node} newParent The new parent this node is moving to
20906         * @param {Number} index The index it is being moved to
20907         */
20908        "beforemove" : true,
20909        /**
20910         * @event beforeinsert
20911         * Fires before a new child is inserted, return false to cancel the insert.
20912         * @param {Tree} tree The owner tree
20913         * @param {Node} this This node
20914         * @param {Node} node The child node to be inserted
20915         * @param {Node} refNode The child node the node is being inserted before
20916         */
20917        "beforeinsert" : true
20918    });
20919     this.listeners = this.attributes.listeners;
20920     Roo.data.Node.superclass.constructor.call(this);
20921 };
20922
20923 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20924     fireEvent : function(evtName){
20925         // first do standard event for this node
20926         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20927             return false;
20928         }
20929         // then bubble it up to the tree if the event wasn't cancelled
20930         var ot = this.getOwnerTree();
20931         if(ot){
20932             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20933                 return false;
20934             }
20935         }
20936         return true;
20937     },
20938
20939     /**
20940      * Returns true if this node is a leaf
20941      * @return {Boolean}
20942      */
20943     isLeaf : function(){
20944         return this.leaf === true;
20945     },
20946
20947     // private
20948     setFirstChild : function(node){
20949         this.firstChild = node;
20950     },
20951
20952     //private
20953     setLastChild : function(node){
20954         this.lastChild = node;
20955     },
20956
20957
20958     /**
20959      * Returns true if this node is the last child of its parent
20960      * @return {Boolean}
20961      */
20962     isLast : function(){
20963        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20964     },
20965
20966     /**
20967      * Returns true if this node is the first child of its parent
20968      * @return {Boolean}
20969      */
20970     isFirst : function(){
20971        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20972     },
20973
20974     hasChildNodes : function(){
20975         return !this.isLeaf() && this.childNodes.length > 0;
20976     },
20977
20978     /**
20979      * Insert node(s) as the last child node of this node.
20980      * @param {Node/Array} node The node or Array of nodes to append
20981      * @return {Node} The appended node if single append, or null if an array was passed
20982      */
20983     appendChild : function(node){
20984         var multi = false;
20985         if(node instanceof Array){
20986             multi = node;
20987         }else if(arguments.length > 1){
20988             multi = arguments;
20989         }
20990         // if passed an array or multiple args do them one by one
20991         if(multi){
20992             for(var i = 0, len = multi.length; i < len; i++) {
20993                 this.appendChild(multi[i]);
20994             }
20995         }else{
20996             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
20997                 return false;
20998             }
20999             var index = this.childNodes.length;
21000             var oldParent = node.parentNode;
21001             // it's a move, make sure we move it cleanly
21002             if(oldParent){
21003                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21004                     return false;
21005                 }
21006                 oldParent.removeChild(node);
21007             }
21008             index = this.childNodes.length;
21009             if(index == 0){
21010                 this.setFirstChild(node);
21011             }
21012             this.childNodes.push(node);
21013             node.parentNode = this;
21014             var ps = this.childNodes[index-1];
21015             if(ps){
21016                 node.previousSibling = ps;
21017                 ps.nextSibling = node;
21018             }else{
21019                 node.previousSibling = null;
21020             }
21021             node.nextSibling = null;
21022             this.setLastChild(node);
21023             node.setOwnerTree(this.getOwnerTree());
21024             this.fireEvent("append", this.ownerTree, this, node, index);
21025             if(oldParent){
21026                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21027             }
21028             return node;
21029         }
21030     },
21031
21032     /**
21033      * Removes a child node from this node.
21034      * @param {Node} node The node to remove
21035      * @return {Node} The removed node
21036      */
21037     removeChild : function(node){
21038         var index = this.childNodes.indexOf(node);
21039         if(index == -1){
21040             return false;
21041         }
21042         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21043             return false;
21044         }
21045
21046         // remove it from childNodes collection
21047         this.childNodes.splice(index, 1);
21048
21049         // update siblings
21050         if(node.previousSibling){
21051             node.previousSibling.nextSibling = node.nextSibling;
21052         }
21053         if(node.nextSibling){
21054             node.nextSibling.previousSibling = node.previousSibling;
21055         }
21056
21057         // update child refs
21058         if(this.firstChild == node){
21059             this.setFirstChild(node.nextSibling);
21060         }
21061         if(this.lastChild == node){
21062             this.setLastChild(node.previousSibling);
21063         }
21064
21065         node.setOwnerTree(null);
21066         // clear any references from the node
21067         node.parentNode = null;
21068         node.previousSibling = null;
21069         node.nextSibling = null;
21070         this.fireEvent("remove", this.ownerTree, this, node);
21071         return node;
21072     },
21073
21074     /**
21075      * Inserts the first node before the second node in this nodes childNodes collection.
21076      * @param {Node} node The node to insert
21077      * @param {Node} refNode The node to insert before (if null the node is appended)
21078      * @return {Node} The inserted node
21079      */
21080     insertBefore : function(node, refNode){
21081         if(!refNode){ // like standard Dom, refNode can be null for append
21082             return this.appendChild(node);
21083         }
21084         // nothing to do
21085         if(node == refNode){
21086             return false;
21087         }
21088
21089         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21090             return false;
21091         }
21092         var index = this.childNodes.indexOf(refNode);
21093         var oldParent = node.parentNode;
21094         var refIndex = index;
21095
21096         // when moving internally, indexes will change after remove
21097         if(oldParent == this && this.childNodes.indexOf(node) < index){
21098             refIndex--;
21099         }
21100
21101         // it's a move, make sure we move it cleanly
21102         if(oldParent){
21103             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21104                 return false;
21105             }
21106             oldParent.removeChild(node);
21107         }
21108         if(refIndex == 0){
21109             this.setFirstChild(node);
21110         }
21111         this.childNodes.splice(refIndex, 0, node);
21112         node.parentNode = this;
21113         var ps = this.childNodes[refIndex-1];
21114         if(ps){
21115             node.previousSibling = ps;
21116             ps.nextSibling = node;
21117         }else{
21118             node.previousSibling = null;
21119         }
21120         node.nextSibling = refNode;
21121         refNode.previousSibling = node;
21122         node.setOwnerTree(this.getOwnerTree());
21123         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21124         if(oldParent){
21125             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21126         }
21127         return node;
21128     },
21129
21130     /**
21131      * Returns the child node at the specified index.
21132      * @param {Number} index
21133      * @return {Node}
21134      */
21135     item : function(index){
21136         return this.childNodes[index];
21137     },
21138
21139     /**
21140      * Replaces one child node in this node with another.
21141      * @param {Node} newChild The replacement node
21142      * @param {Node} oldChild The node to replace
21143      * @return {Node} The replaced node
21144      */
21145     replaceChild : function(newChild, oldChild){
21146         this.insertBefore(newChild, oldChild);
21147         this.removeChild(oldChild);
21148         return oldChild;
21149     },
21150
21151     /**
21152      * Returns the index of a child node
21153      * @param {Node} node
21154      * @return {Number} The index of the node or -1 if it was not found
21155      */
21156     indexOf : function(child){
21157         return this.childNodes.indexOf(child);
21158     },
21159
21160     /**
21161      * Returns the tree this node is in.
21162      * @return {Tree}
21163      */
21164     getOwnerTree : function(){
21165         // if it doesn't have one, look for one
21166         if(!this.ownerTree){
21167             var p = this;
21168             while(p){
21169                 if(p.ownerTree){
21170                     this.ownerTree = p.ownerTree;
21171                     break;
21172                 }
21173                 p = p.parentNode;
21174             }
21175         }
21176         return this.ownerTree;
21177     },
21178
21179     /**
21180      * Returns depth of this node (the root node has a depth of 0)
21181      * @return {Number}
21182      */
21183     getDepth : function(){
21184         var depth = 0;
21185         var p = this;
21186         while(p.parentNode){
21187             ++depth;
21188             p = p.parentNode;
21189         }
21190         return depth;
21191     },
21192
21193     // private
21194     setOwnerTree : function(tree){
21195         // if it's move, we need to update everyone
21196         if(tree != this.ownerTree){
21197             if(this.ownerTree){
21198                 this.ownerTree.unregisterNode(this);
21199             }
21200             this.ownerTree = tree;
21201             var cs = this.childNodes;
21202             for(var i = 0, len = cs.length; i < len; i++) {
21203                 cs[i].setOwnerTree(tree);
21204             }
21205             if(tree){
21206                 tree.registerNode(this);
21207             }
21208         }
21209     },
21210
21211     /**
21212      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21213      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21214      * @return {String} The path
21215      */
21216     getPath : function(attr){
21217         attr = attr || "id";
21218         var p = this.parentNode;
21219         var b = [this.attributes[attr]];
21220         while(p){
21221             b.unshift(p.attributes[attr]);
21222             p = p.parentNode;
21223         }
21224         var sep = this.getOwnerTree().pathSeparator;
21225         return sep + b.join(sep);
21226     },
21227
21228     /**
21229      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21230      * function call will be the scope provided or the current node. The arguments to the function
21231      * will be the args provided or the current node. If the function returns false at any point,
21232      * the bubble is stopped.
21233      * @param {Function} fn The function to call
21234      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21235      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21236      */
21237     bubble : function(fn, scope, args){
21238         var p = this;
21239         while(p){
21240             if(fn.call(scope || p, args || p) === false){
21241                 break;
21242             }
21243             p = p.parentNode;
21244         }
21245     },
21246
21247     /**
21248      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21249      * function call will be the scope provided or the current node. The arguments to the function
21250      * will be the args provided or the current node. If the function returns false at any point,
21251      * the cascade is stopped on that branch.
21252      * @param {Function} fn The function to call
21253      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21254      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21255      */
21256     cascade : function(fn, scope, args){
21257         if(fn.call(scope || this, args || this) !== false){
21258             var cs = this.childNodes;
21259             for(var i = 0, len = cs.length; i < len; i++) {
21260                 cs[i].cascade(fn, scope, args);
21261             }
21262         }
21263     },
21264
21265     /**
21266      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21267      * function call will be the scope provided or the current node. The arguments to the function
21268      * will be the args provided or the current node. If the function returns false at any point,
21269      * the iteration stops.
21270      * @param {Function} fn The function to call
21271      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21272      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21273      */
21274     eachChild : function(fn, scope, args){
21275         var cs = this.childNodes;
21276         for(var i = 0, len = cs.length; i < len; i++) {
21277                 if(fn.call(scope || this, args || cs[i]) === false){
21278                     break;
21279                 }
21280         }
21281     },
21282
21283     /**
21284      * Finds the first child that has the attribute with the specified value.
21285      * @param {String} attribute The attribute name
21286      * @param {Mixed} value The value to search for
21287      * @return {Node} The found child or null if none was found
21288      */
21289     findChild : function(attribute, value){
21290         var cs = this.childNodes;
21291         for(var i = 0, len = cs.length; i < len; i++) {
21292                 if(cs[i].attributes[attribute] == value){
21293                     return cs[i];
21294                 }
21295         }
21296         return null;
21297     },
21298
21299     /**
21300      * Finds the first child by a custom function. The child matches if the function passed
21301      * returns true.
21302      * @param {Function} fn
21303      * @param {Object} scope (optional)
21304      * @return {Node} The found child or null if none was found
21305      */
21306     findChildBy : function(fn, scope){
21307         var cs = this.childNodes;
21308         for(var i = 0, len = cs.length; i < len; i++) {
21309                 if(fn.call(scope||cs[i], cs[i]) === true){
21310                     return cs[i];
21311                 }
21312         }
21313         return null;
21314     },
21315
21316     /**
21317      * Sorts this nodes children using the supplied sort function
21318      * @param {Function} fn
21319      * @param {Object} scope (optional)
21320      */
21321     sort : function(fn, scope){
21322         var cs = this.childNodes;
21323         var len = cs.length;
21324         if(len > 0){
21325             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21326             cs.sort(sortFn);
21327             for(var i = 0; i < len; i++){
21328                 var n = cs[i];
21329                 n.previousSibling = cs[i-1];
21330                 n.nextSibling = cs[i+1];
21331                 if(i == 0){
21332                     this.setFirstChild(n);
21333                 }
21334                 if(i == len-1){
21335                     this.setLastChild(n);
21336                 }
21337             }
21338         }
21339     },
21340
21341     /**
21342      * Returns true if this node is an ancestor (at any point) of the passed node.
21343      * @param {Node} node
21344      * @return {Boolean}
21345      */
21346     contains : function(node){
21347         return node.isAncestor(this);
21348     },
21349
21350     /**
21351      * Returns true if the passed node is an ancestor (at any point) of this node.
21352      * @param {Node} node
21353      * @return {Boolean}
21354      */
21355     isAncestor : function(node){
21356         var p = this.parentNode;
21357         while(p){
21358             if(p == node){
21359                 return true;
21360             }
21361             p = p.parentNode;
21362         }
21363         return false;
21364     },
21365
21366     toString : function(){
21367         return "[Node"+(this.id?" "+this.id:"")+"]";
21368     }
21369 });/*
21370  * Based on:
21371  * Ext JS Library 1.1.1
21372  * Copyright(c) 2006-2007, Ext JS, LLC.
21373  *
21374  * Originally Released Under LGPL - original licence link has changed is not relivant.
21375  *
21376  * Fork - LGPL
21377  * <script type="text/javascript">
21378  */
21379  
21380
21381 /**
21382  * @class Roo.ComponentMgr
21383  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21384  * @singleton
21385  */
21386 Roo.ComponentMgr = function(){
21387     var all = new Roo.util.MixedCollection();
21388
21389     return {
21390         /**
21391          * Registers a component.
21392          * @param {Roo.Component} c The component
21393          */
21394         register : function(c){
21395             all.add(c);
21396         },
21397
21398         /**
21399          * Unregisters a component.
21400          * @param {Roo.Component} c The component
21401          */
21402         unregister : function(c){
21403             all.remove(c);
21404         },
21405
21406         /**
21407          * Returns a component by id
21408          * @param {String} id The component id
21409          */
21410         get : function(id){
21411             return all.get(id);
21412         },
21413
21414         /**
21415          * Registers a function that will be called when a specified component is added to ComponentMgr
21416          * @param {String} id The component id
21417          * @param {Funtction} fn The callback function
21418          * @param {Object} scope The scope of the callback
21419          */
21420         onAvailable : function(id, fn, scope){
21421             all.on("add", function(index, o){
21422                 if(o.id == id){
21423                     fn.call(scope || o, o);
21424                     all.un("add", fn, scope);
21425                 }
21426             });
21427         }
21428     };
21429 }();/*
21430  * Based on:
21431  * Ext JS Library 1.1.1
21432  * Copyright(c) 2006-2007, Ext JS, LLC.
21433  *
21434  * Originally Released Under LGPL - original licence link has changed is not relivant.
21435  *
21436  * Fork - LGPL
21437  * <script type="text/javascript">
21438  */
21439  
21440 /**
21441  * @class Roo.Component
21442  * @extends Roo.util.Observable
21443  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21444  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21445  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21446  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21447  * All visual components (widgets) that require rendering into a layout should subclass Component.
21448  * @constructor
21449  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21450  * 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
21451  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21452  */
21453 Roo.Component = function(config){
21454     config = config || {};
21455     if(config.tagName || config.dom || typeof config == "string"){ // element object
21456         config = {el: config, id: config.id || config};
21457     }
21458     this.initialConfig = config;
21459
21460     Roo.apply(this, config);
21461     this.addEvents({
21462         /**
21463          * @event disable
21464          * Fires after the component is disabled.
21465              * @param {Roo.Component} this
21466              */
21467         disable : true,
21468         /**
21469          * @event enable
21470          * Fires after the component is enabled.
21471              * @param {Roo.Component} this
21472              */
21473         enable : true,
21474         /**
21475          * @event beforeshow
21476          * Fires before the component is shown.  Return false to stop the show.
21477              * @param {Roo.Component} this
21478              */
21479         beforeshow : true,
21480         /**
21481          * @event show
21482          * Fires after the component is shown.
21483              * @param {Roo.Component} this
21484              */
21485         show : true,
21486         /**
21487          * @event beforehide
21488          * Fires before the component is hidden. Return false to stop the hide.
21489              * @param {Roo.Component} this
21490              */
21491         beforehide : true,
21492         /**
21493          * @event hide
21494          * Fires after the component is hidden.
21495              * @param {Roo.Component} this
21496              */
21497         hide : true,
21498         /**
21499          * @event beforerender
21500          * Fires before the component is rendered. Return false to stop the render.
21501              * @param {Roo.Component} this
21502              */
21503         beforerender : true,
21504         /**
21505          * @event render
21506          * Fires after the component is rendered.
21507              * @param {Roo.Component} this
21508              */
21509         render : true,
21510         /**
21511          * @event beforedestroy
21512          * Fires before the component is destroyed. Return false to stop the destroy.
21513              * @param {Roo.Component} this
21514              */
21515         beforedestroy : true,
21516         /**
21517          * @event destroy
21518          * Fires after the component is destroyed.
21519              * @param {Roo.Component} this
21520              */
21521         destroy : true
21522     });
21523     if(!this.id){
21524         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21525     }
21526     Roo.ComponentMgr.register(this);
21527     Roo.Component.superclass.constructor.call(this);
21528     this.initComponent();
21529     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21530         this.render(this.renderTo);
21531         delete this.renderTo;
21532     }
21533 };
21534
21535 // private
21536 Roo.Component.AUTO_ID = 1000;
21537
21538 Roo.extend(Roo.Component, Roo.util.Observable, {
21539     /**
21540      * @property {Boolean} hidden
21541      * true if this component is hidden. Read-only.
21542      */
21543     hidden : false,
21544     /**
21545      * true if this component is disabled. Read-only.
21546      */
21547     disabled : false,
21548     /**
21549      * true if this component has been rendered. Read-only.
21550      */
21551     rendered : false,
21552     
21553     /** @cfg {String} disableClass
21554      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21555      */
21556     disabledClass : "x-item-disabled",
21557         /** @cfg {Boolean} allowDomMove
21558          * Whether the component can move the Dom node when rendering (defaults to true).
21559          */
21560     allowDomMove : true,
21561     /** @cfg {String} hideMode
21562      * How this component should hidden. Supported values are
21563      * "visibility" (css visibility), "offsets" (negative offset position) and
21564      * "display" (css display) - defaults to "display".
21565      */
21566     hideMode: 'display',
21567
21568     // private
21569     ctype : "Roo.Component",
21570
21571     /** @cfg {String} actionMode 
21572      * which property holds the element that used for  hide() / show() / disable() / enable()
21573      * default is 'el' 
21574      */
21575     actionMode : "el",
21576
21577     // private
21578     getActionEl : function(){
21579         return this[this.actionMode];
21580     },
21581
21582     initComponent : Roo.emptyFn,
21583     /**
21584      * If this is a lazy rendering component, render it to its container element.
21585      * @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.
21586      */
21587     render : function(container, position){
21588         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21589             if(!container && this.el){
21590                 this.el = Roo.get(this.el);
21591                 container = this.el.dom.parentNode;
21592                 this.allowDomMove = false;
21593             }
21594             this.container = Roo.get(container);
21595             this.rendered = true;
21596             if(position !== undefined){
21597                 if(typeof position == 'number'){
21598                     position = this.container.dom.childNodes[position];
21599                 }else{
21600                     position = Roo.getDom(position);
21601                 }
21602             }
21603             this.onRender(this.container, position || null);
21604             if(this.cls){
21605                 this.el.addClass(this.cls);
21606                 delete this.cls;
21607             }
21608             if(this.style){
21609                 this.el.applyStyles(this.style);
21610                 delete this.style;
21611             }
21612             this.fireEvent("render", this);
21613             this.afterRender(this.container);
21614             if(this.hidden){
21615                 this.hide();
21616             }
21617             if(this.disabled){
21618                 this.disable();
21619             }
21620         }
21621         return this;
21622     },
21623
21624     // private
21625     // default function is not really useful
21626     onRender : function(ct, position){
21627         if(this.el){
21628             this.el = Roo.get(this.el);
21629             if(this.allowDomMove !== false){
21630                 ct.dom.insertBefore(this.el.dom, position);
21631             }
21632         }
21633     },
21634
21635     // private
21636     getAutoCreate : function(){
21637         var cfg = typeof this.autoCreate == "object" ?
21638                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21639         if(this.id && !cfg.id){
21640             cfg.id = this.id;
21641         }
21642         return cfg;
21643     },
21644
21645     // private
21646     afterRender : Roo.emptyFn,
21647
21648     /**
21649      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21650      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21651      */
21652     destroy : function(){
21653         if(this.fireEvent("beforedestroy", this) !== false){
21654             this.purgeListeners();
21655             this.beforeDestroy();
21656             if(this.rendered){
21657                 this.el.removeAllListeners();
21658                 this.el.remove();
21659                 if(this.actionMode == "container"){
21660                     this.container.remove();
21661                 }
21662             }
21663             this.onDestroy();
21664             Roo.ComponentMgr.unregister(this);
21665             this.fireEvent("destroy", this);
21666         }
21667     },
21668
21669         // private
21670     beforeDestroy : function(){
21671
21672     },
21673
21674         // private
21675         onDestroy : function(){
21676
21677     },
21678
21679     /**
21680      * Returns the underlying {@link Roo.Element}.
21681      * @return {Roo.Element} The element
21682      */
21683     getEl : function(){
21684         return this.el;
21685     },
21686
21687     /**
21688      * Returns the id of this component.
21689      * @return {String}
21690      */
21691     getId : function(){
21692         return this.id;
21693     },
21694
21695     /**
21696      * Try to focus this component.
21697      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21698      * @return {Roo.Component} this
21699      */
21700     focus : function(selectText){
21701         if(this.rendered){
21702             this.el.focus();
21703             if(selectText === true){
21704                 this.el.dom.select();
21705             }
21706         }
21707         return this;
21708     },
21709
21710     // private
21711     blur : function(){
21712         if(this.rendered){
21713             this.el.blur();
21714         }
21715         return this;
21716     },
21717
21718     /**
21719      * Disable this component.
21720      * @return {Roo.Component} this
21721      */
21722     disable : function(){
21723         if(this.rendered){
21724             this.onDisable();
21725         }
21726         this.disabled = true;
21727         this.fireEvent("disable", this);
21728         return this;
21729     },
21730
21731         // private
21732     onDisable : function(){
21733         this.getActionEl().addClass(this.disabledClass);
21734         this.el.dom.disabled = true;
21735     },
21736
21737     /**
21738      * Enable this component.
21739      * @return {Roo.Component} this
21740      */
21741     enable : function(){
21742         if(this.rendered){
21743             this.onEnable();
21744         }
21745         this.disabled = false;
21746         this.fireEvent("enable", this);
21747         return this;
21748     },
21749
21750         // private
21751     onEnable : function(){
21752         this.getActionEl().removeClass(this.disabledClass);
21753         this.el.dom.disabled = false;
21754     },
21755
21756     /**
21757      * Convenience function for setting disabled/enabled by boolean.
21758      * @param {Boolean} disabled
21759      */
21760     setDisabled : function(disabled){
21761         this[disabled ? "disable" : "enable"]();
21762     },
21763
21764     /**
21765      * Show this component.
21766      * @return {Roo.Component} this
21767      */
21768     show: function(){
21769         if(this.fireEvent("beforeshow", this) !== false){
21770             this.hidden = false;
21771             if(this.rendered){
21772                 this.onShow();
21773             }
21774             this.fireEvent("show", this);
21775         }
21776         return this;
21777     },
21778
21779     // private
21780     onShow : function(){
21781         var ae = this.getActionEl();
21782         if(this.hideMode == 'visibility'){
21783             ae.dom.style.visibility = "visible";
21784         }else if(this.hideMode == 'offsets'){
21785             ae.removeClass('x-hidden');
21786         }else{
21787             ae.dom.style.display = "";
21788         }
21789     },
21790
21791     /**
21792      * Hide this component.
21793      * @return {Roo.Component} this
21794      */
21795     hide: function(){
21796         if(this.fireEvent("beforehide", this) !== false){
21797             this.hidden = true;
21798             if(this.rendered){
21799                 this.onHide();
21800             }
21801             this.fireEvent("hide", this);
21802         }
21803         return this;
21804     },
21805
21806     // private
21807     onHide : function(){
21808         var ae = this.getActionEl();
21809         if(this.hideMode == 'visibility'){
21810             ae.dom.style.visibility = "hidden";
21811         }else if(this.hideMode == 'offsets'){
21812             ae.addClass('x-hidden');
21813         }else{
21814             ae.dom.style.display = "none";
21815         }
21816     },
21817
21818     /**
21819      * Convenience function to hide or show this component by boolean.
21820      * @param {Boolean} visible True to show, false to hide
21821      * @return {Roo.Component} this
21822      */
21823     setVisible: function(visible){
21824         if(visible) {
21825             this.show();
21826         }else{
21827             this.hide();
21828         }
21829         return this;
21830     },
21831
21832     /**
21833      * Returns true if this component is visible.
21834      */
21835     isVisible : function(){
21836         return this.getActionEl().isVisible();
21837     },
21838
21839     cloneConfig : function(overrides){
21840         overrides = overrides || {};
21841         var id = overrides.id || Roo.id();
21842         var cfg = Roo.applyIf(overrides, this.initialConfig);
21843         cfg.id = id; // prevent dup id
21844         return new this.constructor(cfg);
21845     }
21846 });/*
21847  * Based on:
21848  * Ext JS Library 1.1.1
21849  * Copyright(c) 2006-2007, Ext JS, LLC.
21850  *
21851  * Originally Released Under LGPL - original licence link has changed is not relivant.
21852  *
21853  * Fork - LGPL
21854  * <script type="text/javascript">
21855  */
21856  (function(){ 
21857 /**
21858  * @class Roo.Layer
21859  * @extends Roo.Element
21860  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21861  * automatic maintaining of shadow/shim positions.
21862  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21863  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21864  * you can pass a string with a CSS class name. False turns off the shadow.
21865  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21866  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21867  * @cfg {String} cls CSS class to add to the element
21868  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21869  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21870  * @constructor
21871  * @param {Object} config An object with config options.
21872  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21873  */
21874
21875 Roo.Layer = function(config, existingEl){
21876     config = config || {};
21877     var dh = Roo.DomHelper;
21878     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21879     if(existingEl){
21880         this.dom = Roo.getDom(existingEl);
21881     }
21882     if(!this.dom){
21883         var o = config.dh || {tag: "div", cls: "x-layer"};
21884         this.dom = dh.append(pel, o);
21885     }
21886     if(config.cls){
21887         this.addClass(config.cls);
21888     }
21889     this.constrain = config.constrain !== false;
21890     this.visibilityMode = Roo.Element.VISIBILITY;
21891     if(config.id){
21892         this.id = this.dom.id = config.id;
21893     }else{
21894         this.id = Roo.id(this.dom);
21895     }
21896     this.zindex = config.zindex || this.getZIndex();
21897     this.position("absolute", this.zindex);
21898     if(config.shadow){
21899         this.shadowOffset = config.shadowOffset || 4;
21900         this.shadow = new Roo.Shadow({
21901             offset : this.shadowOffset,
21902             mode : config.shadow
21903         });
21904     }else{
21905         this.shadowOffset = 0;
21906     }
21907     this.useShim = config.shim !== false && Roo.useShims;
21908     this.useDisplay = config.useDisplay;
21909     this.hide();
21910 };
21911
21912 var supr = Roo.Element.prototype;
21913
21914 // shims are shared among layer to keep from having 100 iframes
21915 var shims = [];
21916
21917 Roo.extend(Roo.Layer, Roo.Element, {
21918
21919     getZIndex : function(){
21920         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21921     },
21922
21923     getShim : function(){
21924         if(!this.useShim){
21925             return null;
21926         }
21927         if(this.shim){
21928             return this.shim;
21929         }
21930         var shim = shims.shift();
21931         if(!shim){
21932             shim = this.createShim();
21933             shim.enableDisplayMode('block');
21934             shim.dom.style.display = 'none';
21935             shim.dom.style.visibility = 'visible';
21936         }
21937         var pn = this.dom.parentNode;
21938         if(shim.dom.parentNode != pn){
21939             pn.insertBefore(shim.dom, this.dom);
21940         }
21941         shim.setStyle('z-index', this.getZIndex()-2);
21942         this.shim = shim;
21943         return shim;
21944     },
21945
21946     hideShim : function(){
21947         if(this.shim){
21948             this.shim.setDisplayed(false);
21949             shims.push(this.shim);
21950             delete this.shim;
21951         }
21952     },
21953
21954     disableShadow : function(){
21955         if(this.shadow){
21956             this.shadowDisabled = true;
21957             this.shadow.hide();
21958             this.lastShadowOffset = this.shadowOffset;
21959             this.shadowOffset = 0;
21960         }
21961     },
21962
21963     enableShadow : function(show){
21964         if(this.shadow){
21965             this.shadowDisabled = false;
21966             this.shadowOffset = this.lastShadowOffset;
21967             delete this.lastShadowOffset;
21968             if(show){
21969                 this.sync(true);
21970             }
21971         }
21972     },
21973
21974     // private
21975     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21976     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21977     sync : function(doShow){
21978         var sw = this.shadow;
21979         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21980             var sh = this.getShim();
21981
21982             var w = this.getWidth(),
21983                 h = this.getHeight();
21984
21985             var l = this.getLeft(true),
21986                 t = this.getTop(true);
21987
21988             if(sw && !this.shadowDisabled){
21989                 if(doShow && !sw.isVisible()){
21990                     sw.show(this);
21991                 }else{
21992                     sw.realign(l, t, w, h);
21993                 }
21994                 if(sh){
21995                     if(doShow){
21996                        sh.show();
21997                     }
21998                     // fit the shim behind the shadow, so it is shimmed too
21999                     var a = sw.adjusts, s = sh.dom.style;
22000                     s.left = (Math.min(l, l+a.l))+"px";
22001                     s.top = (Math.min(t, t+a.t))+"px";
22002                     s.width = (w+a.w)+"px";
22003                     s.height = (h+a.h)+"px";
22004                 }
22005             }else if(sh){
22006                 if(doShow){
22007                    sh.show();
22008                 }
22009                 sh.setSize(w, h);
22010                 sh.setLeftTop(l, t);
22011             }
22012             
22013         }
22014     },
22015
22016     // private
22017     destroy : function(){
22018         this.hideShim();
22019         if(this.shadow){
22020             this.shadow.hide();
22021         }
22022         this.removeAllListeners();
22023         var pn = this.dom.parentNode;
22024         if(pn){
22025             pn.removeChild(this.dom);
22026         }
22027         Roo.Element.uncache(this.id);
22028     },
22029
22030     remove : function(){
22031         this.destroy();
22032     },
22033
22034     // private
22035     beginUpdate : function(){
22036         this.updating = true;
22037     },
22038
22039     // private
22040     endUpdate : function(){
22041         this.updating = false;
22042         this.sync(true);
22043     },
22044
22045     // private
22046     hideUnders : function(negOffset){
22047         if(this.shadow){
22048             this.shadow.hide();
22049         }
22050         this.hideShim();
22051     },
22052
22053     // private
22054     constrainXY : function(){
22055         if(this.constrain){
22056             var vw = Roo.lib.Dom.getViewWidth(),
22057                 vh = Roo.lib.Dom.getViewHeight();
22058             var s = Roo.get(document).getScroll();
22059
22060             var xy = this.getXY();
22061             var x = xy[0], y = xy[1];   
22062             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22063             // only move it if it needs it
22064             var moved = false;
22065             // first validate right/bottom
22066             if((x + w) > vw+s.left){
22067                 x = vw - w - this.shadowOffset;
22068                 moved = true;
22069             }
22070             if((y + h) > vh+s.top){
22071                 y = vh - h - this.shadowOffset;
22072                 moved = true;
22073             }
22074             // then make sure top/left isn't negative
22075             if(x < s.left){
22076                 x = s.left;
22077                 moved = true;
22078             }
22079             if(y < s.top){
22080                 y = s.top;
22081                 moved = true;
22082             }
22083             if(moved){
22084                 if(this.avoidY){
22085                     var ay = this.avoidY;
22086                     if(y <= ay && (y+h) >= ay){
22087                         y = ay-h-5;   
22088                     }
22089                 }
22090                 xy = [x, y];
22091                 this.storeXY(xy);
22092                 supr.setXY.call(this, xy);
22093                 this.sync();
22094             }
22095         }
22096     },
22097
22098     isVisible : function(){
22099         return this.visible;    
22100     },
22101
22102     // private
22103     showAction : function(){
22104         this.visible = true; // track visibility to prevent getStyle calls
22105         if(this.useDisplay === true){
22106             this.setDisplayed("");
22107         }else if(this.lastXY){
22108             supr.setXY.call(this, this.lastXY);
22109         }else if(this.lastLT){
22110             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22111         }
22112     },
22113
22114     // private
22115     hideAction : function(){
22116         this.visible = false;
22117         if(this.useDisplay === true){
22118             this.setDisplayed(false);
22119         }else{
22120             this.setLeftTop(-10000,-10000);
22121         }
22122     },
22123
22124     // overridden Element method
22125     setVisible : function(v, a, d, c, e){
22126         if(v){
22127             this.showAction();
22128         }
22129         if(a && v){
22130             var cb = function(){
22131                 this.sync(true);
22132                 if(c){
22133                     c();
22134                 }
22135             }.createDelegate(this);
22136             supr.setVisible.call(this, true, true, d, cb, e);
22137         }else{
22138             if(!v){
22139                 this.hideUnders(true);
22140             }
22141             var cb = c;
22142             if(a){
22143                 cb = function(){
22144                     this.hideAction();
22145                     if(c){
22146                         c();
22147                     }
22148                 }.createDelegate(this);
22149             }
22150             supr.setVisible.call(this, v, a, d, cb, e);
22151             if(v){
22152                 this.sync(true);
22153             }else if(!a){
22154                 this.hideAction();
22155             }
22156         }
22157     },
22158
22159     storeXY : function(xy){
22160         delete this.lastLT;
22161         this.lastXY = xy;
22162     },
22163
22164     storeLeftTop : function(left, top){
22165         delete this.lastXY;
22166         this.lastLT = [left, top];
22167     },
22168
22169     // private
22170     beforeFx : function(){
22171         this.beforeAction();
22172         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22173     },
22174
22175     // private
22176     afterFx : function(){
22177         Roo.Layer.superclass.afterFx.apply(this, arguments);
22178         this.sync(this.isVisible());
22179     },
22180
22181     // private
22182     beforeAction : function(){
22183         if(!this.updating && this.shadow){
22184             this.shadow.hide();
22185         }
22186     },
22187
22188     // overridden Element method
22189     setLeft : function(left){
22190         this.storeLeftTop(left, this.getTop(true));
22191         supr.setLeft.apply(this, arguments);
22192         this.sync();
22193     },
22194
22195     setTop : function(top){
22196         this.storeLeftTop(this.getLeft(true), top);
22197         supr.setTop.apply(this, arguments);
22198         this.sync();
22199     },
22200
22201     setLeftTop : function(left, top){
22202         this.storeLeftTop(left, top);
22203         supr.setLeftTop.apply(this, arguments);
22204         this.sync();
22205     },
22206
22207     setXY : function(xy, a, d, c, e){
22208         this.fixDisplay();
22209         this.beforeAction();
22210         this.storeXY(xy);
22211         var cb = this.createCB(c);
22212         supr.setXY.call(this, xy, a, d, cb, e);
22213         if(!a){
22214             cb();
22215         }
22216     },
22217
22218     // private
22219     createCB : function(c){
22220         var el = this;
22221         return function(){
22222             el.constrainXY();
22223             el.sync(true);
22224             if(c){
22225                 c();
22226             }
22227         };
22228     },
22229
22230     // overridden Element method
22231     setX : function(x, a, d, c, e){
22232         this.setXY([x, this.getY()], a, d, c, e);
22233     },
22234
22235     // overridden Element method
22236     setY : function(y, a, d, c, e){
22237         this.setXY([this.getX(), y], a, d, c, e);
22238     },
22239
22240     // overridden Element method
22241     setSize : function(w, h, a, d, c, e){
22242         this.beforeAction();
22243         var cb = this.createCB(c);
22244         supr.setSize.call(this, w, h, a, d, cb, e);
22245         if(!a){
22246             cb();
22247         }
22248     },
22249
22250     // overridden Element method
22251     setWidth : function(w, a, d, c, e){
22252         this.beforeAction();
22253         var cb = this.createCB(c);
22254         supr.setWidth.call(this, w, a, d, cb, e);
22255         if(!a){
22256             cb();
22257         }
22258     },
22259
22260     // overridden Element method
22261     setHeight : function(h, a, d, c, e){
22262         this.beforeAction();
22263         var cb = this.createCB(c);
22264         supr.setHeight.call(this, h, a, d, cb, e);
22265         if(!a){
22266             cb();
22267         }
22268     },
22269
22270     // overridden Element method
22271     setBounds : function(x, y, w, h, a, d, c, e){
22272         this.beforeAction();
22273         var cb = this.createCB(c);
22274         if(!a){
22275             this.storeXY([x, y]);
22276             supr.setXY.call(this, [x, y]);
22277             supr.setSize.call(this, w, h, a, d, cb, e);
22278             cb();
22279         }else{
22280             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22281         }
22282         return this;
22283     },
22284     
22285     /**
22286      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22287      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22288      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22289      * @param {Number} zindex The new z-index to set
22290      * @return {this} The Layer
22291      */
22292     setZIndex : function(zindex){
22293         this.zindex = zindex;
22294         this.setStyle("z-index", zindex + 2);
22295         if(this.shadow){
22296             this.shadow.setZIndex(zindex + 1);
22297         }
22298         if(this.shim){
22299             this.shim.setStyle("z-index", zindex);
22300         }
22301     }
22302 });
22303 })();/*
22304  * Based on:
22305  * Ext JS Library 1.1.1
22306  * Copyright(c) 2006-2007, Ext JS, LLC.
22307  *
22308  * Originally Released Under LGPL - original licence link has changed is not relivant.
22309  *
22310  * Fork - LGPL
22311  * <script type="text/javascript">
22312  */
22313
22314
22315 /**
22316  * @class Roo.Shadow
22317  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22318  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22319  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22320  * @constructor
22321  * Create a new Shadow
22322  * @param {Object} config The config object
22323  */
22324 Roo.Shadow = function(config){
22325     Roo.apply(this, config);
22326     if(typeof this.mode != "string"){
22327         this.mode = this.defaultMode;
22328     }
22329     var o = this.offset, a = {h: 0};
22330     var rad = Math.floor(this.offset/2);
22331     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22332         case "drop":
22333             a.w = 0;
22334             a.l = a.t = o;
22335             a.t -= 1;
22336             if(Roo.isIE){
22337                 a.l -= this.offset + rad;
22338                 a.t -= this.offset + rad;
22339                 a.w -= rad;
22340                 a.h -= rad;
22341                 a.t += 1;
22342             }
22343         break;
22344         case "sides":
22345             a.w = (o*2);
22346             a.l = -o;
22347             a.t = o-1;
22348             if(Roo.isIE){
22349                 a.l -= (this.offset - rad);
22350                 a.t -= this.offset + rad;
22351                 a.l += 1;
22352                 a.w -= (this.offset - rad)*2;
22353                 a.w -= rad + 1;
22354                 a.h -= 1;
22355             }
22356         break;
22357         case "frame":
22358             a.w = a.h = (o*2);
22359             a.l = a.t = -o;
22360             a.t += 1;
22361             a.h -= 2;
22362             if(Roo.isIE){
22363                 a.l -= (this.offset - rad);
22364                 a.t -= (this.offset - rad);
22365                 a.l += 1;
22366                 a.w -= (this.offset + rad + 1);
22367                 a.h -= (this.offset + rad);
22368                 a.h += 1;
22369             }
22370         break;
22371     };
22372
22373     this.adjusts = a;
22374 };
22375
22376 Roo.Shadow.prototype = {
22377     /**
22378      * @cfg {String} mode
22379      * The shadow display mode.  Supports the following options:<br />
22380      * sides: Shadow displays on both sides and bottom only<br />
22381      * frame: Shadow displays equally on all four sides<br />
22382      * drop: Traditional bottom-right drop shadow (default)
22383      */
22384     /**
22385      * @cfg {String} offset
22386      * The number of pixels to offset the shadow from the element (defaults to 4)
22387      */
22388     offset: 4,
22389
22390     // private
22391     defaultMode: "drop",
22392
22393     /**
22394      * Displays the shadow under the target element
22395      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22396      */
22397     show : function(target){
22398         target = Roo.get(target);
22399         if(!this.el){
22400             this.el = Roo.Shadow.Pool.pull();
22401             if(this.el.dom.nextSibling != target.dom){
22402                 this.el.insertBefore(target);
22403             }
22404         }
22405         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22406         if(Roo.isIE){
22407             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22408         }
22409         this.realign(
22410             target.getLeft(true),
22411             target.getTop(true),
22412             target.getWidth(),
22413             target.getHeight()
22414         );
22415         this.el.dom.style.display = "block";
22416     },
22417
22418     /**
22419      * Returns true if the shadow is visible, else false
22420      */
22421     isVisible : function(){
22422         return this.el ? true : false;  
22423     },
22424
22425     /**
22426      * Direct alignment when values are already available. Show must be called at least once before
22427      * calling this method to ensure it is initialized.
22428      * @param {Number} left The target element left position
22429      * @param {Number} top The target element top position
22430      * @param {Number} width The target element width
22431      * @param {Number} height The target element height
22432      */
22433     realign : function(l, t, w, h){
22434         if(!this.el){
22435             return;
22436         }
22437         var a = this.adjusts, d = this.el.dom, s = d.style;
22438         var iea = 0;
22439         s.left = (l+a.l)+"px";
22440         s.top = (t+a.t)+"px";
22441         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22442         if(s.width != sws || s.height != shs){
22443             s.width = sws;
22444             s.height = shs;
22445             if(!Roo.isIE){
22446                 var cn = d.childNodes;
22447                 var sww = Math.max(0, (sw-12))+"px";
22448                 cn[0].childNodes[1].style.width = sww;
22449                 cn[1].childNodes[1].style.width = sww;
22450                 cn[2].childNodes[1].style.width = sww;
22451                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22452             }
22453         }
22454     },
22455
22456     /**
22457      * Hides this shadow
22458      */
22459     hide : function(){
22460         if(this.el){
22461             this.el.dom.style.display = "none";
22462             Roo.Shadow.Pool.push(this.el);
22463             delete this.el;
22464         }
22465     },
22466
22467     /**
22468      * Adjust the z-index of this shadow
22469      * @param {Number} zindex The new z-index
22470      */
22471     setZIndex : function(z){
22472         this.zIndex = z;
22473         if(this.el){
22474             this.el.setStyle("z-index", z);
22475         }
22476     }
22477 };
22478
22479 // Private utility class that manages the internal Shadow cache
22480 Roo.Shadow.Pool = function(){
22481     var p = [];
22482     var markup = Roo.isIE ?
22483                  '<div class="x-ie-shadow"></div>' :
22484                  '<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>';
22485     return {
22486         pull : function(){
22487             var sh = p.shift();
22488             if(!sh){
22489                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22490                 sh.autoBoxAdjust = false;
22491             }
22492             return sh;
22493         },
22494
22495         push : function(sh){
22496             p.push(sh);
22497         }
22498     };
22499 }();/*
22500  * Based on:
22501  * Ext JS Library 1.1.1
22502  * Copyright(c) 2006-2007, Ext JS, LLC.
22503  *
22504  * Originally Released Under LGPL - original licence link has changed is not relivant.
22505  *
22506  * Fork - LGPL
22507  * <script type="text/javascript">
22508  */
22509
22510 /**
22511  * @class Roo.BoxComponent
22512  * @extends Roo.Component
22513  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22514  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22515  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22516  * layout containers.
22517  * @constructor
22518  * @param {Roo.Element/String/Object} config The configuration options.
22519  */
22520 Roo.BoxComponent = function(config){
22521     Roo.Component.call(this, config);
22522     this.addEvents({
22523         /**
22524          * @event resize
22525          * Fires after the component is resized.
22526              * @param {Roo.Component} this
22527              * @param {Number} adjWidth The box-adjusted width that was set
22528              * @param {Number} adjHeight The box-adjusted height that was set
22529              * @param {Number} rawWidth The width that was originally specified
22530              * @param {Number} rawHeight The height that was originally specified
22531              */
22532         resize : true,
22533         /**
22534          * @event move
22535          * Fires after the component is moved.
22536              * @param {Roo.Component} this
22537              * @param {Number} x The new x position
22538              * @param {Number} y The new y position
22539              */
22540         move : true
22541     });
22542 };
22543
22544 Roo.extend(Roo.BoxComponent, Roo.Component, {
22545     // private, set in afterRender to signify that the component has been rendered
22546     boxReady : false,
22547     // private, used to defer height settings to subclasses
22548     deferHeight: false,
22549     /** @cfg {Number} width
22550      * width (optional) size of component
22551      */
22552      /** @cfg {Number} height
22553      * height (optional) size of component
22554      */
22555      
22556     /**
22557      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22558      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22559      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22560      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22561      * @return {Roo.BoxComponent} this
22562      */
22563     setSize : function(w, h){
22564         // support for standard size objects
22565         if(typeof w == 'object'){
22566             h = w.height;
22567             w = w.width;
22568         }
22569         // not rendered
22570         if(!this.boxReady){
22571             this.width = w;
22572             this.height = h;
22573             return this;
22574         }
22575
22576         // prevent recalcs when not needed
22577         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22578             return this;
22579         }
22580         this.lastSize = {width: w, height: h};
22581
22582         var adj = this.adjustSize(w, h);
22583         var aw = adj.width, ah = adj.height;
22584         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22585             var rz = this.getResizeEl();
22586             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22587                 rz.setSize(aw, ah);
22588             }else if(!this.deferHeight && ah !== undefined){
22589                 rz.setHeight(ah);
22590             }else if(aw !== undefined){
22591                 rz.setWidth(aw);
22592             }
22593             this.onResize(aw, ah, w, h);
22594             this.fireEvent('resize', this, aw, ah, w, h);
22595         }
22596         return this;
22597     },
22598
22599     /**
22600      * Gets the current size of the component's underlying element.
22601      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22602      */
22603     getSize : function(){
22604         return this.el.getSize();
22605     },
22606
22607     /**
22608      * Gets the current XY position of the component's underlying element.
22609      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22610      * @return {Array} The XY position of the element (e.g., [100, 200])
22611      */
22612     getPosition : function(local){
22613         if(local === true){
22614             return [this.el.getLeft(true), this.el.getTop(true)];
22615         }
22616         return this.xy || this.el.getXY();
22617     },
22618
22619     /**
22620      * Gets the current box measurements of the component's underlying element.
22621      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22622      * @returns {Object} box An object in the format {x, y, width, height}
22623      */
22624     getBox : function(local){
22625         var s = this.el.getSize();
22626         if(local){
22627             s.x = this.el.getLeft(true);
22628             s.y = this.el.getTop(true);
22629         }else{
22630             var xy = this.xy || this.el.getXY();
22631             s.x = xy[0];
22632             s.y = xy[1];
22633         }
22634         return s;
22635     },
22636
22637     /**
22638      * Sets the current box measurements of the component's underlying element.
22639      * @param {Object} box An object in the format {x, y, width, height}
22640      * @returns {Roo.BoxComponent} this
22641      */
22642     updateBox : function(box){
22643         this.setSize(box.width, box.height);
22644         this.setPagePosition(box.x, box.y);
22645         return this;
22646     },
22647
22648     // protected
22649     getResizeEl : function(){
22650         return this.resizeEl || this.el;
22651     },
22652
22653     // protected
22654     getPositionEl : function(){
22655         return this.positionEl || this.el;
22656     },
22657
22658     /**
22659      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22660      * This method fires the move event.
22661      * @param {Number} left The new left
22662      * @param {Number} top The new top
22663      * @returns {Roo.BoxComponent} this
22664      */
22665     setPosition : function(x, y){
22666         this.x = x;
22667         this.y = y;
22668         if(!this.boxReady){
22669             return this;
22670         }
22671         var adj = this.adjustPosition(x, y);
22672         var ax = adj.x, ay = adj.y;
22673
22674         var el = this.getPositionEl();
22675         if(ax !== undefined || ay !== undefined){
22676             if(ax !== undefined && ay !== undefined){
22677                 el.setLeftTop(ax, ay);
22678             }else if(ax !== undefined){
22679                 el.setLeft(ax);
22680             }else if(ay !== undefined){
22681                 el.setTop(ay);
22682             }
22683             this.onPosition(ax, ay);
22684             this.fireEvent('move', this, ax, ay);
22685         }
22686         return this;
22687     },
22688
22689     /**
22690      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22691      * This method fires the move event.
22692      * @param {Number} x The new x position
22693      * @param {Number} y The new y position
22694      * @returns {Roo.BoxComponent} this
22695      */
22696     setPagePosition : function(x, y){
22697         this.pageX = x;
22698         this.pageY = y;
22699         if(!this.boxReady){
22700             return;
22701         }
22702         if(x === undefined || y === undefined){ // cannot translate undefined points
22703             return;
22704         }
22705         var p = this.el.translatePoints(x, y);
22706         this.setPosition(p.left, p.top);
22707         return this;
22708     },
22709
22710     // private
22711     onRender : function(ct, position){
22712         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22713         if(this.resizeEl){
22714             this.resizeEl = Roo.get(this.resizeEl);
22715         }
22716         if(this.positionEl){
22717             this.positionEl = Roo.get(this.positionEl);
22718         }
22719     },
22720
22721     // private
22722     afterRender : function(){
22723         Roo.BoxComponent.superclass.afterRender.call(this);
22724         this.boxReady = true;
22725         this.setSize(this.width, this.height);
22726         if(this.x || this.y){
22727             this.setPosition(this.x, this.y);
22728         }
22729         if(this.pageX || this.pageY){
22730             this.setPagePosition(this.pageX, this.pageY);
22731         }
22732     },
22733
22734     /**
22735      * Force the component's size to recalculate based on the underlying element's current height and width.
22736      * @returns {Roo.BoxComponent} this
22737      */
22738     syncSize : function(){
22739         delete this.lastSize;
22740         this.setSize(this.el.getWidth(), this.el.getHeight());
22741         return this;
22742     },
22743
22744     /**
22745      * Called after the component is resized, this method is empty by default but can be implemented by any
22746      * subclass that needs to perform custom logic after a resize occurs.
22747      * @param {Number} adjWidth The box-adjusted width that was set
22748      * @param {Number} adjHeight The box-adjusted height that was set
22749      * @param {Number} rawWidth The width that was originally specified
22750      * @param {Number} rawHeight The height that was originally specified
22751      */
22752     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22753
22754     },
22755
22756     /**
22757      * Called after the component is moved, this method is empty by default but can be implemented by any
22758      * subclass that needs to perform custom logic after a move occurs.
22759      * @param {Number} x The new x position
22760      * @param {Number} y The new y position
22761      */
22762     onPosition : function(x, y){
22763
22764     },
22765
22766     // private
22767     adjustSize : function(w, h){
22768         if(this.autoWidth){
22769             w = 'auto';
22770         }
22771         if(this.autoHeight){
22772             h = 'auto';
22773         }
22774         return {width : w, height: h};
22775     },
22776
22777     // private
22778     adjustPosition : function(x, y){
22779         return {x : x, y: y};
22780     }
22781 });/*
22782  * Based on:
22783  * Ext JS Library 1.1.1
22784  * Copyright(c) 2006-2007, Ext JS, LLC.
22785  *
22786  * Originally Released Under LGPL - original licence link has changed is not relivant.
22787  *
22788  * Fork - LGPL
22789  * <script type="text/javascript">
22790  */
22791
22792
22793 /**
22794  * @class Roo.SplitBar
22795  * @extends Roo.util.Observable
22796  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22797  * <br><br>
22798  * Usage:
22799  * <pre><code>
22800 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22801                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22802 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22803 split.minSize = 100;
22804 split.maxSize = 600;
22805 split.animate = true;
22806 split.on('moved', splitterMoved);
22807 </code></pre>
22808  * @constructor
22809  * Create a new SplitBar
22810  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22811  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22812  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22813  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22814                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22815                         position of the SplitBar).
22816  */
22817 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22818     
22819     /** @private */
22820     this.el = Roo.get(dragElement, true);
22821     this.el.dom.unselectable = "on";
22822     /** @private */
22823     this.resizingEl = Roo.get(resizingElement, true);
22824
22825     /**
22826      * @private
22827      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22828      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22829      * @type Number
22830      */
22831     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22832     
22833     /**
22834      * The minimum size of the resizing element. (Defaults to 0)
22835      * @type Number
22836      */
22837     this.minSize = 0;
22838     
22839     /**
22840      * The maximum size of the resizing element. (Defaults to 2000)
22841      * @type Number
22842      */
22843     this.maxSize = 2000;
22844     
22845     /**
22846      * Whether to animate the transition to the new size
22847      * @type Boolean
22848      */
22849     this.animate = false;
22850     
22851     /**
22852      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22853      * @type Boolean
22854      */
22855     this.useShim = false;
22856     
22857     /** @private */
22858     this.shim = null;
22859     
22860     if(!existingProxy){
22861         /** @private */
22862         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22863     }else{
22864         this.proxy = Roo.get(existingProxy).dom;
22865     }
22866     /** @private */
22867     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22868     
22869     /** @private */
22870     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22871     
22872     /** @private */
22873     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22874     
22875     /** @private */
22876     this.dragSpecs = {};
22877     
22878     /**
22879      * @private The adapter to use to positon and resize elements
22880      */
22881     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22882     this.adapter.init(this);
22883     
22884     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22885         /** @private */
22886         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22887         this.el.addClass("x-splitbar-h");
22888     }else{
22889         /** @private */
22890         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22891         this.el.addClass("x-splitbar-v");
22892     }
22893     
22894     this.addEvents({
22895         /**
22896          * @event resize
22897          * Fires when the splitter is moved (alias for {@link #event-moved})
22898          * @param {Roo.SplitBar} this
22899          * @param {Number} newSize the new width or height
22900          */
22901         "resize" : true,
22902         /**
22903          * @event moved
22904          * Fires when the splitter is moved
22905          * @param {Roo.SplitBar} this
22906          * @param {Number} newSize the new width or height
22907          */
22908         "moved" : true,
22909         /**
22910          * @event beforeresize
22911          * Fires before the splitter is dragged
22912          * @param {Roo.SplitBar} this
22913          */
22914         "beforeresize" : true,
22915
22916         "beforeapply" : true
22917     });
22918
22919     Roo.util.Observable.call(this);
22920 };
22921
22922 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22923     onStartProxyDrag : function(x, y){
22924         this.fireEvent("beforeresize", this);
22925         if(!this.overlay){
22926             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22927             o.unselectable();
22928             o.enableDisplayMode("block");
22929             // all splitbars share the same overlay
22930             Roo.SplitBar.prototype.overlay = o;
22931         }
22932         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22933         this.overlay.show();
22934         Roo.get(this.proxy).setDisplayed("block");
22935         var size = this.adapter.getElementSize(this);
22936         this.activeMinSize = this.getMinimumSize();;
22937         this.activeMaxSize = this.getMaximumSize();;
22938         var c1 = size - this.activeMinSize;
22939         var c2 = Math.max(this.activeMaxSize - size, 0);
22940         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22941             this.dd.resetConstraints();
22942             this.dd.setXConstraint(
22943                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22944                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22945             );
22946             this.dd.setYConstraint(0, 0);
22947         }else{
22948             this.dd.resetConstraints();
22949             this.dd.setXConstraint(0, 0);
22950             this.dd.setYConstraint(
22951                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22952                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22953             );
22954          }
22955         this.dragSpecs.startSize = size;
22956         this.dragSpecs.startPoint = [x, y];
22957         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22958     },
22959     
22960     /** 
22961      * @private Called after the drag operation by the DDProxy
22962      */
22963     onEndProxyDrag : function(e){
22964         Roo.get(this.proxy).setDisplayed(false);
22965         var endPoint = Roo.lib.Event.getXY(e);
22966         if(this.overlay){
22967             this.overlay.hide();
22968         }
22969         var newSize;
22970         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22971             newSize = this.dragSpecs.startSize + 
22972                 (this.placement == Roo.SplitBar.LEFT ?
22973                     endPoint[0] - this.dragSpecs.startPoint[0] :
22974                     this.dragSpecs.startPoint[0] - endPoint[0]
22975                 );
22976         }else{
22977             newSize = this.dragSpecs.startSize + 
22978                 (this.placement == Roo.SplitBar.TOP ?
22979                     endPoint[1] - this.dragSpecs.startPoint[1] :
22980                     this.dragSpecs.startPoint[1] - endPoint[1]
22981                 );
22982         }
22983         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22984         if(newSize != this.dragSpecs.startSize){
22985             if(this.fireEvent('beforeapply', this, newSize) !== false){
22986                 this.adapter.setElementSize(this, newSize);
22987                 this.fireEvent("moved", this, newSize);
22988                 this.fireEvent("resize", this, newSize);
22989             }
22990         }
22991     },
22992     
22993     /**
22994      * Get the adapter this SplitBar uses
22995      * @return The adapter object
22996      */
22997     getAdapter : function(){
22998         return this.adapter;
22999     },
23000     
23001     /**
23002      * Set the adapter this SplitBar uses
23003      * @param {Object} adapter A SplitBar adapter object
23004      */
23005     setAdapter : function(adapter){
23006         this.adapter = adapter;
23007         this.adapter.init(this);
23008     },
23009     
23010     /**
23011      * Gets the minimum size for the resizing element
23012      * @return {Number} The minimum size
23013      */
23014     getMinimumSize : function(){
23015         return this.minSize;
23016     },
23017     
23018     /**
23019      * Sets the minimum size for the resizing element
23020      * @param {Number} minSize The minimum size
23021      */
23022     setMinimumSize : function(minSize){
23023         this.minSize = minSize;
23024     },
23025     
23026     /**
23027      * Gets the maximum size for the resizing element
23028      * @return {Number} The maximum size
23029      */
23030     getMaximumSize : function(){
23031         return this.maxSize;
23032     },
23033     
23034     /**
23035      * Sets the maximum size for the resizing element
23036      * @param {Number} maxSize The maximum size
23037      */
23038     setMaximumSize : function(maxSize){
23039         this.maxSize = maxSize;
23040     },
23041     
23042     /**
23043      * Sets the initialize size for the resizing element
23044      * @param {Number} size The initial size
23045      */
23046     setCurrentSize : function(size){
23047         var oldAnimate = this.animate;
23048         this.animate = false;
23049         this.adapter.setElementSize(this, size);
23050         this.animate = oldAnimate;
23051     },
23052     
23053     /**
23054      * Destroy this splitbar. 
23055      * @param {Boolean} removeEl True to remove the element
23056      */
23057     destroy : function(removeEl){
23058         if(this.shim){
23059             this.shim.remove();
23060         }
23061         this.dd.unreg();
23062         this.proxy.parentNode.removeChild(this.proxy);
23063         if(removeEl){
23064             this.el.remove();
23065         }
23066     }
23067 });
23068
23069 /**
23070  * @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.
23071  */
23072 Roo.SplitBar.createProxy = function(dir){
23073     var proxy = new Roo.Element(document.createElement("div"));
23074     proxy.unselectable();
23075     var cls = 'x-splitbar-proxy';
23076     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23077     document.body.appendChild(proxy.dom);
23078     return proxy.dom;
23079 };
23080
23081 /** 
23082  * @class Roo.SplitBar.BasicLayoutAdapter
23083  * Default Adapter. It assumes the splitter and resizing element are not positioned
23084  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23085  */
23086 Roo.SplitBar.BasicLayoutAdapter = function(){
23087 };
23088
23089 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23090     // do nothing for now
23091     init : function(s){
23092     
23093     },
23094     /**
23095      * Called before drag operations to get the current size of the resizing element. 
23096      * @param {Roo.SplitBar} s The SplitBar using this adapter
23097      */
23098      getElementSize : function(s){
23099         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23100             return s.resizingEl.getWidth();
23101         }else{
23102             return s.resizingEl.getHeight();
23103         }
23104     },
23105     
23106     /**
23107      * Called after drag operations to set the size of the resizing element.
23108      * @param {Roo.SplitBar} s The SplitBar using this adapter
23109      * @param {Number} newSize The new size to set
23110      * @param {Function} onComplete A function to be invoked when resizing is complete
23111      */
23112     setElementSize : function(s, newSize, onComplete){
23113         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23114             if(!s.animate){
23115                 s.resizingEl.setWidth(newSize);
23116                 if(onComplete){
23117                     onComplete(s, newSize);
23118                 }
23119             }else{
23120                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23121             }
23122         }else{
23123             
23124             if(!s.animate){
23125                 s.resizingEl.setHeight(newSize);
23126                 if(onComplete){
23127                     onComplete(s, newSize);
23128                 }
23129             }else{
23130                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23131             }
23132         }
23133     }
23134 };
23135
23136 /** 
23137  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23138  * @extends Roo.SplitBar.BasicLayoutAdapter
23139  * Adapter that  moves the splitter element to align with the resized sizing element. 
23140  * Used with an absolute positioned SplitBar.
23141  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23142  * document.body, make sure you assign an id to the body element.
23143  */
23144 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23145     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23146     this.container = Roo.get(container);
23147 };
23148
23149 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23150     init : function(s){
23151         this.basic.init(s);
23152     },
23153     
23154     getElementSize : function(s){
23155         return this.basic.getElementSize(s);
23156     },
23157     
23158     setElementSize : function(s, newSize, onComplete){
23159         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23160     },
23161     
23162     moveSplitter : function(s){
23163         var yes = Roo.SplitBar;
23164         switch(s.placement){
23165             case yes.LEFT:
23166                 s.el.setX(s.resizingEl.getRight());
23167                 break;
23168             case yes.RIGHT:
23169                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23170                 break;
23171             case yes.TOP:
23172                 s.el.setY(s.resizingEl.getBottom());
23173                 break;
23174             case yes.BOTTOM:
23175                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23176                 break;
23177         }
23178     }
23179 };
23180
23181 /**
23182  * Orientation constant - Create a vertical SplitBar
23183  * @static
23184  * @type Number
23185  */
23186 Roo.SplitBar.VERTICAL = 1;
23187
23188 /**
23189  * Orientation constant - Create a horizontal SplitBar
23190  * @static
23191  * @type Number
23192  */
23193 Roo.SplitBar.HORIZONTAL = 2;
23194
23195 /**
23196  * Placement constant - The resizing element is to the left of the splitter element
23197  * @static
23198  * @type Number
23199  */
23200 Roo.SplitBar.LEFT = 1;
23201
23202 /**
23203  * Placement constant - The resizing element is to the right of the splitter element
23204  * @static
23205  * @type Number
23206  */
23207 Roo.SplitBar.RIGHT = 2;
23208
23209 /**
23210  * Placement constant - The resizing element is positioned above the splitter element
23211  * @static
23212  * @type Number
23213  */
23214 Roo.SplitBar.TOP = 3;
23215
23216 /**
23217  * Placement constant - The resizing element is positioned under splitter element
23218  * @static
23219  * @type Number
23220  */
23221 Roo.SplitBar.BOTTOM = 4;
23222 /*
23223  * Based on:
23224  * Ext JS Library 1.1.1
23225  * Copyright(c) 2006-2007, Ext JS, LLC.
23226  *
23227  * Originally Released Under LGPL - original licence link has changed is not relivant.
23228  *
23229  * Fork - LGPL
23230  * <script type="text/javascript">
23231  */
23232
23233 /**
23234  * @class Roo.View
23235  * @extends Roo.util.Observable
23236  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23237  * This class also supports single and multi selection modes. <br>
23238  * Create a data model bound view:
23239  <pre><code>
23240  var store = new Roo.data.Store(...);
23241
23242  var view = new Roo.View({
23243     el : "my-element",
23244     template : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23245  
23246     singleSelect: true,
23247     selectedClass: "ydataview-selected",
23248     store: store
23249  });
23250
23251  // listen for node click?
23252  view.on("click", function(vw, index, node, e){
23253  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23254  });
23255
23256  // load XML data
23257  dataModel.load("foobar.xml");
23258  </code></pre>
23259  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23260  * <br><br>
23261  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23262  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23263  * 
23264  * Note: old style constructor is still suported (container, template, config)
23265  * 
23266  * @constructor
23267  * Create a new View
23268  * @param {Object} config The config object
23269  * 
23270  */
23271 Roo.View = function(config, depreciated_tpl, depreciated_config){
23272     
23273     if (typeof(depreciated_tpl) == 'undefined') {
23274         // new way.. - universal constructor.
23275         Roo.apply(this, config);
23276         this.el  = Roo.get(this.el);
23277     } else {
23278         // old format..
23279         this.el  = Roo.get(config);
23280         this.tpl = depreciated_tpl;
23281         Roo.apply(this, depreciated_config);
23282     }
23283      
23284     
23285     if(typeof(this.tpl) == "string"){
23286         this.tpl = new Roo.Template(this.tpl);
23287     } 
23288     
23289     
23290     this.tpl.compile();
23291    
23292
23293      
23294     /** @private */
23295     this.addEvents({
23296     /**
23297      * @event beforeclick
23298      * Fires before a click is processed. Returns false to cancel the default action.
23299      * @param {Roo.View} this
23300      * @param {Number} index The index of the target node
23301      * @param {HTMLElement} node The target node
23302      * @param {Roo.EventObject} e The raw event object
23303      */
23304         "beforeclick" : true,
23305     /**
23306      * @event click
23307      * Fires when a template node is clicked.
23308      * @param {Roo.View} this
23309      * @param {Number} index The index of the target node
23310      * @param {HTMLElement} node The target node
23311      * @param {Roo.EventObject} e The raw event object
23312      */
23313         "click" : true,
23314     /**
23315      * @event dblclick
23316      * Fires when a template node is double clicked.
23317      * @param {Roo.View} this
23318      * @param {Number} index The index of the target node
23319      * @param {HTMLElement} node The target node
23320      * @param {Roo.EventObject} e The raw event object
23321      */
23322         "dblclick" : true,
23323     /**
23324      * @event contextmenu
23325      * Fires when a template node is right clicked.
23326      * @param {Roo.View} this
23327      * @param {Number} index The index of the target node
23328      * @param {HTMLElement} node The target node
23329      * @param {Roo.EventObject} e The raw event object
23330      */
23331         "contextmenu" : true,
23332     /**
23333      * @event selectionchange
23334      * Fires when the selected nodes change.
23335      * @param {Roo.View} this
23336      * @param {Array} selections Array of the selected nodes
23337      */
23338         "selectionchange" : true,
23339
23340     /**
23341      * @event beforeselect
23342      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23343      * @param {Roo.View} this
23344      * @param {HTMLElement} node The node to be selected
23345      * @param {Array} selections Array of currently selected nodes
23346      */
23347         "beforeselect" : true
23348     });
23349
23350     this.el.on({
23351         "click": this.onClick,
23352         "dblclick": this.onDblClick,
23353         "contextmenu": this.onContextMenu,
23354         scope:this
23355     });
23356
23357     this.selections = [];
23358     this.nodes = [];
23359     this.cmp = new Roo.CompositeElementLite([]);
23360     if(this.store){
23361         this.store = Roo.factory(this.store, Roo.data);
23362         this.setStore(this.store, true);
23363     }
23364     Roo.View.superclass.constructor.call(this);
23365 };
23366
23367 Roo.extend(Roo.View, Roo.util.Observable, {
23368     
23369      /**
23370      * @cfg {Roo.data.Store} store Data store to load data from.
23371      */
23372     store : false,
23373     
23374     /**
23375      * @cfg {String|Roo.Element} el The container element.
23376      */
23377     el : '',
23378     
23379     /**
23380      * @cfg {String|Roo.Template} tpl The template used by this View 
23381      */
23382     tpl : false,
23383     
23384     /**
23385      * @cfg {String} selectedClass The css class to add to selected nodes
23386      */
23387     selectedClass : "x-view-selected",
23388      /**
23389      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23390      */
23391     emptyText : "",
23392     /**
23393      * Returns the element this view is bound to.
23394      * @return {Roo.Element}
23395      */
23396     getEl : function(){
23397         return this.el;
23398     },
23399
23400     /**
23401      * Refreshes the view.
23402      */
23403     refresh : function(){
23404         var t = this.tpl;
23405         this.clearSelections();
23406         this.el.update("");
23407         var html = [];
23408         var records = this.store.getRange();
23409         if(records.length < 1){
23410             this.el.update(this.emptyText);
23411             return;
23412         }
23413         for(var i = 0, len = records.length; i < len; i++){
23414             var data = this.prepareData(records[i].data, i, records[i]);
23415             html[html.length] = t.apply(data);
23416         }
23417         this.el.update(html.join(""));
23418         this.nodes = this.el.dom.childNodes;
23419         this.updateIndexes(0);
23420     },
23421
23422     /**
23423      * Function to override to reformat the data that is sent to
23424      * the template for each node.
23425      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23426      * a JSON object for an UpdateManager bound view).
23427      */
23428     prepareData : function(data){
23429         return data;
23430     },
23431
23432     onUpdate : function(ds, record){
23433         this.clearSelections();
23434         var index = this.store.indexOf(record);
23435         var n = this.nodes[index];
23436         this.tpl.insertBefore(n, this.prepareData(record.data));
23437         n.parentNode.removeChild(n);
23438         this.updateIndexes(index, index);
23439     },
23440
23441     onAdd : function(ds, records, index){
23442         this.clearSelections();
23443         if(this.nodes.length == 0){
23444             this.refresh();
23445             return;
23446         }
23447         var n = this.nodes[index];
23448         for(var i = 0, len = records.length; i < len; i++){
23449             var d = this.prepareData(records[i].data);
23450             if(n){
23451                 this.tpl.insertBefore(n, d);
23452             }else{
23453                 this.tpl.append(this.el, d);
23454             }
23455         }
23456         this.updateIndexes(index);
23457     },
23458
23459     onRemove : function(ds, record, index){
23460         this.clearSelections();
23461         this.el.dom.removeChild(this.nodes[index]);
23462         this.updateIndexes(index);
23463     },
23464
23465     /**
23466      * Refresh an individual node.
23467      * @param {Number} index
23468      */
23469     refreshNode : function(index){
23470         this.onUpdate(this.store, this.store.getAt(index));
23471     },
23472
23473     updateIndexes : function(startIndex, endIndex){
23474         var ns = this.nodes;
23475         startIndex = startIndex || 0;
23476         endIndex = endIndex || ns.length - 1;
23477         for(var i = startIndex; i <= endIndex; i++){
23478             ns[i].nodeIndex = i;
23479         }
23480     },
23481
23482     /**
23483      * Changes the data store this view uses and refresh the view.
23484      * @param {Store} store
23485      */
23486     setStore : function(store, initial){
23487         if(!initial && this.store){
23488             this.store.un("datachanged", this.refresh);
23489             this.store.un("add", this.onAdd);
23490             this.store.un("remove", this.onRemove);
23491             this.store.un("update", this.onUpdate);
23492             this.store.un("clear", this.refresh);
23493         }
23494         if(store){
23495           
23496             store.on("datachanged", this.refresh, this);
23497             store.on("add", this.onAdd, this);
23498             store.on("remove", this.onRemove, this);
23499             store.on("update", this.onUpdate, this);
23500             store.on("clear", this.refresh, this);
23501         }
23502         
23503         if(store){
23504             this.refresh();
23505         }
23506     },
23507
23508     /**
23509      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23510      * @param {HTMLElement} node
23511      * @return {HTMLElement} The template node
23512      */
23513     findItemFromChild : function(node){
23514         var el = this.el.dom;
23515         if(!node || node.parentNode == el){
23516                     return node;
23517             }
23518             var p = node.parentNode;
23519             while(p && p != el){
23520             if(p.parentNode == el){
23521                 return p;
23522             }
23523             p = p.parentNode;
23524         }
23525             return null;
23526     },
23527
23528     /** @ignore */
23529     onClick : function(e){
23530         var item = this.findItemFromChild(e.getTarget());
23531         if(item){
23532             var index = this.indexOf(item);
23533             if(this.onItemClick(item, index, e) !== false){
23534                 this.fireEvent("click", this, index, item, e);
23535             }
23536         }else{
23537             this.clearSelections();
23538         }
23539     },
23540
23541     /** @ignore */
23542     onContextMenu : function(e){
23543         var item = this.findItemFromChild(e.getTarget());
23544         if(item){
23545             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23546         }
23547     },
23548
23549     /** @ignore */
23550     onDblClick : function(e){
23551         var item = this.findItemFromChild(e.getTarget());
23552         if(item){
23553             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23554         }
23555     },
23556
23557     onItemClick : function(item, index, e){
23558         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23559             return false;
23560         }
23561         if(this.multiSelect || this.singleSelect){
23562             if(this.multiSelect && e.shiftKey && this.lastSelection){
23563                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23564             }else{
23565                 this.select(item, this.multiSelect && e.ctrlKey);
23566                 this.lastSelection = item;
23567             }
23568             e.preventDefault();
23569         }
23570         return true;
23571     },
23572
23573     /**
23574      * Get the number of selected nodes.
23575      * @return {Number}
23576      */
23577     getSelectionCount : function(){
23578         return this.selections.length;
23579     },
23580
23581     /**
23582      * Get the currently selected nodes.
23583      * @return {Array} An array of HTMLElements
23584      */
23585     getSelectedNodes : function(){
23586         return this.selections;
23587     },
23588
23589     /**
23590      * Get the indexes of the selected nodes.
23591      * @return {Array}
23592      */
23593     getSelectedIndexes : function(){
23594         var indexes = [], s = this.selections;
23595         for(var i = 0, len = s.length; i < len; i++){
23596             indexes.push(s[i].nodeIndex);
23597         }
23598         return indexes;
23599     },
23600
23601     /**
23602      * Clear all selections
23603      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23604      */
23605     clearSelections : function(suppressEvent){
23606         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23607             this.cmp.elements = this.selections;
23608             this.cmp.removeClass(this.selectedClass);
23609             this.selections = [];
23610             if(!suppressEvent){
23611                 this.fireEvent("selectionchange", this, this.selections);
23612             }
23613         }
23614     },
23615
23616     /**
23617      * Returns true if the passed node is selected
23618      * @param {HTMLElement/Number} node The node or node index
23619      * @return {Boolean}
23620      */
23621     isSelected : function(node){
23622         var s = this.selections;
23623         if(s.length < 1){
23624             return false;
23625         }
23626         node = this.getNode(node);
23627         return s.indexOf(node) !== -1;
23628     },
23629
23630     /**
23631      * Selects nodes.
23632      * @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
23633      * @param {Boolean} keepExisting (optional) true to keep existing selections
23634      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23635      */
23636     select : function(nodeInfo, keepExisting, suppressEvent){
23637         if(nodeInfo instanceof Array){
23638             if(!keepExisting){
23639                 this.clearSelections(true);
23640             }
23641             for(var i = 0, len = nodeInfo.length; i < len; i++){
23642                 this.select(nodeInfo[i], true, true);
23643             }
23644         } else{
23645             var node = this.getNode(nodeInfo);
23646             if(node && !this.isSelected(node)){
23647                 if(!keepExisting){
23648                     this.clearSelections(true);
23649                 }
23650                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23651                     Roo.fly(node).addClass(this.selectedClass);
23652                     this.selections.push(node);
23653                     if(!suppressEvent){
23654                         this.fireEvent("selectionchange", this, this.selections);
23655                     }
23656                 }
23657             }
23658         }
23659     },
23660
23661     /**
23662      * Gets a template node.
23663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23664      * @return {HTMLElement} The node or null if it wasn't found
23665      */
23666     getNode : function(nodeInfo){
23667         if(typeof nodeInfo == "string"){
23668             return document.getElementById(nodeInfo);
23669         }else if(typeof nodeInfo == "number"){
23670             return this.nodes[nodeInfo];
23671         }
23672         return nodeInfo;
23673     },
23674
23675     /**
23676      * Gets a range template nodes.
23677      * @param {Number} startIndex
23678      * @param {Number} endIndex
23679      * @return {Array} An array of nodes
23680      */
23681     getNodes : function(start, end){
23682         var ns = this.nodes;
23683         start = start || 0;
23684         end = typeof end == "undefined" ? ns.length - 1 : end;
23685         var nodes = [];
23686         if(start <= end){
23687             for(var i = start; i <= end; i++){
23688                 nodes.push(ns[i]);
23689             }
23690         } else{
23691             for(var i = start; i >= end; i--){
23692                 nodes.push(ns[i]);
23693             }
23694         }
23695         return nodes;
23696     },
23697
23698     /**
23699      * Finds the index of the passed node
23700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23701      * @return {Number} The index of the node or -1
23702      */
23703     indexOf : function(node){
23704         node = this.getNode(node);
23705         if(typeof node.nodeIndex == "number"){
23706             return node.nodeIndex;
23707         }
23708         var ns = this.nodes;
23709         for(var i = 0, len = ns.length; i < len; i++){
23710             if(ns[i] == node){
23711                 return i;
23712             }
23713         }
23714         return -1;
23715     }
23716 });
23717 /*
23718  * Based on:
23719  * Ext JS Library 1.1.1
23720  * Copyright(c) 2006-2007, Ext JS, LLC.
23721  *
23722  * Originally Released Under LGPL - original licence link has changed is not relivant.
23723  *
23724  * Fork - LGPL
23725  * <script type="text/javascript">
23726  */
23727
23728 /**
23729  * @class Roo.JsonView
23730  * @extends Roo.View
23731  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23732 <pre><code>
23733 var view = new Roo.JsonView({
23734     container: "my-element",
23735     template: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23736     multiSelect: true, 
23737     jsonRoot: "data" 
23738 });
23739
23740 // listen for node click?
23741 view.on("click", function(vw, index, node, e){
23742     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23743 });
23744
23745 // direct load of JSON data
23746 view.load("foobar.php");
23747
23748 // Example from my blog list
23749 var tpl = new Roo.Template(
23750     '&lt;div class="entry"&gt;' +
23751     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23752     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23753     "&lt;/div&gt;&lt;hr /&gt;"
23754 );
23755
23756 var moreView = new Roo.JsonView({
23757     container :  "entry-list", 
23758     template : tpl,
23759     jsonRoot: "posts"
23760 });
23761 moreView.on("beforerender", this.sortEntries, this);
23762 moreView.load({
23763     url: "/blog/get-posts.php",
23764     params: "allposts=true",
23765     text: "Loading Blog Entries..."
23766 });
23767 </code></pre>
23768
23769 * Note: old code is supported with arguments : (container, template, config)
23770
23771
23772  * @constructor
23773  * Create a new JsonView
23774  * 
23775  * @param {Object} config The config object
23776  * 
23777  */
23778 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23779     
23780     
23781     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23782
23783     var um = this.el.getUpdateManager();
23784     um.setRenderer(this);
23785     um.on("update", this.onLoad, this);
23786     um.on("failure", this.onLoadException, this);
23787
23788     /**
23789      * @event beforerender
23790      * Fires before rendering of the downloaded JSON data.
23791      * @param {Roo.JsonView} this
23792      * @param {Object} data The JSON data loaded
23793      */
23794     /**
23795      * @event load
23796      * Fires when data is loaded.
23797      * @param {Roo.JsonView} this
23798      * @param {Object} data The JSON data loaded
23799      * @param {Object} response The raw Connect response object
23800      */
23801     /**
23802      * @event loadexception
23803      * Fires when loading fails.
23804      * @param {Roo.JsonView} this
23805      * @param {Object} response The raw Connect response object
23806      */
23807     this.addEvents({
23808         'beforerender' : true,
23809         'load' : true,
23810         'loadexception' : true
23811     });
23812 };
23813 Roo.extend(Roo.JsonView, Roo.View, {
23814     /**
23815      * @type {String} The root property in the loaded JSON object that contains the data
23816      */
23817     jsonRoot : "",
23818
23819     /**
23820      * Refreshes the view.
23821      */
23822     refresh : function(){
23823         this.clearSelections();
23824         this.el.update("");
23825         var html = [];
23826         var o = this.jsonData;
23827         if(o && o.length > 0){
23828             for(var i = 0, len = o.length; i < len; i++){
23829                 var data = this.prepareData(o[i], i, o);
23830                 html[html.length] = this.tpl.apply(data);
23831             }
23832         }else{
23833             html.push(this.emptyText);
23834         }
23835         this.el.update(html.join(""));
23836         this.nodes = this.el.dom.childNodes;
23837         this.updateIndexes(0);
23838     },
23839
23840     /**
23841      * 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.
23842      * @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:
23843      <pre><code>
23844      view.load({
23845          url: "your-url.php",
23846          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23847          callback: yourFunction,
23848          scope: yourObject, //(optional scope)
23849          discardUrl: false,
23850          nocache: false,
23851          text: "Loading...",
23852          timeout: 30,
23853          scripts: false
23854      });
23855      </code></pre>
23856      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23857      * 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.
23858      * @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}
23859      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23860      * @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.
23861      */
23862     load : function(){
23863         var um = this.el.getUpdateManager();
23864         um.update.apply(um, arguments);
23865     },
23866
23867     render : function(el, response){
23868         this.clearSelections();
23869         this.el.update("");
23870         var o;
23871         try{
23872             o = Roo.util.JSON.decode(response.responseText);
23873             if(this.jsonRoot){
23874                 
23875                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23876             }
23877         } catch(e){
23878         }
23879         /**
23880          * The current JSON data or null
23881          */
23882         this.jsonData = o;
23883         this.beforeRender();
23884         this.refresh();
23885     },
23886
23887 /**
23888  * Get the number of records in the current JSON dataset
23889  * @return {Number}
23890  */
23891     getCount : function(){
23892         return this.jsonData ? this.jsonData.length : 0;
23893     },
23894
23895 /**
23896  * Returns the JSON object for the specified node(s)
23897  * @param {HTMLElement/Array} node The node or an array of nodes
23898  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23899  * you get the JSON object for the node
23900  */
23901     getNodeData : function(node){
23902         if(node instanceof Array){
23903             var data = [];
23904             for(var i = 0, len = node.length; i < len; i++){
23905                 data.push(this.getNodeData(node[i]));
23906             }
23907             return data;
23908         }
23909         return this.jsonData[this.indexOf(node)] || null;
23910     },
23911
23912     beforeRender : function(){
23913         this.snapshot = this.jsonData;
23914         if(this.sortInfo){
23915             this.sort.apply(this, this.sortInfo);
23916         }
23917         this.fireEvent("beforerender", this, this.jsonData);
23918     },
23919
23920     onLoad : function(el, o){
23921         this.fireEvent("load", this, this.jsonData, o);
23922     },
23923
23924     onLoadException : function(el, o){
23925         this.fireEvent("loadexception", this, o);
23926     },
23927
23928 /**
23929  * Filter the data by a specific property.
23930  * @param {String} property A property on your JSON objects
23931  * @param {String/RegExp} value Either string that the property values
23932  * should start with, or a RegExp to test against the property
23933  */
23934     filter : function(property, value){
23935         if(this.jsonData){
23936             var data = [];
23937             var ss = this.snapshot;
23938             if(typeof value == "string"){
23939                 var vlen = value.length;
23940                 if(vlen == 0){
23941                     this.clearFilter();
23942                     return;
23943                 }
23944                 value = value.toLowerCase();
23945                 for(var i = 0, len = ss.length; i < len; i++){
23946                     var o = ss[i];
23947                     if(o[property].substr(0, vlen).toLowerCase() == value){
23948                         data.push(o);
23949                     }
23950                 }
23951             } else if(value.exec){ // regex?
23952                 for(var i = 0, len = ss.length; i < len; i++){
23953                     var o = ss[i];
23954                     if(value.test(o[property])){
23955                         data.push(o);
23956                     }
23957                 }
23958             } else{
23959                 return;
23960             }
23961             this.jsonData = data;
23962             this.refresh();
23963         }
23964     },
23965
23966 /**
23967  * Filter by a function. The passed function will be called with each
23968  * object in the current dataset. If the function returns true the value is kept,
23969  * otherwise it is filtered.
23970  * @param {Function} fn
23971  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23972  */
23973     filterBy : function(fn, scope){
23974         if(this.jsonData){
23975             var data = [];
23976             var ss = this.snapshot;
23977             for(var i = 0, len = ss.length; i < len; i++){
23978                 var o = ss[i];
23979                 if(fn.call(scope || this, o)){
23980                     data.push(o);
23981                 }
23982             }
23983             this.jsonData = data;
23984             this.refresh();
23985         }
23986     },
23987
23988 /**
23989  * Clears the current filter.
23990  */
23991     clearFilter : function(){
23992         if(this.snapshot && this.jsonData != this.snapshot){
23993             this.jsonData = this.snapshot;
23994             this.refresh();
23995         }
23996     },
23997
23998
23999 /**
24000  * Sorts the data for this view and refreshes it.
24001  * @param {String} property A property on your JSON objects to sort on
24002  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24003  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24004  */
24005     sort : function(property, dir, sortType){
24006         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24007         if(this.jsonData){
24008             var p = property;
24009             var dsc = dir && dir.toLowerCase() == "desc";
24010             var f = function(o1, o2){
24011                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24012                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24013                 ;
24014                 if(v1 < v2){
24015                     return dsc ? +1 : -1;
24016                 } else if(v1 > v2){
24017                     return dsc ? -1 : +1;
24018                 } else{
24019                     return 0;
24020                 }
24021             };
24022             this.jsonData.sort(f);
24023             this.refresh();
24024             if(this.jsonData != this.snapshot){
24025                 this.snapshot.sort(f);
24026             }
24027         }
24028     }
24029 });/*
24030  * Based on:
24031  * Ext JS Library 1.1.1
24032  * Copyright(c) 2006-2007, Ext JS, LLC.
24033  *
24034  * Originally Released Under LGPL - original licence link has changed is not relivant.
24035  *
24036  * Fork - LGPL
24037  * <script type="text/javascript">
24038  */
24039  
24040
24041 /**
24042  * @class Roo.ColorPalette
24043  * @extends Roo.Component
24044  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24045  * Here's an example of typical usage:
24046  * <pre><code>
24047 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24048 cp.render('my-div');
24049
24050 cp.on('select', function(palette, selColor){
24051     // do something with selColor
24052 });
24053 </code></pre>
24054  * @constructor
24055  * Create a new ColorPalette
24056  * @param {Object} config The config object
24057  */
24058 Roo.ColorPalette = function(config){
24059     Roo.ColorPalette.superclass.constructor.call(this, config);
24060     this.addEvents({
24061         /**
24062              * @event select
24063              * Fires when a color is selected
24064              * @param {ColorPalette} this
24065              * @param {String} color The 6-digit color hex code (without the # symbol)
24066              */
24067         select: true
24068     });
24069
24070     if(this.handler){
24071         this.on("select", this.handler, this.scope, true);
24072     }
24073 };
24074 Roo.extend(Roo.ColorPalette, Roo.Component, {
24075     /**
24076      * @cfg {String} itemCls
24077      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24078      */
24079     itemCls : "x-color-palette",
24080     /**
24081      * @cfg {String} value
24082      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24083      * the hex codes are case-sensitive.
24084      */
24085     value : null,
24086     clickEvent:'click',
24087     // private
24088     ctype: "Roo.ColorPalette",
24089
24090     /**
24091      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24092      */
24093     allowReselect : false,
24094
24095     /**
24096      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24097      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24098      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24099      * of colors with the width setting until the box is symmetrical.</p>
24100      * <p>You can override individual colors if needed:</p>
24101      * <pre><code>
24102 var cp = new Roo.ColorPalette();
24103 cp.colors[0] = "FF0000";  // change the first box to red
24104 </code></pre>
24105
24106 Or you can provide a custom array of your own for complete control:
24107 <pre><code>
24108 var cp = new Roo.ColorPalette();
24109 cp.colors = ["000000", "993300", "333300"];
24110 </code></pre>
24111      * @type Array
24112      */
24113     colors : [
24114         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24115         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24116         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24117         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24118         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24119     ],
24120
24121     // private
24122     onRender : function(container, position){
24123         var t = new Roo.MasterTemplate(
24124             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24125         );
24126         var c = this.colors;
24127         for(var i = 0, len = c.length; i < len; i++){
24128             t.add([c[i]]);
24129         }
24130         var el = document.createElement("div");
24131         el.className = this.itemCls;
24132         t.overwrite(el);
24133         container.dom.insertBefore(el, position);
24134         this.el = Roo.get(el);
24135         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24136         if(this.clickEvent != 'click'){
24137             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24138         }
24139     },
24140
24141     // private
24142     afterRender : function(){
24143         Roo.ColorPalette.superclass.afterRender.call(this);
24144         if(this.value){
24145             var s = this.value;
24146             this.value = null;
24147             this.select(s);
24148         }
24149     },
24150
24151     // private
24152     handleClick : function(e, t){
24153         e.preventDefault();
24154         if(!this.disabled){
24155             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24156             this.select(c.toUpperCase());
24157         }
24158     },
24159
24160     /**
24161      * Selects the specified color in the palette (fires the select event)
24162      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24163      */
24164     select : function(color){
24165         color = color.replace("#", "");
24166         if(color != this.value || this.allowReselect){
24167             var el = this.el;
24168             if(this.value){
24169                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24170             }
24171             el.child("a.color-"+color).addClass("x-color-palette-sel");
24172             this.value = color;
24173             this.fireEvent("select", this, color);
24174         }
24175     }
24176 });/*
24177  * Based on:
24178  * Ext JS Library 1.1.1
24179  * Copyright(c) 2006-2007, Ext JS, LLC.
24180  *
24181  * Originally Released Under LGPL - original licence link has changed is not relivant.
24182  *
24183  * Fork - LGPL
24184  * <script type="text/javascript">
24185  */
24186  
24187 /**
24188  * @class Roo.DatePicker
24189  * @extends Roo.Component
24190  * Simple date picker class.
24191  * @constructor
24192  * Create a new DatePicker
24193  * @param {Object} config The config object
24194  */
24195 Roo.DatePicker = function(config){
24196     Roo.DatePicker.superclass.constructor.call(this, config);
24197
24198     this.value = config && config.value ?
24199                  config.value.clearTime() : new Date().clearTime();
24200
24201     this.addEvents({
24202         /**
24203              * @event select
24204              * Fires when a date is selected
24205              * @param {DatePicker} this
24206              * @param {Date} date The selected date
24207              */
24208         select: true
24209     });
24210
24211     if(this.handler){
24212         this.on("select", this.handler,  this.scope || this);
24213     }
24214     // build the disabledDatesRE
24215     if(!this.disabledDatesRE && this.disabledDates){
24216         var dd = this.disabledDates;
24217         var re = "(?:";
24218         for(var i = 0; i < dd.length; i++){
24219             re += dd[i];
24220             if(i != dd.length-1) re += "|";
24221         }
24222         this.disabledDatesRE = new RegExp(re + ")");
24223     }
24224 };
24225
24226 Roo.extend(Roo.DatePicker, Roo.Component, {
24227     /**
24228      * @cfg {String} todayText
24229      * The text to display on the button that selects the current date (defaults to "Today")
24230      */
24231     todayText : "Today",
24232     /**
24233      * @cfg {String} okText
24234      * The text to display on the ok button
24235      */
24236     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24237     /**
24238      * @cfg {String} cancelText
24239      * The text to display on the cancel button
24240      */
24241     cancelText : "Cancel",
24242     /**
24243      * @cfg {String} todayTip
24244      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24245      */
24246     todayTip : "{0} (Spacebar)",
24247     /**
24248      * @cfg {Date} minDate
24249      * Minimum allowable date (JavaScript date object, defaults to null)
24250      */
24251     minDate : null,
24252     /**
24253      * @cfg {Date} maxDate
24254      * Maximum allowable date (JavaScript date object, defaults to null)
24255      */
24256     maxDate : null,
24257     /**
24258      * @cfg {String} minText
24259      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24260      */
24261     minText : "This date is before the minimum date",
24262     /**
24263      * @cfg {String} maxText
24264      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24265      */
24266     maxText : "This date is after the maximum date",
24267     /**
24268      * @cfg {String} format
24269      * The default date format string which can be overriden for localization support.  The format must be
24270      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24271      */
24272     format : "m/d/y",
24273     /**
24274      * @cfg {Array} disabledDays
24275      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24276      */
24277     disabledDays : null,
24278     /**
24279      * @cfg {String} disabledDaysText
24280      * The tooltip to display when the date falls on a disabled day (defaults to "")
24281      */
24282     disabledDaysText : "",
24283     /**
24284      * @cfg {RegExp} disabledDatesRE
24285      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24286      */
24287     disabledDatesRE : null,
24288     /**
24289      * @cfg {String} disabledDatesText
24290      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24291      */
24292     disabledDatesText : "",
24293     /**
24294      * @cfg {Boolean} constrainToViewport
24295      * True to constrain the date picker to the viewport (defaults to true)
24296      */
24297     constrainToViewport : true,
24298     /**
24299      * @cfg {Array} monthNames
24300      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24301      */
24302     monthNames : Date.monthNames,
24303     /**
24304      * @cfg {Array} dayNames
24305      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24306      */
24307     dayNames : Date.dayNames,
24308     /**
24309      * @cfg {String} nextText
24310      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24311      */
24312     nextText: 'Next Month (Control+Right)',
24313     /**
24314      * @cfg {String} prevText
24315      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24316      */
24317     prevText: 'Previous Month (Control+Left)',
24318     /**
24319      * @cfg {String} monthYearText
24320      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24321      */
24322     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24323     /**
24324      * @cfg {Number} startDay
24325      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24326      */
24327     startDay : 0,
24328     /**
24329      * @cfg {Bool} showClear
24330      * Show a clear button (usefull for date form elements that can be blank.)
24331      */
24332     
24333     showClear: false,
24334     
24335     /**
24336      * Sets the value of the date field
24337      * @param {Date} value The date to set
24338      */
24339     setValue : function(value){
24340         var old = this.value;
24341         this.value = value.clearTime(true);
24342         if(this.el){
24343             this.update(this.value);
24344         }
24345     },
24346
24347     /**
24348      * Gets the current selected value of the date field
24349      * @return {Date} The selected date
24350      */
24351     getValue : function(){
24352         return this.value;
24353     },
24354
24355     // private
24356     focus : function(){
24357         if(this.el){
24358             this.update(this.activeDate);
24359         }
24360     },
24361
24362     // private
24363     onRender : function(container, position){
24364         var m = [
24365              '<table cellspacing="0">',
24366                 '<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>',
24367                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24368         var dn = this.dayNames;
24369         for(var i = 0; i < 7; i++){
24370             var d = this.startDay+i;
24371             if(d > 6){
24372                 d = d-7;
24373             }
24374             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24375         }
24376         m[m.length] = "</tr></thead><tbody><tr>";
24377         for(var i = 0; i < 42; i++) {
24378             if(i % 7 == 0 && i != 0){
24379                 m[m.length] = "</tr><tr>";
24380             }
24381             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24382         }
24383         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24384             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24385
24386         var el = document.createElement("div");
24387         el.className = "x-date-picker";
24388         el.innerHTML = m.join("");
24389
24390         container.dom.insertBefore(el, position);
24391
24392         this.el = Roo.get(el);
24393         this.eventEl = Roo.get(el.firstChild);
24394
24395         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24396             handler: this.showPrevMonth,
24397             scope: this,
24398             preventDefault:true,
24399             stopDefault:true
24400         });
24401
24402         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24403             handler: this.showNextMonth,
24404             scope: this,
24405             preventDefault:true,
24406             stopDefault:true
24407         });
24408
24409         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24410
24411         this.monthPicker = this.el.down('div.x-date-mp');
24412         this.monthPicker.enableDisplayMode('block');
24413         
24414         var kn = new Roo.KeyNav(this.eventEl, {
24415             "left" : function(e){
24416                 e.ctrlKey ?
24417                     this.showPrevMonth() :
24418                     this.update(this.activeDate.add("d", -1));
24419             },
24420
24421             "right" : function(e){
24422                 e.ctrlKey ?
24423                     this.showNextMonth() :
24424                     this.update(this.activeDate.add("d", 1));
24425             },
24426
24427             "up" : function(e){
24428                 e.ctrlKey ?
24429                     this.showNextYear() :
24430                     this.update(this.activeDate.add("d", -7));
24431             },
24432
24433             "down" : function(e){
24434                 e.ctrlKey ?
24435                     this.showPrevYear() :
24436                     this.update(this.activeDate.add("d", 7));
24437             },
24438
24439             "pageUp" : function(e){
24440                 this.showNextMonth();
24441             },
24442
24443             "pageDown" : function(e){
24444                 this.showPrevMonth();
24445             },
24446
24447             "enter" : function(e){
24448                 e.stopPropagation();
24449                 return true;
24450             },
24451
24452             scope : this
24453         });
24454
24455         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24456
24457         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24458
24459         this.el.unselectable();
24460         
24461         this.cells = this.el.select("table.x-date-inner tbody td");
24462         this.textNodes = this.el.query("table.x-date-inner tbody span");
24463
24464         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24465             text: "&#160;",
24466             tooltip: this.monthYearText
24467         });
24468
24469         this.mbtn.on('click', this.showMonthPicker, this);
24470         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24471
24472
24473         var today = (new Date()).dateFormat(this.format);
24474         
24475         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24476         baseTb.add({
24477             text: String.format(this.todayText, today),
24478             tooltip: String.format(this.todayTip, today),
24479             handler: this.selectToday,
24480             scope: this
24481         });
24482         
24483         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24484             
24485         //});
24486         if (this.showClear) {
24487             
24488             baseTb.add( new Roo.Toolbar.Fill());
24489             baseTb.add({
24490                 text: '&#160;',
24491                 cls: 'x-btn-icon x-btn-clear',
24492                 handler: function() {
24493                     //this.value = '';
24494                     this.fireEvent("select", this, '');
24495                 },
24496                 scope: this
24497             });
24498         }
24499         
24500         
24501         if(Roo.isIE){
24502             this.el.repaint();
24503         }
24504         this.update(this.value);
24505     },
24506
24507     createMonthPicker : function(){
24508         if(!this.monthPicker.dom.firstChild){
24509             var buf = ['<table border="0" cellspacing="0">'];
24510             for(var i = 0; i < 6; i++){
24511                 buf.push(
24512                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24513                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24514                     i == 0 ?
24515                     '<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>' :
24516                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24517                 );
24518             }
24519             buf.push(
24520                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24521                     this.okText,
24522                     '</button><button type="button" class="x-date-mp-cancel">',
24523                     this.cancelText,
24524                     '</button></td></tr>',
24525                 '</table>'
24526             );
24527             this.monthPicker.update(buf.join(''));
24528             this.monthPicker.on('click', this.onMonthClick, this);
24529             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24530
24531             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24532             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24533
24534             this.mpMonths.each(function(m, a, i){
24535                 i += 1;
24536                 if((i%2) == 0){
24537                     m.dom.xmonth = 5 + Math.round(i * .5);
24538                 }else{
24539                     m.dom.xmonth = Math.round((i-1) * .5);
24540                 }
24541             });
24542         }
24543     },
24544
24545     showMonthPicker : function(){
24546         this.createMonthPicker();
24547         var size = this.el.getSize();
24548         this.monthPicker.setSize(size);
24549         this.monthPicker.child('table').setSize(size);
24550
24551         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24552         this.updateMPMonth(this.mpSelMonth);
24553         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24554         this.updateMPYear(this.mpSelYear);
24555
24556         this.monthPicker.slideIn('t', {duration:.2});
24557     },
24558
24559     updateMPYear : function(y){
24560         this.mpyear = y;
24561         var ys = this.mpYears.elements;
24562         for(var i = 1; i <= 10; i++){
24563             var td = ys[i-1], y2;
24564             if((i%2) == 0){
24565                 y2 = y + Math.round(i * .5);
24566                 td.firstChild.innerHTML = y2;
24567                 td.xyear = y2;
24568             }else{
24569                 y2 = y - (5-Math.round(i * .5));
24570                 td.firstChild.innerHTML = y2;
24571                 td.xyear = y2;
24572             }
24573             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24574         }
24575     },
24576
24577     updateMPMonth : function(sm){
24578         this.mpMonths.each(function(m, a, i){
24579             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24580         });
24581     },
24582
24583     selectMPMonth: function(m){
24584         
24585     },
24586
24587     onMonthClick : function(e, t){
24588         e.stopEvent();
24589         var el = new Roo.Element(t), pn;
24590         if(el.is('button.x-date-mp-cancel')){
24591             this.hideMonthPicker();
24592         }
24593         else if(el.is('button.x-date-mp-ok')){
24594             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24595             this.hideMonthPicker();
24596         }
24597         else if(pn = el.up('td.x-date-mp-month', 2)){
24598             this.mpMonths.removeClass('x-date-mp-sel');
24599             pn.addClass('x-date-mp-sel');
24600             this.mpSelMonth = pn.dom.xmonth;
24601         }
24602         else if(pn = el.up('td.x-date-mp-year', 2)){
24603             this.mpYears.removeClass('x-date-mp-sel');
24604             pn.addClass('x-date-mp-sel');
24605             this.mpSelYear = pn.dom.xyear;
24606         }
24607         else if(el.is('a.x-date-mp-prev')){
24608             this.updateMPYear(this.mpyear-10);
24609         }
24610         else if(el.is('a.x-date-mp-next')){
24611             this.updateMPYear(this.mpyear+10);
24612         }
24613     },
24614
24615     onMonthDblClick : function(e, t){
24616         e.stopEvent();
24617         var el = new Roo.Element(t), pn;
24618         if(pn = el.up('td.x-date-mp-month', 2)){
24619             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24620             this.hideMonthPicker();
24621         }
24622         else if(pn = el.up('td.x-date-mp-year', 2)){
24623             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24624             this.hideMonthPicker();
24625         }
24626     },
24627
24628     hideMonthPicker : function(disableAnim){
24629         if(this.monthPicker){
24630             if(disableAnim === true){
24631                 this.monthPicker.hide();
24632             }else{
24633                 this.monthPicker.slideOut('t', {duration:.2});
24634             }
24635         }
24636     },
24637
24638     // private
24639     showPrevMonth : function(e){
24640         this.update(this.activeDate.add("mo", -1));
24641     },
24642
24643     // private
24644     showNextMonth : function(e){
24645         this.update(this.activeDate.add("mo", 1));
24646     },
24647
24648     // private
24649     showPrevYear : function(){
24650         this.update(this.activeDate.add("y", -1));
24651     },
24652
24653     // private
24654     showNextYear : function(){
24655         this.update(this.activeDate.add("y", 1));
24656     },
24657
24658     // private
24659     handleMouseWheel : function(e){
24660         var delta = e.getWheelDelta();
24661         if(delta > 0){
24662             this.showPrevMonth();
24663             e.stopEvent();
24664         } else if(delta < 0){
24665             this.showNextMonth();
24666             e.stopEvent();
24667         }
24668     },
24669
24670     // private
24671     handleDateClick : function(e, t){
24672         e.stopEvent();
24673         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24674             this.setValue(new Date(t.dateValue));
24675             this.fireEvent("select", this, this.value);
24676         }
24677     },
24678
24679     // private
24680     selectToday : function(){
24681         this.setValue(new Date().clearTime());
24682         this.fireEvent("select", this, this.value);
24683     },
24684
24685     // private
24686     update : function(date){
24687         var vd = this.activeDate;
24688         this.activeDate = date;
24689         if(vd && this.el){
24690             var t = date.getTime();
24691             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24692                 this.cells.removeClass("x-date-selected");
24693                 this.cells.each(function(c){
24694                    if(c.dom.firstChild.dateValue == t){
24695                        c.addClass("x-date-selected");
24696                        setTimeout(function(){
24697                             try{c.dom.firstChild.focus();}catch(e){}
24698                        }, 50);
24699                        return false;
24700                    }
24701                 });
24702                 return;
24703             }
24704         }
24705         var days = date.getDaysInMonth();
24706         var firstOfMonth = date.getFirstDateOfMonth();
24707         var startingPos = firstOfMonth.getDay()-this.startDay;
24708
24709         if(startingPos <= this.startDay){
24710             startingPos += 7;
24711         }
24712
24713         var pm = date.add("mo", -1);
24714         var prevStart = pm.getDaysInMonth()-startingPos;
24715
24716         var cells = this.cells.elements;
24717         var textEls = this.textNodes;
24718         days += startingPos;
24719
24720         // convert everything to numbers so it's fast
24721         var day = 86400000;
24722         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24723         var today = new Date().clearTime().getTime();
24724         var sel = date.clearTime().getTime();
24725         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24726         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24727         var ddMatch = this.disabledDatesRE;
24728         var ddText = this.disabledDatesText;
24729         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24730         var ddaysText = this.disabledDaysText;
24731         var format = this.format;
24732
24733         var setCellClass = function(cal, cell){
24734             cell.title = "";
24735             var t = d.getTime();
24736             cell.firstChild.dateValue = t;
24737             if(t == today){
24738                 cell.className += " x-date-today";
24739                 cell.title = cal.todayText;
24740             }
24741             if(t == sel){
24742                 cell.className += " x-date-selected";
24743                 setTimeout(function(){
24744                     try{cell.firstChild.focus();}catch(e){}
24745                 }, 50);
24746             }
24747             // disabling
24748             if(t < min) {
24749                 cell.className = " x-date-disabled";
24750                 cell.title = cal.minText;
24751                 return;
24752             }
24753             if(t > max) {
24754                 cell.className = " x-date-disabled";
24755                 cell.title = cal.maxText;
24756                 return;
24757             }
24758             if(ddays){
24759                 if(ddays.indexOf(d.getDay()) != -1){
24760                     cell.title = ddaysText;
24761                     cell.className = " x-date-disabled";
24762                 }
24763             }
24764             if(ddMatch && format){
24765                 var fvalue = d.dateFormat(format);
24766                 if(ddMatch.test(fvalue)){
24767                     cell.title = ddText.replace("%0", fvalue);
24768                     cell.className = " x-date-disabled";
24769                 }
24770             }
24771         };
24772
24773         var i = 0;
24774         for(; i < startingPos; i++) {
24775             textEls[i].innerHTML = (++prevStart);
24776             d.setDate(d.getDate()+1);
24777             cells[i].className = "x-date-prevday";
24778             setCellClass(this, cells[i]);
24779         }
24780         for(; i < days; i++){
24781             intDay = i - startingPos + 1;
24782             textEls[i].innerHTML = (intDay);
24783             d.setDate(d.getDate()+1);
24784             cells[i].className = "x-date-active";
24785             setCellClass(this, cells[i]);
24786         }
24787         var extraDays = 0;
24788         for(; i < 42; i++) {
24789              textEls[i].innerHTML = (++extraDays);
24790              d.setDate(d.getDate()+1);
24791              cells[i].className = "x-date-nextday";
24792              setCellClass(this, cells[i]);
24793         }
24794
24795         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24796
24797         if(!this.internalRender){
24798             var main = this.el.dom.firstChild;
24799             var w = main.offsetWidth;
24800             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24801             Roo.fly(main).setWidth(w);
24802             this.internalRender = true;
24803             // opera does not respect the auto grow header center column
24804             // then, after it gets a width opera refuses to recalculate
24805             // without a second pass
24806             if(Roo.isOpera && !this.secondPass){
24807                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24808                 this.secondPass = true;
24809                 this.update.defer(10, this, [date]);
24810             }
24811         }
24812     }
24813 });/*
24814  * Based on:
24815  * Ext JS Library 1.1.1
24816  * Copyright(c) 2006-2007, Ext JS, LLC.
24817  *
24818  * Originally Released Under LGPL - original licence link has changed is not relivant.
24819  *
24820  * Fork - LGPL
24821  * <script type="text/javascript">
24822  */
24823 /**
24824  * @class Roo.TabPanel
24825  * @extends Roo.util.Observable
24826  * A lightweight tab container.
24827  * <br><br>
24828  * Usage:
24829  * <pre><code>
24830 // basic tabs 1, built from existing content
24831 var tabs = new Roo.TabPanel("tabs1");
24832 tabs.addTab("script", "View Script");
24833 tabs.addTab("markup", "View Markup");
24834 tabs.activate("script");
24835
24836 // more advanced tabs, built from javascript
24837 var jtabs = new Roo.TabPanel("jtabs");
24838 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24839
24840 // set up the UpdateManager
24841 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24842 var updater = tab2.getUpdateManager();
24843 updater.setDefaultUrl("ajax1.htm");
24844 tab2.on('activate', updater.refresh, updater, true);
24845
24846 // Use setUrl for Ajax loading
24847 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24848 tab3.setUrl("ajax2.htm", null, true);
24849
24850 // Disabled tab
24851 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24852 tab4.disable();
24853
24854 jtabs.activate("jtabs-1");
24855  * </code></pre>
24856  * @constructor
24857  * Create a new TabPanel.
24858  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24859  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24860  */
24861 Roo.TabPanel = function(container, config){
24862     /**
24863     * The container element for this TabPanel.
24864     * @type Roo.Element
24865     */
24866     this.el = Roo.get(container, true);
24867     if(config){
24868         if(typeof config == "boolean"){
24869             this.tabPosition = config ? "bottom" : "top";
24870         }else{
24871             Roo.apply(this, config);
24872         }
24873     }
24874     if(this.tabPosition == "bottom"){
24875         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24876         this.el.addClass("x-tabs-bottom");
24877     }
24878     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24879     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24880     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24881     if(Roo.isIE){
24882         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24883     }
24884     if(this.tabPosition != "bottom"){
24885     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24886      * @type Roo.Element
24887      */
24888       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24889       this.el.addClass("x-tabs-top");
24890     }
24891     this.items = [];
24892
24893     this.bodyEl.setStyle("position", "relative");
24894
24895     this.active = null;
24896     this.activateDelegate = this.activate.createDelegate(this);
24897
24898     this.addEvents({
24899         /**
24900          * @event tabchange
24901          * Fires when the active tab changes
24902          * @param {Roo.TabPanel} this
24903          * @param {Roo.TabPanelItem} activePanel The new active tab
24904          */
24905         "tabchange": true,
24906         /**
24907          * @event beforetabchange
24908          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24909          * @param {Roo.TabPanel} this
24910          * @param {Object} e Set cancel to true on this object to cancel the tab change
24911          * @param {Roo.TabPanelItem} tab The tab being changed to
24912          */
24913         "beforetabchange" : true
24914     });
24915
24916     Roo.EventManager.onWindowResize(this.onResize, this);
24917     this.cpad = this.el.getPadding("lr");
24918     this.hiddenCount = 0;
24919
24920     Roo.TabPanel.superclass.constructor.call(this);
24921 };
24922
24923 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24924         /*
24925          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24926          */
24927     tabPosition : "top",
24928         /*
24929          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24930          */
24931     currentTabWidth : 0,
24932         /*
24933          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24934          */
24935     minTabWidth : 40,
24936         /*
24937          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24938          */
24939     maxTabWidth : 250,
24940         /*
24941          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24942          */
24943     preferredTabWidth : 175,
24944         /*
24945          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24946          */
24947     resizeTabs : false,
24948         /*
24949          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24950          */
24951     monitorResize : true,
24952
24953     /**
24954      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24955      * @param {String} id The id of the div to use <b>or create</b>
24956      * @param {String} text The text for the tab
24957      * @param {String} content (optional) Content to put in the TabPanelItem body
24958      * @param {Boolean} closable (optional) True to create a close icon on the tab
24959      * @return {Roo.TabPanelItem} The created TabPanelItem
24960      */
24961     addTab : function(id, text, content, closable){
24962         var item = new Roo.TabPanelItem(this, id, text, closable);
24963         this.addTabItem(item);
24964         if(content){
24965             item.setContent(content);
24966         }
24967         return item;
24968     },
24969
24970     /**
24971      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24972      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24973      * @return {Roo.TabPanelItem}
24974      */
24975     getTab : function(id){
24976         return this.items[id];
24977     },
24978
24979     /**
24980      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24981      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24982      */
24983     hideTab : function(id){
24984         var t = this.items[id];
24985         if(!t.isHidden()){
24986            t.setHidden(true);
24987            this.hiddenCount++;
24988            this.autoSizeTabs();
24989         }
24990     },
24991
24992     /**
24993      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24994      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24995      */
24996     unhideTab : function(id){
24997         var t = this.items[id];
24998         if(t.isHidden()){
24999            t.setHidden(false);
25000            this.hiddenCount--;
25001            this.autoSizeTabs();
25002         }
25003     },
25004
25005     /**
25006      * Adds an existing {@link Roo.TabPanelItem}.
25007      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25008      */
25009     addTabItem : function(item){
25010         this.items[item.id] = item;
25011         this.items.push(item);
25012         if(this.resizeTabs){
25013            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25014            this.autoSizeTabs();
25015         }else{
25016             item.autoSize();
25017         }
25018     },
25019
25020     /**
25021      * Removes a {@link Roo.TabPanelItem}.
25022      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25023      */
25024     removeTab : function(id){
25025         var items = this.items;
25026         var tab = items[id];
25027         if(!tab) return;
25028         var index = items.indexOf(tab);
25029         if(this.active == tab && items.length > 1){
25030             var newTab = this.getNextAvailable(index);
25031             if(newTab)newTab.activate();
25032         }
25033         this.stripEl.dom.removeChild(tab.pnode.dom);
25034         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25035             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25036         }
25037         items.splice(index, 1);
25038         delete this.items[tab.id];
25039         tab.fireEvent("close", tab);
25040         tab.purgeListeners();
25041         this.autoSizeTabs();
25042     },
25043
25044     getNextAvailable : function(start){
25045         var items = this.items;
25046         var index = start;
25047         // look for a next tab that will slide over to
25048         // replace the one being removed
25049         while(index < items.length){
25050             var item = items[++index];
25051             if(item && !item.isHidden()){
25052                 return item;
25053             }
25054         }
25055         // if one isn't found select the previous tab (on the left)
25056         index = start;
25057         while(index >= 0){
25058             var item = items[--index];
25059             if(item && !item.isHidden()){
25060                 return item;
25061             }
25062         }
25063         return null;
25064     },
25065
25066     /**
25067      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25068      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25069      */
25070     disableTab : function(id){
25071         var tab = this.items[id];
25072         if(tab && this.active != tab){
25073             tab.disable();
25074         }
25075     },
25076
25077     /**
25078      * Enables a {@link Roo.TabPanelItem} that is disabled.
25079      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25080      */
25081     enableTab : function(id){
25082         var tab = this.items[id];
25083         tab.enable();
25084     },
25085
25086     /**
25087      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25088      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25089      * @return {Roo.TabPanelItem} The TabPanelItem.
25090      */
25091     activate : function(id){
25092         var tab = this.items[id];
25093         if(!tab){
25094             return null;
25095         }
25096         if(tab == this.active || tab.disabled){
25097             return tab;
25098         }
25099         var e = {};
25100         this.fireEvent("beforetabchange", this, e, tab);
25101         if(e.cancel !== true && !tab.disabled){
25102             if(this.active){
25103                 this.active.hide();
25104             }
25105             this.active = this.items[id];
25106             this.active.show();
25107             this.fireEvent("tabchange", this, this.active);
25108         }
25109         return tab;
25110     },
25111
25112     /**
25113      * Gets the active {@link Roo.TabPanelItem}.
25114      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25115      */
25116     getActiveTab : function(){
25117         return this.active;
25118     },
25119
25120     /**
25121      * Updates the tab body element to fit the height of the container element
25122      * for overflow scrolling
25123      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25124      */
25125     syncHeight : function(targetHeight){
25126         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25127         var bm = this.bodyEl.getMargins();
25128         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25129         this.bodyEl.setHeight(newHeight);
25130         return newHeight;
25131     },
25132
25133     onResize : function(){
25134         if(this.monitorResize){
25135             this.autoSizeTabs();
25136         }
25137     },
25138
25139     /**
25140      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25141      */
25142     beginUpdate : function(){
25143         this.updating = true;
25144     },
25145
25146     /**
25147      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25148      */
25149     endUpdate : function(){
25150         this.updating = false;
25151         this.autoSizeTabs();
25152     },
25153
25154     /**
25155      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25156      */
25157     autoSizeTabs : function(){
25158         var count = this.items.length;
25159         var vcount = count - this.hiddenCount;
25160         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25161         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25162         var availWidth = Math.floor(w / vcount);
25163         var b = this.stripBody;
25164         if(b.getWidth() > w){
25165             var tabs = this.items;
25166             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25167             if(availWidth < this.minTabWidth){
25168                 /*if(!this.sleft){    // incomplete scrolling code
25169                     this.createScrollButtons();
25170                 }
25171                 this.showScroll();
25172                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25173             }
25174         }else{
25175             if(this.currentTabWidth < this.preferredTabWidth){
25176                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25177             }
25178         }
25179     },
25180
25181     /**
25182      * Returns the number of tabs in this TabPanel.
25183      * @return {Number}
25184      */
25185      getCount : function(){
25186          return this.items.length;
25187      },
25188
25189     /**
25190      * Resizes all the tabs to the passed width
25191      * @param {Number} The new width
25192      */
25193     setTabWidth : function(width){
25194         this.currentTabWidth = width;
25195         for(var i = 0, len = this.items.length; i < len; i++) {
25196                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25197         }
25198     },
25199
25200     /**
25201      * Destroys this TabPanel
25202      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25203      */
25204     destroy : function(removeEl){
25205         Roo.EventManager.removeResizeListener(this.onResize, this);
25206         for(var i = 0, len = this.items.length; i < len; i++){
25207             this.items[i].purgeListeners();
25208         }
25209         if(removeEl === true){
25210             this.el.update("");
25211             this.el.remove();
25212         }
25213     }
25214 });
25215
25216 /**
25217  * @class Roo.TabPanelItem
25218  * @extends Roo.util.Observable
25219  * Represents an individual item (tab plus body) in a TabPanel.
25220  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25221  * @param {String} id The id of this TabPanelItem
25222  * @param {String} text The text for the tab of this TabPanelItem
25223  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25224  */
25225 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25226     /**
25227      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25228      * @type Roo.TabPanel
25229      */
25230     this.tabPanel = tabPanel;
25231     /**
25232      * The id for this TabPanelItem
25233      * @type String
25234      */
25235     this.id = id;
25236     /** @private */
25237     this.disabled = false;
25238     /** @private */
25239     this.text = text;
25240     /** @private */
25241     this.loaded = false;
25242     this.closable = closable;
25243
25244     /**
25245      * The body element for this TabPanelItem.
25246      * @type Roo.Element
25247      */
25248     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25249     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25250     this.bodyEl.setStyle("display", "block");
25251     this.bodyEl.setStyle("zoom", "1");
25252     this.hideAction();
25253
25254     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25255     /** @private */
25256     this.el = Roo.get(els.el, true);
25257     this.inner = Roo.get(els.inner, true);
25258     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25259     this.pnode = Roo.get(els.el.parentNode, true);
25260     this.el.on("mousedown", this.onTabMouseDown, this);
25261     this.el.on("click", this.onTabClick, this);
25262     /** @private */
25263     if(closable){
25264         var c = Roo.get(els.close, true);
25265         c.dom.title = this.closeText;
25266         c.addClassOnOver("close-over");
25267         c.on("click", this.closeClick, this);
25268      }
25269
25270     this.addEvents({
25271          /**
25272          * @event activate
25273          * Fires when this tab becomes the active tab.
25274          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25275          * @param {Roo.TabPanelItem} this
25276          */
25277         "activate": true,
25278         /**
25279          * @event beforeclose
25280          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25281          * @param {Roo.TabPanelItem} this
25282          * @param {Object} e Set cancel to true on this object to cancel the close.
25283          */
25284         "beforeclose": true,
25285         /**
25286          * @event close
25287          * Fires when this tab is closed.
25288          * @param {Roo.TabPanelItem} this
25289          */
25290          "close": true,
25291         /**
25292          * @event deactivate
25293          * Fires when this tab is no longer the active tab.
25294          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25295          * @param {Roo.TabPanelItem} this
25296          */
25297          "deactivate" : true
25298     });
25299     this.hidden = false;
25300
25301     Roo.TabPanelItem.superclass.constructor.call(this);
25302 };
25303
25304 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25305     purgeListeners : function(){
25306        Roo.util.Observable.prototype.purgeListeners.call(this);
25307        this.el.removeAllListeners();
25308     },
25309     /**
25310      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25311      */
25312     show : function(){
25313         this.pnode.addClass("on");
25314         this.showAction();
25315         if(Roo.isOpera){
25316             this.tabPanel.stripWrap.repaint();
25317         }
25318         this.fireEvent("activate", this.tabPanel, this);
25319     },
25320
25321     /**
25322      * Returns true if this tab is the active tab.
25323      * @return {Boolean}
25324      */
25325     isActive : function(){
25326         return this.tabPanel.getActiveTab() == this;
25327     },
25328
25329     /**
25330      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25331      */
25332     hide : function(){
25333         this.pnode.removeClass("on");
25334         this.hideAction();
25335         this.fireEvent("deactivate", this.tabPanel, this);
25336     },
25337
25338     hideAction : function(){
25339         this.bodyEl.hide();
25340         this.bodyEl.setStyle("position", "absolute");
25341         this.bodyEl.setLeft("-20000px");
25342         this.bodyEl.setTop("-20000px");
25343     },
25344
25345     showAction : function(){
25346         this.bodyEl.setStyle("position", "relative");
25347         this.bodyEl.setTop("");
25348         this.bodyEl.setLeft("");
25349         this.bodyEl.show();
25350     },
25351
25352     /**
25353      * Set the tooltip for the tab.
25354      * @param {String} tooltip The tab's tooltip
25355      */
25356     setTooltip : function(text){
25357         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25358             this.textEl.dom.qtip = text;
25359             this.textEl.dom.removeAttribute('title');
25360         }else{
25361             this.textEl.dom.title = text;
25362         }
25363     },
25364
25365     onTabClick : function(e){
25366         e.preventDefault();
25367         this.tabPanel.activate(this.id);
25368     },
25369
25370     onTabMouseDown : function(e){
25371         e.preventDefault();
25372         this.tabPanel.activate(this.id);
25373     },
25374
25375     getWidth : function(){
25376         return this.inner.getWidth();
25377     },
25378
25379     setWidth : function(width){
25380         var iwidth = width - this.pnode.getPadding("lr");
25381         this.inner.setWidth(iwidth);
25382         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25383         this.pnode.setWidth(width);
25384     },
25385
25386     /**
25387      * Show or hide the tab
25388      * @param {Boolean} hidden True to hide or false to show.
25389      */
25390     setHidden : function(hidden){
25391         this.hidden = hidden;
25392         this.pnode.setStyle("display", hidden ? "none" : "");
25393     },
25394
25395     /**
25396      * Returns true if this tab is "hidden"
25397      * @return {Boolean}
25398      */
25399     isHidden : function(){
25400         return this.hidden;
25401     },
25402
25403     /**
25404      * Returns the text for this tab
25405      * @return {String}
25406      */
25407     getText : function(){
25408         return this.text;
25409     },
25410
25411     autoSize : function(){
25412         //this.el.beginMeasure();
25413         this.textEl.setWidth(1);
25414         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25415         //this.el.endMeasure();
25416     },
25417
25418     /**
25419      * Sets the text for the tab (Note: this also sets the tooltip text)
25420      * @param {String} text The tab's text and tooltip
25421      */
25422     setText : function(text){
25423         this.text = text;
25424         this.textEl.update(text);
25425         this.setTooltip(text);
25426         if(!this.tabPanel.resizeTabs){
25427             this.autoSize();
25428         }
25429     },
25430     /**
25431      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25432      */
25433     activate : function(){
25434         this.tabPanel.activate(this.id);
25435     },
25436
25437     /**
25438      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25439      */
25440     disable : function(){
25441         if(this.tabPanel.active != this){
25442             this.disabled = true;
25443             this.pnode.addClass("disabled");
25444         }
25445     },
25446
25447     /**
25448      * Enables this TabPanelItem if it was previously disabled.
25449      */
25450     enable : function(){
25451         this.disabled = false;
25452         this.pnode.removeClass("disabled");
25453     },
25454
25455     /**
25456      * Sets the content for this TabPanelItem.
25457      * @param {String} content The content
25458      * @param {Boolean} loadScripts true to look for and load scripts
25459      */
25460     setContent : function(content, loadScripts){
25461         this.bodyEl.update(content, loadScripts);
25462     },
25463
25464     /**
25465      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25466      * @return {Roo.UpdateManager} The UpdateManager
25467      */
25468     getUpdateManager : function(){
25469         return this.bodyEl.getUpdateManager();
25470     },
25471
25472     /**
25473      * Set a URL to be used to load the content for this TabPanelItem.
25474      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25475      * @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)
25476      * @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)
25477      * @return {Roo.UpdateManager} The UpdateManager
25478      */
25479     setUrl : function(url, params, loadOnce){
25480         if(this.refreshDelegate){
25481             this.un('activate', this.refreshDelegate);
25482         }
25483         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25484         this.on("activate", this.refreshDelegate);
25485         return this.bodyEl.getUpdateManager();
25486     },
25487
25488     /** @private */
25489     _handleRefresh : function(url, params, loadOnce){
25490         if(!loadOnce || !this.loaded){
25491             var updater = this.bodyEl.getUpdateManager();
25492             updater.update(url, params, this._setLoaded.createDelegate(this));
25493         }
25494     },
25495
25496     /**
25497      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25498      *   Will fail silently if the setUrl method has not been called.
25499      *   This does not activate the panel, just updates its content.
25500      */
25501     refresh : function(){
25502         if(this.refreshDelegate){
25503            this.loaded = false;
25504            this.refreshDelegate();
25505         }
25506     },
25507
25508     /** @private */
25509     _setLoaded : function(){
25510         this.loaded = true;
25511     },
25512
25513     /** @private */
25514     closeClick : function(e){
25515         var o = {};
25516         e.stopEvent();
25517         this.fireEvent("beforeclose", this, o);
25518         if(o.cancel !== true){
25519             this.tabPanel.removeTab(this.id);
25520         }
25521     },
25522     /**
25523      * The text displayed in the tooltip for the close icon.
25524      * @type String
25525      */
25526     closeText : "Close this tab"
25527 });
25528
25529 /** @private */
25530 Roo.TabPanel.prototype.createStrip = function(container){
25531     var strip = document.createElement("div");
25532     strip.className = "x-tabs-wrap";
25533     container.appendChild(strip);
25534     return strip;
25535 };
25536 /** @private */
25537 Roo.TabPanel.prototype.createStripList = function(strip){
25538     // div wrapper for retard IE
25539     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>';
25540     return strip.firstChild.firstChild.firstChild.firstChild;
25541 };
25542 /** @private */
25543 Roo.TabPanel.prototype.createBody = function(container){
25544     var body = document.createElement("div");
25545     Roo.id(body, "tab-body");
25546     Roo.fly(body).addClass("x-tabs-body");
25547     container.appendChild(body);
25548     return body;
25549 };
25550 /** @private */
25551 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25552     var body = Roo.getDom(id);
25553     if(!body){
25554         body = document.createElement("div");
25555         body.id = id;
25556     }
25557     Roo.fly(body).addClass("x-tabs-item-body");
25558     bodyEl.insertBefore(body, bodyEl.firstChild);
25559     return body;
25560 };
25561 /** @private */
25562 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25563     var td = document.createElement("td");
25564     stripEl.appendChild(td);
25565     if(closable){
25566         td.className = "x-tabs-closable";
25567         if(!this.closeTpl){
25568             this.closeTpl = new Roo.Template(
25569                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25570                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25571                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25572             );
25573         }
25574         var el = this.closeTpl.overwrite(td, {"text": text});
25575         var close = el.getElementsByTagName("div")[0];
25576         var inner = el.getElementsByTagName("em")[0];
25577         return {"el": el, "close": close, "inner": inner};
25578     } else {
25579         if(!this.tabTpl){
25580             this.tabTpl = new Roo.Template(
25581                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25582                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25583             );
25584         }
25585         var el = this.tabTpl.overwrite(td, {"text": text});
25586         var inner = el.getElementsByTagName("em")[0];
25587         return {"el": el, "inner": inner};
25588     }
25589 };/*
25590  * Based on:
25591  * Ext JS Library 1.1.1
25592  * Copyright(c) 2006-2007, Ext JS, LLC.
25593  *
25594  * Originally Released Under LGPL - original licence link has changed is not relivant.
25595  *
25596  * Fork - LGPL
25597  * <script type="text/javascript">
25598  */
25599
25600 /**
25601  * @class Roo.Button
25602  * @extends Roo.util.Observable
25603  * Simple Button class
25604  * @cfg {String} text The button text
25605  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25606  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25607  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25608  * @cfg {Object} scope The scope of the handler
25609  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25610  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25611  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25612  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25613  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25614  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25615    applies if enableToggle = true)
25616  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25617  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25618   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25619  * @constructor
25620  * Create a new button
25621  * @param {Object} config The config object
25622  */
25623 Roo.Button = function(renderTo, config)
25624 {
25625     if (!config) {
25626         config = renderTo;
25627         renderTo = config.renderTo || false;
25628     }
25629     
25630     Roo.apply(this, config);
25631     this.addEvents({
25632         /**
25633              * @event click
25634              * Fires when this button is clicked
25635              * @param {Button} this
25636              * @param {EventObject} e The click event
25637              */
25638             "click" : true,
25639         /**
25640              * @event toggle
25641              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25642              * @param {Button} this
25643              * @param {Boolean} pressed
25644              */
25645             "toggle" : true,
25646         /**
25647              * @event mouseover
25648              * Fires when the mouse hovers over the button
25649              * @param {Button} this
25650              * @param {Event} e The event object
25651              */
25652         'mouseover' : true,
25653         /**
25654              * @event mouseout
25655              * Fires when the mouse exits the button
25656              * @param {Button} this
25657              * @param {Event} e The event object
25658              */
25659         'mouseout': true,
25660          /**
25661              * @event render
25662              * Fires when the button is rendered
25663              * @param {Button} this
25664              */
25665         'render': true
25666     });
25667     if(this.menu){
25668         this.menu = Roo.menu.MenuMgr.get(this.menu);
25669     }
25670     if(renderTo){
25671         this.render(renderTo);
25672     }
25673     
25674     Roo.util.Observable.call(this);
25675 };
25676
25677 Roo.extend(Roo.Button, Roo.util.Observable, {
25678     /**
25679      * 
25680      */
25681     
25682     /**
25683      * Read-only. True if this button is hidden
25684      * @type Boolean
25685      */
25686     hidden : false,
25687     /**
25688      * Read-only. True if this button is disabled
25689      * @type Boolean
25690      */
25691     disabled : false,
25692     /**
25693      * Read-only. True if this button is pressed (only if enableToggle = true)
25694      * @type Boolean
25695      */
25696     pressed : false,
25697
25698     /**
25699      * @cfg {Number} tabIndex 
25700      * The DOM tabIndex for this button (defaults to undefined)
25701      */
25702     tabIndex : undefined,
25703
25704     /**
25705      * @cfg {Boolean} enableToggle
25706      * True to enable pressed/not pressed toggling (defaults to false)
25707      */
25708     enableToggle: false,
25709     /**
25710      * @cfg {Mixed} menu
25711      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25712      */
25713     menu : undefined,
25714     /**
25715      * @cfg {String} menuAlign
25716      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25717      */
25718     menuAlign : "tl-bl?",
25719
25720     /**
25721      * @cfg {String} iconCls
25722      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25723      */
25724     iconCls : undefined,
25725     /**
25726      * @cfg {String} type
25727      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25728      */
25729     type : 'button',
25730
25731     // private
25732     menuClassTarget: 'tr',
25733
25734     /**
25735      * @cfg {String} clickEvent
25736      * The type of event to map to the button's event handler (defaults to 'click')
25737      */
25738     clickEvent : 'click',
25739
25740     /**
25741      * @cfg {Boolean} handleMouseEvents
25742      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25743      */
25744     handleMouseEvents : true,
25745
25746     /**
25747      * @cfg {String} tooltipType
25748      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25749      */
25750     tooltipType : 'qtip',
25751
25752     /**
25753      * @cfg {String} cls
25754      * A CSS class to apply to the button's main element.
25755      */
25756     
25757     /**
25758      * @cfg {Roo.Template} template (Optional)
25759      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25760      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25761      * require code modifications if required elements (e.g. a button) aren't present.
25762      */
25763
25764     // private
25765     render : function(renderTo){
25766         var btn;
25767         if(this.hideParent){
25768             this.parentEl = Roo.get(renderTo);
25769         }
25770         if(!this.dhconfig){
25771             if(!this.template){
25772                 if(!Roo.Button.buttonTemplate){
25773                     // hideous table template
25774                     Roo.Button.buttonTemplate = new Roo.Template(
25775                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25776                         '<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>',
25777                         "</tr></tbody></table>");
25778                 }
25779                 this.template = Roo.Button.buttonTemplate;
25780             }
25781             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25782             var btnEl = btn.child("button:first");
25783             btnEl.on('focus', this.onFocus, this);
25784             btnEl.on('blur', this.onBlur, this);
25785             if(this.cls){
25786                 btn.addClass(this.cls);
25787             }
25788             if(this.icon){
25789                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25790             }
25791             if(this.iconCls){
25792                 btnEl.addClass(this.iconCls);
25793                 if(!this.cls){
25794                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25795                 }
25796             }
25797             if(this.tabIndex !== undefined){
25798                 btnEl.dom.tabIndex = this.tabIndex;
25799             }
25800             if(this.tooltip){
25801                 if(typeof this.tooltip == 'object'){
25802                     Roo.QuickTips.tips(Roo.apply({
25803                           target: btnEl.id
25804                     }, this.tooltip));
25805                 } else {
25806                     btnEl.dom[this.tooltipType] = this.tooltip;
25807                 }
25808             }
25809         }else{
25810             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25811         }
25812         this.el = btn;
25813         if(this.id){
25814             this.el.dom.id = this.el.id = this.id;
25815         }
25816         if(this.menu){
25817             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25818             this.menu.on("show", this.onMenuShow, this);
25819             this.menu.on("hide", this.onMenuHide, this);
25820         }
25821         btn.addClass("x-btn");
25822         if(Roo.isIE && !Roo.isIE7){
25823             this.autoWidth.defer(1, this);
25824         }else{
25825             this.autoWidth();
25826         }
25827         if(this.handleMouseEvents){
25828             btn.on("mouseover", this.onMouseOver, this);
25829             btn.on("mouseout", this.onMouseOut, this);
25830             btn.on("mousedown", this.onMouseDown, this);
25831         }
25832         btn.on(this.clickEvent, this.onClick, this);
25833         //btn.on("mouseup", this.onMouseUp, this);
25834         if(this.hidden){
25835             this.hide();
25836         }
25837         if(this.disabled){
25838             this.disable();
25839         }
25840         Roo.ButtonToggleMgr.register(this);
25841         if(this.pressed){
25842             this.el.addClass("x-btn-pressed");
25843         }
25844         if(this.repeat){
25845             var repeater = new Roo.util.ClickRepeater(btn,
25846                 typeof this.repeat == "object" ? this.repeat : {}
25847             );
25848             repeater.on("click", this.onClick,  this);
25849         }
25850         this.fireEvent('render', this);
25851         
25852     },
25853     /**
25854      * Returns the button's underlying element
25855      * @return {Roo.Element} The element
25856      */
25857     getEl : function(){
25858         return this.el;  
25859     },
25860     
25861     /**
25862      * Destroys this Button and removes any listeners.
25863      */
25864     destroy : function(){
25865         Roo.ButtonToggleMgr.unregister(this);
25866         this.el.removeAllListeners();
25867         this.purgeListeners();
25868         this.el.remove();
25869     },
25870
25871     // private
25872     autoWidth : function(){
25873         if(this.el){
25874             this.el.setWidth("auto");
25875             if(Roo.isIE7 && Roo.isStrict){
25876                 var ib = this.el.child('button');
25877                 if(ib && ib.getWidth() > 20){
25878                     ib.clip();
25879                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25880                 }
25881             }
25882             if(this.minWidth){
25883                 if(this.hidden){
25884                     this.el.beginMeasure();
25885                 }
25886                 if(this.el.getWidth() < this.minWidth){
25887                     this.el.setWidth(this.minWidth);
25888                 }
25889                 if(this.hidden){
25890                     this.el.endMeasure();
25891                 }
25892             }
25893         }
25894     },
25895
25896     /**
25897      * Assigns this button's click handler
25898      * @param {Function} handler The function to call when the button is clicked
25899      * @param {Object} scope (optional) Scope for the function passed in
25900      */
25901     setHandler : function(handler, scope){
25902         this.handler = handler;
25903         this.scope = scope;  
25904     },
25905     
25906     /**
25907      * Sets this button's text
25908      * @param {String} text The button text
25909      */
25910     setText : function(text){
25911         this.text = text;
25912         if(this.el){
25913             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25914         }
25915         this.autoWidth();
25916     },
25917     
25918     /**
25919      * Gets the text for this button
25920      * @return {String} The button text
25921      */
25922     getText : function(){
25923         return this.text;  
25924     },
25925     
25926     /**
25927      * Show this button
25928      */
25929     show: function(){
25930         this.hidden = false;
25931         if(this.el){
25932             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25933         }
25934     },
25935     
25936     /**
25937      * Hide this button
25938      */
25939     hide: function(){
25940         this.hidden = true;
25941         if(this.el){
25942             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25943         }
25944     },
25945     
25946     /**
25947      * Convenience function for boolean show/hide
25948      * @param {Boolean} visible True to show, false to hide
25949      */
25950     setVisible: function(visible){
25951         if(visible) {
25952             this.show();
25953         }else{
25954             this.hide();
25955         }
25956     },
25957     
25958     /**
25959      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25960      * @param {Boolean} state (optional) Force a particular state
25961      */
25962     toggle : function(state){
25963         state = state === undefined ? !this.pressed : state;
25964         if(state != this.pressed){
25965             if(state){
25966                 this.el.addClass("x-btn-pressed");
25967                 this.pressed = true;
25968                 this.fireEvent("toggle", this, true);
25969             }else{
25970                 this.el.removeClass("x-btn-pressed");
25971                 this.pressed = false;
25972                 this.fireEvent("toggle", this, false);
25973             }
25974             if(this.toggleHandler){
25975                 this.toggleHandler.call(this.scope || this, this, state);
25976             }
25977         }
25978     },
25979     
25980     /**
25981      * Focus the button
25982      */
25983     focus : function(){
25984         this.el.child('button:first').focus();
25985     },
25986     
25987     /**
25988      * Disable this button
25989      */
25990     disable : function(){
25991         if(this.el){
25992             this.el.addClass("x-btn-disabled");
25993         }
25994         this.disabled = true;
25995     },
25996     
25997     /**
25998      * Enable this button
25999      */
26000     enable : function(){
26001         if(this.el){
26002             this.el.removeClass("x-btn-disabled");
26003         }
26004         this.disabled = false;
26005     },
26006
26007     /**
26008      * Convenience function for boolean enable/disable
26009      * @param {Boolean} enabled True to enable, false to disable
26010      */
26011     setDisabled : function(v){
26012         this[v !== true ? "enable" : "disable"]();
26013     },
26014
26015     // private
26016     onClick : function(e){
26017         if(e){
26018             e.preventDefault();
26019         }
26020         if(e.button != 0){
26021             return;
26022         }
26023         if(!this.disabled){
26024             if(this.enableToggle){
26025                 this.toggle();
26026             }
26027             if(this.menu && !this.menu.isVisible()){
26028                 this.menu.show(this.el, this.menuAlign);
26029             }
26030             this.fireEvent("click", this, e);
26031             if(this.handler){
26032                 this.el.removeClass("x-btn-over");
26033                 this.handler.call(this.scope || this, this, e);
26034             }
26035         }
26036     },
26037     // private
26038     onMouseOver : function(e){
26039         if(!this.disabled){
26040             this.el.addClass("x-btn-over");
26041             this.fireEvent('mouseover', this, e);
26042         }
26043     },
26044     // private
26045     onMouseOut : function(e){
26046         if(!e.within(this.el,  true)){
26047             this.el.removeClass("x-btn-over");
26048             this.fireEvent('mouseout', this, e);
26049         }
26050     },
26051     // private
26052     onFocus : function(e){
26053         if(!this.disabled){
26054             this.el.addClass("x-btn-focus");
26055         }
26056     },
26057     // private
26058     onBlur : function(e){
26059         this.el.removeClass("x-btn-focus");
26060     },
26061     // private
26062     onMouseDown : function(e){
26063         if(!this.disabled && e.button == 0){
26064             this.el.addClass("x-btn-click");
26065             Roo.get(document).on('mouseup', this.onMouseUp, this);
26066         }
26067     },
26068     // private
26069     onMouseUp : function(e){
26070         if(e.button == 0){
26071             this.el.removeClass("x-btn-click");
26072             Roo.get(document).un('mouseup', this.onMouseUp, this);
26073         }
26074     },
26075     // private
26076     onMenuShow : function(e){
26077         this.el.addClass("x-btn-menu-active");
26078     },
26079     // private
26080     onMenuHide : function(e){
26081         this.el.removeClass("x-btn-menu-active");
26082     }   
26083 });
26084
26085 // Private utility class used by Button
26086 Roo.ButtonToggleMgr = function(){
26087    var groups = {};
26088    
26089    function toggleGroup(btn, state){
26090        if(state){
26091            var g = groups[btn.toggleGroup];
26092            for(var i = 0, l = g.length; i < l; i++){
26093                if(g[i] != btn){
26094                    g[i].toggle(false);
26095                }
26096            }
26097        }
26098    }
26099    
26100    return {
26101        register : function(btn){
26102            if(!btn.toggleGroup){
26103                return;
26104            }
26105            var g = groups[btn.toggleGroup];
26106            if(!g){
26107                g = groups[btn.toggleGroup] = [];
26108            }
26109            g.push(btn);
26110            btn.on("toggle", toggleGroup);
26111        },
26112        
26113        unregister : function(btn){
26114            if(!btn.toggleGroup){
26115                return;
26116            }
26117            var g = groups[btn.toggleGroup];
26118            if(g){
26119                g.remove(btn);
26120                btn.un("toggle", toggleGroup);
26121            }
26122        }
26123    };
26124 }();/*
26125  * Based on:
26126  * Ext JS Library 1.1.1
26127  * Copyright(c) 2006-2007, Ext JS, LLC.
26128  *
26129  * Originally Released Under LGPL - original licence link has changed is not relivant.
26130  *
26131  * Fork - LGPL
26132  * <script type="text/javascript">
26133  */
26134  
26135 /**
26136  * @class Roo.SplitButton
26137  * @extends Roo.Button
26138  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26139  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26140  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26141  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26142  * @cfg {String} arrowTooltip The title attribute of the arrow
26143  * @constructor
26144  * Create a new menu button
26145  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26146  * @param {Object} config The config object
26147  */
26148 Roo.SplitButton = function(renderTo, config){
26149     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26150     /**
26151      * @event arrowclick
26152      * Fires when this button's arrow is clicked
26153      * @param {SplitButton} this
26154      * @param {EventObject} e The click event
26155      */
26156     this.addEvents({"arrowclick":true});
26157 };
26158
26159 Roo.extend(Roo.SplitButton, Roo.Button, {
26160     render : function(renderTo){
26161         // this is one sweet looking template!
26162         var tpl = new Roo.Template(
26163             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26164             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26165             '<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>',
26166             "</tbody></table></td><td>",
26167             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26168             '<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>',
26169             "</tbody></table></td></tr></table>"
26170         );
26171         var btn = tpl.append(renderTo, [this.text, this.type], true);
26172         var btnEl = btn.child("button");
26173         if(this.cls){
26174             btn.addClass(this.cls);
26175         }
26176         if(this.icon){
26177             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26178         }
26179         if(this.iconCls){
26180             btnEl.addClass(this.iconCls);
26181             if(!this.cls){
26182                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26183             }
26184         }
26185         this.el = btn;
26186         if(this.handleMouseEvents){
26187             btn.on("mouseover", this.onMouseOver, this);
26188             btn.on("mouseout", this.onMouseOut, this);
26189             btn.on("mousedown", this.onMouseDown, this);
26190             btn.on("mouseup", this.onMouseUp, this);
26191         }
26192         btn.on(this.clickEvent, this.onClick, this);
26193         if(this.tooltip){
26194             if(typeof this.tooltip == 'object'){
26195                 Roo.QuickTips.tips(Roo.apply({
26196                       target: btnEl.id
26197                 }, this.tooltip));
26198             } else {
26199                 btnEl.dom[this.tooltipType] = this.tooltip;
26200             }
26201         }
26202         if(this.arrowTooltip){
26203             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26204         }
26205         if(this.hidden){
26206             this.hide();
26207         }
26208         if(this.disabled){
26209             this.disable();
26210         }
26211         if(this.pressed){
26212             this.el.addClass("x-btn-pressed");
26213         }
26214         if(Roo.isIE && !Roo.isIE7){
26215             this.autoWidth.defer(1, this);
26216         }else{
26217             this.autoWidth();
26218         }
26219         if(this.menu){
26220             this.menu.on("show", this.onMenuShow, this);
26221             this.menu.on("hide", this.onMenuHide, this);
26222         }
26223         this.fireEvent('render', this);
26224     },
26225
26226     // private
26227     autoWidth : function(){
26228         if(this.el){
26229             var tbl = this.el.child("table:first");
26230             var tbl2 = this.el.child("table:last");
26231             this.el.setWidth("auto");
26232             tbl.setWidth("auto");
26233             if(Roo.isIE7 && Roo.isStrict){
26234                 var ib = this.el.child('button:first');
26235                 if(ib && ib.getWidth() > 20){
26236                     ib.clip();
26237                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26238                 }
26239             }
26240             if(this.minWidth){
26241                 if(this.hidden){
26242                     this.el.beginMeasure();
26243                 }
26244                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26245                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26246                 }
26247                 if(this.hidden){
26248                     this.el.endMeasure();
26249                 }
26250             }
26251             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26252         } 
26253     },
26254     /**
26255      * Sets this button's click handler
26256      * @param {Function} handler The function to call when the button is clicked
26257      * @param {Object} scope (optional) Scope for the function passed above
26258      */
26259     setHandler : function(handler, scope){
26260         this.handler = handler;
26261         this.scope = scope;  
26262     },
26263     
26264     /**
26265      * Sets this button's arrow click handler
26266      * @param {Function} handler The function to call when the arrow is clicked
26267      * @param {Object} scope (optional) Scope for the function passed above
26268      */
26269     setArrowHandler : function(handler, scope){
26270         this.arrowHandler = handler;
26271         this.scope = scope;  
26272     },
26273     
26274     /**
26275      * Focus the button
26276      */
26277     focus : function(){
26278         if(this.el){
26279             this.el.child("button:first").focus();
26280         }
26281     },
26282
26283     // private
26284     onClick : function(e){
26285         e.preventDefault();
26286         if(!this.disabled){
26287             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26288                 if(this.menu && !this.menu.isVisible()){
26289                     this.menu.show(this.el, this.menuAlign);
26290                 }
26291                 this.fireEvent("arrowclick", this, e);
26292                 if(this.arrowHandler){
26293                     this.arrowHandler.call(this.scope || this, this, e);
26294                 }
26295             }else{
26296                 this.fireEvent("click", this, e);
26297                 if(this.handler){
26298                     this.handler.call(this.scope || this, this, e);
26299                 }
26300             }
26301         }
26302     },
26303     // private
26304     onMouseDown : function(e){
26305         if(!this.disabled){
26306             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26307         }
26308     },
26309     // private
26310     onMouseUp : function(e){
26311         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26312     }   
26313 });
26314
26315
26316 // backwards compat
26317 Roo.MenuButton = Roo.SplitButton;/*
26318  * Based on:
26319  * Ext JS Library 1.1.1
26320  * Copyright(c) 2006-2007, Ext JS, LLC.
26321  *
26322  * Originally Released Under LGPL - original licence link has changed is not relivant.
26323  *
26324  * Fork - LGPL
26325  * <script type="text/javascript">
26326  */
26327
26328 /**
26329  * @class Roo.Toolbar
26330  * Basic Toolbar class.
26331  * @constructor
26332  * Creates a new Toolbar
26333  * @param {Object} config The config object
26334  */ 
26335 Roo.Toolbar = function(container, buttons, config)
26336 {
26337     /// old consturctor format still supported..
26338     if(container instanceof Array){ // omit the container for later rendering
26339         buttons = container;
26340         config = buttons;
26341         container = null;
26342     }
26343     if (typeof(container) == 'object' && container.xtype) {
26344         config = container;
26345         container = config.container;
26346         buttons = config.buttons; // not really - use items!!
26347     }
26348     var xitems = [];
26349     if (config && config.items) {
26350         xitems = config.items;
26351         delete config.items;
26352     }
26353     Roo.apply(this, config);
26354     this.buttons = buttons;
26355     
26356     if(container){
26357         this.render(container);
26358     }
26359     Roo.each(xitems, function(b) {
26360         this.add(b);
26361     }, this);
26362     
26363 };
26364
26365 Roo.Toolbar.prototype = {
26366     /**
26367      * @cfg {Roo.data.Store} items
26368      * array of button configs or elements to add
26369      */
26370     
26371     /**
26372      * @cfg {String/HTMLElement/Element} container
26373      * The id or element that will contain the toolbar
26374      */
26375     // private
26376     render : function(ct){
26377         this.el = Roo.get(ct);
26378         if(this.cls){
26379             this.el.addClass(this.cls);
26380         }
26381         // using a table allows for vertical alignment
26382         // 100% width is needed by Safari...
26383         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26384         this.tr = this.el.child("tr", true);
26385         var autoId = 0;
26386         this.items = new Roo.util.MixedCollection(false, function(o){
26387             return o.id || ("item" + (++autoId));
26388         });
26389         if(this.buttons){
26390             this.add.apply(this, this.buttons);
26391             delete this.buttons;
26392         }
26393     },
26394
26395     /**
26396      * Adds element(s) to the toolbar -- this function takes a variable number of 
26397      * arguments of mixed type and adds them to the toolbar.
26398      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26399      * <ul>
26400      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26401      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26402      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26403      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26404      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26405      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26406      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26407      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26408      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26409      * </ul>
26410      * @param {Mixed} arg2
26411      * @param {Mixed} etc.
26412      */
26413     add : function(){
26414         var a = arguments, l = a.length;
26415         for(var i = 0; i < l; i++){
26416             this._add(a[i]);
26417         }
26418     },
26419     // private..
26420     _add : function(el) {
26421         
26422         if (el.xtype) {
26423             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26424         }
26425         
26426         if (el.applyTo){ // some kind of form field
26427             return this.addField(el);
26428         } 
26429         if (el.render){ // some kind of Toolbar.Item
26430             return this.addItem(el);
26431         }
26432         if (typeof el == "string"){ // string
26433             if(el == "separator" || el == "-"){
26434                 return this.addSeparator();
26435             }
26436             if (el == " "){
26437                 return this.addSpacer();
26438             }
26439             if(el == "->"){
26440                 return this.addFill();
26441             }
26442             return this.addText(el);
26443             
26444         }
26445         if(el.tagName){ // element
26446             return this.addElement(el);
26447         }
26448         if(typeof el == "object"){ // must be button config?
26449             return this.addButton(el);
26450         }
26451         // and now what?!?!
26452         return false;
26453         
26454     },
26455     
26456     /**
26457      * Add an Xtype element
26458      * @param {Object} xtype Xtype Object
26459      * @return {Object} created Object
26460      */
26461     addxtype : function(e){
26462         return this.add(e);  
26463     },
26464     
26465     /**
26466      * Returns the Element for this toolbar.
26467      * @return {Roo.Element}
26468      */
26469     getEl : function(){
26470         return this.el;  
26471     },
26472     
26473     /**
26474      * Adds a separator
26475      * @return {Roo.Toolbar.Item} The separator item
26476      */
26477     addSeparator : function(){
26478         return this.addItem(new Roo.Toolbar.Separator());
26479     },
26480
26481     /**
26482      * Adds a spacer element
26483      * @return {Roo.Toolbar.Spacer} The spacer item
26484      */
26485     addSpacer : function(){
26486         return this.addItem(new Roo.Toolbar.Spacer());
26487     },
26488
26489     /**
26490      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26491      * @return {Roo.Toolbar.Fill} The fill item
26492      */
26493     addFill : function(){
26494         return this.addItem(new Roo.Toolbar.Fill());
26495     },
26496
26497     /**
26498      * Adds any standard HTML element to the toolbar
26499      * @param {String/HTMLElement/Element} el The element or id of the element to add
26500      * @return {Roo.Toolbar.Item} The element's item
26501      */
26502     addElement : function(el){
26503         return this.addItem(new Roo.Toolbar.Item(el));
26504     },
26505     /**
26506      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26507      * @type Roo.util.MixedCollection  
26508      */
26509     items : false,
26510      
26511     /**
26512      * Adds any Toolbar.Item or subclass
26513      * @param {Roo.Toolbar.Item} item
26514      * @return {Roo.Toolbar.Item} The item
26515      */
26516     addItem : function(item){
26517         var td = this.nextBlock();
26518         item.render(td);
26519         this.items.add(item);
26520         return item;
26521     },
26522     
26523     /**
26524      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26525      * @param {Object/Array} config A button config or array of configs
26526      * @return {Roo.Toolbar.Button/Array}
26527      */
26528     addButton : function(config){
26529         if(config instanceof Array){
26530             var buttons = [];
26531             for(var i = 0, len = config.length; i < len; i++) {
26532                 buttons.push(this.addButton(config[i]));
26533             }
26534             return buttons;
26535         }
26536         var b = config;
26537         if(!(config instanceof Roo.Toolbar.Button)){
26538             b = config.split ?
26539                 new Roo.Toolbar.SplitButton(config) :
26540                 new Roo.Toolbar.Button(config);
26541         }
26542         var td = this.nextBlock();
26543         b.render(td);
26544         this.items.add(b);
26545         return b;
26546     },
26547     
26548     /**
26549      * Adds text to the toolbar
26550      * @param {String} text The text to add
26551      * @return {Roo.Toolbar.Item} The element's item
26552      */
26553     addText : function(text){
26554         return this.addItem(new Roo.Toolbar.TextItem(text));
26555     },
26556     
26557     /**
26558      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26559      * @param {Number} index The index where the item is to be inserted
26560      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26561      * @return {Roo.Toolbar.Button/Item}
26562      */
26563     insertButton : function(index, item){
26564         if(item instanceof Array){
26565             var buttons = [];
26566             for(var i = 0, len = item.length; i < len; i++) {
26567                buttons.push(this.insertButton(index + i, item[i]));
26568             }
26569             return buttons;
26570         }
26571         if (!(item instanceof Roo.Toolbar.Button)){
26572            item = new Roo.Toolbar.Button(item);
26573         }
26574         var td = document.createElement("td");
26575         this.tr.insertBefore(td, this.tr.childNodes[index]);
26576         item.render(td);
26577         this.items.insert(index, item);
26578         return item;
26579     },
26580     
26581     /**
26582      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26583      * @param {Object} config
26584      * @return {Roo.Toolbar.Item} The element's item
26585      */
26586     addDom : function(config, returnEl){
26587         var td = this.nextBlock();
26588         Roo.DomHelper.overwrite(td, config);
26589         var ti = new Roo.Toolbar.Item(td.firstChild);
26590         ti.render(td);
26591         this.items.add(ti);
26592         return ti;
26593     },
26594
26595     /**
26596      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26597      * @type Roo.util.MixedCollection  
26598      */
26599     fields : false,
26600     
26601     /**
26602      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26603      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26604      * @param {Roo.form.Field} field
26605      * @return {Roo.ToolbarItem}
26606      */
26607      
26608       
26609     addField : function(field) {
26610         if (!this.fields) {
26611             var autoId = 0;
26612             this.fields = new Roo.util.MixedCollection(false, function(o){
26613                 return o.id || ("item" + (++autoId));
26614             });
26615
26616         }
26617         
26618         var td = this.nextBlock();
26619         field.render(td);
26620         var ti = new Roo.Toolbar.Item(td.firstChild);
26621         ti.render(td);
26622         this.items.add(ti);
26623         this.fields.add(field);
26624         return ti;
26625     },
26626     /**
26627      * Hide the toolbar
26628      * @method hide
26629      */
26630      
26631       
26632     hide : function()
26633     {
26634         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26635         this.el.child('div').hide();
26636     },
26637     /**
26638      * Show the toolbar
26639      * @method show
26640      */
26641     show : function()
26642     {
26643         this.el.child('div').show();
26644     },
26645       
26646     // private
26647     nextBlock : function(){
26648         var td = document.createElement("td");
26649         this.tr.appendChild(td);
26650         return td;
26651     },
26652
26653     // private
26654     destroy : function(){
26655         if(this.items){ // rendered?
26656             Roo.destroy.apply(Roo, this.items.items);
26657         }
26658         if(this.fields){ // rendered?
26659             Roo.destroy.apply(Roo, this.fields.items);
26660         }
26661         Roo.Element.uncache(this.el, this.tr);
26662     }
26663 };
26664
26665 /**
26666  * @class Roo.Toolbar.Item
26667  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26668  * @constructor
26669  * Creates a new Item
26670  * @param {HTMLElement} el 
26671  */
26672 Roo.Toolbar.Item = function(el){
26673     this.el = Roo.getDom(el);
26674     this.id = Roo.id(this.el);
26675     this.hidden = false;
26676 };
26677
26678 Roo.Toolbar.Item.prototype = {
26679     
26680     /**
26681      * Get this item's HTML Element
26682      * @return {HTMLElement}
26683      */
26684     getEl : function(){
26685        return this.el;  
26686     },
26687
26688     // private
26689     render : function(td){
26690         this.td = td;
26691         td.appendChild(this.el);
26692     },
26693     
26694     /**
26695      * Removes and destroys this item.
26696      */
26697     destroy : function(){
26698         this.td.parentNode.removeChild(this.td);
26699     },
26700     
26701     /**
26702      * Shows this item.
26703      */
26704     show: function(){
26705         this.hidden = false;
26706         this.td.style.display = "";
26707     },
26708     
26709     /**
26710      * Hides this item.
26711      */
26712     hide: function(){
26713         this.hidden = true;
26714         this.td.style.display = "none";
26715     },
26716     
26717     /**
26718      * Convenience function for boolean show/hide.
26719      * @param {Boolean} visible true to show/false to hide
26720      */
26721     setVisible: function(visible){
26722         if(visible) {
26723             this.show();
26724         }else{
26725             this.hide();
26726         }
26727     },
26728     
26729     /**
26730      * Try to focus this item.
26731      */
26732     focus : function(){
26733         Roo.fly(this.el).focus();
26734     },
26735     
26736     /**
26737      * Disables this item.
26738      */
26739     disable : function(){
26740         Roo.fly(this.td).addClass("x-item-disabled");
26741         this.disabled = true;
26742         this.el.disabled = true;
26743     },
26744     
26745     /**
26746      * Enables this item.
26747      */
26748     enable : function(){
26749         Roo.fly(this.td).removeClass("x-item-disabled");
26750         this.disabled = false;
26751         this.el.disabled = false;
26752     }
26753 };
26754
26755
26756 /**
26757  * @class Roo.Toolbar.Separator
26758  * @extends Roo.Toolbar.Item
26759  * A simple toolbar separator class
26760  * @constructor
26761  * Creates a new Separator
26762  */
26763 Roo.Toolbar.Separator = function(){
26764     var s = document.createElement("span");
26765     s.className = "ytb-sep";
26766     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26767 };
26768 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26769     enable:Roo.emptyFn,
26770     disable:Roo.emptyFn,
26771     focus:Roo.emptyFn
26772 });
26773
26774 /**
26775  * @class Roo.Toolbar.Spacer
26776  * @extends Roo.Toolbar.Item
26777  * A simple element that adds extra horizontal space to a toolbar.
26778  * @constructor
26779  * Creates a new Spacer
26780  */
26781 Roo.Toolbar.Spacer = function(){
26782     var s = document.createElement("div");
26783     s.className = "ytb-spacer";
26784     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26785 };
26786 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26787     enable:Roo.emptyFn,
26788     disable:Roo.emptyFn,
26789     focus:Roo.emptyFn
26790 });
26791
26792 /**
26793  * @class Roo.Toolbar.Fill
26794  * @extends Roo.Toolbar.Spacer
26795  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26796  * @constructor
26797  * Creates a new Spacer
26798  */
26799 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26800     // private
26801     render : function(td){
26802         td.style.width = '100%';
26803         Roo.Toolbar.Fill.superclass.render.call(this, td);
26804     }
26805 });
26806
26807 /**
26808  * @class Roo.Toolbar.TextItem
26809  * @extends Roo.Toolbar.Item
26810  * A simple class that renders text directly into a toolbar.
26811  * @constructor
26812  * Creates a new TextItem
26813  * @param {String} text
26814  */
26815 Roo.Toolbar.TextItem = function(text){
26816     if (typeof(text) == 'object') {
26817         text = text.text;
26818     }
26819     var s = document.createElement("span");
26820     s.className = "ytb-text";
26821     s.innerHTML = text;
26822     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26823 };
26824 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26825     enable:Roo.emptyFn,
26826     disable:Roo.emptyFn,
26827     focus:Roo.emptyFn
26828 });
26829
26830 /**
26831  * @class Roo.Toolbar.Button
26832  * @extends Roo.Button
26833  * A button that renders into a toolbar.
26834  * @constructor
26835  * Creates a new Button
26836  * @param {Object} config A standard {@link Roo.Button} config object
26837  */
26838 Roo.Toolbar.Button = function(config){
26839     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26840 };
26841 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26842     render : function(td){
26843         this.td = td;
26844         Roo.Toolbar.Button.superclass.render.call(this, td);
26845     },
26846     
26847     /**
26848      * Removes and destroys this button
26849      */
26850     destroy : function(){
26851         Roo.Toolbar.Button.superclass.destroy.call(this);
26852         this.td.parentNode.removeChild(this.td);
26853     },
26854     
26855     /**
26856      * Shows this button
26857      */
26858     show: function(){
26859         this.hidden = false;
26860         this.td.style.display = "";
26861     },
26862     
26863     /**
26864      * Hides this button
26865      */
26866     hide: function(){
26867         this.hidden = true;
26868         this.td.style.display = "none";
26869     },
26870
26871     /**
26872      * Disables this item
26873      */
26874     disable : function(){
26875         Roo.fly(this.td).addClass("x-item-disabled");
26876         this.disabled = true;
26877     },
26878
26879     /**
26880      * Enables this item
26881      */
26882     enable : function(){
26883         Roo.fly(this.td).removeClass("x-item-disabled");
26884         this.disabled = false;
26885     }
26886 });
26887 // backwards compat
26888 Roo.ToolbarButton = Roo.Toolbar.Button;
26889
26890 /**
26891  * @class Roo.Toolbar.SplitButton
26892  * @extends Roo.SplitButton
26893  * A menu button that renders into a toolbar.
26894  * @constructor
26895  * Creates a new SplitButton
26896  * @param {Object} config A standard {@link Roo.SplitButton} config object
26897  */
26898 Roo.Toolbar.SplitButton = function(config){
26899     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26900 };
26901 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26902     render : function(td){
26903         this.td = td;
26904         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26905     },
26906     
26907     /**
26908      * Removes and destroys this button
26909      */
26910     destroy : function(){
26911         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26912         this.td.parentNode.removeChild(this.td);
26913     },
26914     
26915     /**
26916      * Shows this button
26917      */
26918     show: function(){
26919         this.hidden = false;
26920         this.td.style.display = "";
26921     },
26922     
26923     /**
26924      * Hides this button
26925      */
26926     hide: function(){
26927         this.hidden = true;
26928         this.td.style.display = "none";
26929     }
26930 });
26931
26932 // backwards compat
26933 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26934  * Based on:
26935  * Ext JS Library 1.1.1
26936  * Copyright(c) 2006-2007, Ext JS, LLC.
26937  *
26938  * Originally Released Under LGPL - original licence link has changed is not relivant.
26939  *
26940  * Fork - LGPL
26941  * <script type="text/javascript">
26942  */
26943  
26944 /**
26945  * @class Roo.PagingToolbar
26946  * @extends Roo.Toolbar
26947  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26948  * @constructor
26949  * Create a new PagingToolbar
26950  * @param {Object} config The config object
26951  */
26952 Roo.PagingToolbar = function(el, ds, config)
26953 {
26954     // old args format still supported... - xtype is prefered..
26955     if (typeof(el) == 'object' && el.xtype) {
26956         // created from xtype...
26957         config = el;
26958         ds = el.dataSource;
26959         el = config.container;
26960     }
26961     
26962     
26963     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26964     this.ds = ds;
26965     this.cursor = 0;
26966     this.renderButtons(this.el);
26967     this.bind(ds);
26968 };
26969
26970 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26971     /**
26972      * @cfg {Roo.data.Store} dataSource
26973      * The underlying data store providing the paged data
26974      */
26975     /**
26976      * @cfg {String/HTMLElement/Element} container
26977      * container The id or element that will contain the toolbar
26978      */
26979     /**
26980      * @cfg {Boolean} displayInfo
26981      * True to display the displayMsg (defaults to false)
26982      */
26983     /**
26984      * @cfg {Number} pageSize
26985      * The number of records to display per page (defaults to 20)
26986      */
26987     pageSize: 20,
26988     /**
26989      * @cfg {String} displayMsg
26990      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26991      */
26992     displayMsg : 'Displaying {0} - {1} of {2}',
26993     /**
26994      * @cfg {String} emptyMsg
26995      * The message to display when no records are found (defaults to "No data to display")
26996      */
26997     emptyMsg : 'No data to display',
26998     /**
26999      * Customizable piece of the default paging text (defaults to "Page")
27000      * @type String
27001      */
27002     beforePageText : "Page",
27003     /**
27004      * Customizable piece of the default paging text (defaults to "of %0")
27005      * @type String
27006      */
27007     afterPageText : "of {0}",
27008     /**
27009      * Customizable piece of the default paging text (defaults to "First Page")
27010      * @type String
27011      */
27012     firstText : "First Page",
27013     /**
27014      * Customizable piece of the default paging text (defaults to "Previous Page")
27015      * @type String
27016      */
27017     prevText : "Previous Page",
27018     /**
27019      * Customizable piece of the default paging text (defaults to "Next Page")
27020      * @type String
27021      */
27022     nextText : "Next Page",
27023     /**
27024      * Customizable piece of the default paging text (defaults to "Last Page")
27025      * @type String
27026      */
27027     lastText : "Last Page",
27028     /**
27029      * Customizable piece of the default paging text (defaults to "Refresh")
27030      * @type String
27031      */
27032     refreshText : "Refresh",
27033
27034     // private
27035     renderButtons : function(el){
27036         Roo.PagingToolbar.superclass.render.call(this, el);
27037         this.first = this.addButton({
27038             tooltip: this.firstText,
27039             cls: "x-btn-icon x-grid-page-first",
27040             disabled: true,
27041             handler: this.onClick.createDelegate(this, ["first"])
27042         });
27043         this.prev = this.addButton({
27044             tooltip: this.prevText,
27045             cls: "x-btn-icon x-grid-page-prev",
27046             disabled: true,
27047             handler: this.onClick.createDelegate(this, ["prev"])
27048         });
27049         this.addSeparator();
27050         this.add(this.beforePageText);
27051         this.field = Roo.get(this.addDom({
27052            tag: "input",
27053            type: "text",
27054            size: "3",
27055            value: "1",
27056            cls: "x-grid-page-number"
27057         }).el);
27058         this.field.on("keydown", this.onPagingKeydown, this);
27059         this.field.on("focus", function(){this.dom.select();});
27060         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27061         this.field.setHeight(18);
27062         this.addSeparator();
27063         this.next = this.addButton({
27064             tooltip: this.nextText,
27065             cls: "x-btn-icon x-grid-page-next",
27066             disabled: true,
27067             handler: this.onClick.createDelegate(this, ["next"])
27068         });
27069         this.last = this.addButton({
27070             tooltip: this.lastText,
27071             cls: "x-btn-icon x-grid-page-last",
27072             disabled: true,
27073             handler: this.onClick.createDelegate(this, ["last"])
27074         });
27075         this.addSeparator();
27076         this.loading = this.addButton({
27077             tooltip: this.refreshText,
27078             cls: "x-btn-icon x-grid-loading",
27079             handler: this.onClick.createDelegate(this, ["refresh"])
27080         });
27081
27082         if(this.displayInfo){
27083             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27084         }
27085     },
27086
27087     // private
27088     updateInfo : function(){
27089         if(this.displayEl){
27090             var count = this.ds.getCount();
27091             var msg = count == 0 ?
27092                 this.emptyMsg :
27093                 String.format(
27094                     this.displayMsg,
27095                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27096                 );
27097             this.displayEl.update(msg);
27098         }
27099     },
27100
27101     // private
27102     onLoad : function(ds, r, o){
27103        this.cursor = o.params ? o.params.start : 0;
27104        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27105
27106        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27107        this.field.dom.value = ap;
27108        this.first.setDisabled(ap == 1);
27109        this.prev.setDisabled(ap == 1);
27110        this.next.setDisabled(ap == ps);
27111        this.last.setDisabled(ap == ps);
27112        this.loading.enable();
27113        this.updateInfo();
27114     },
27115
27116     // private
27117     getPageData : function(){
27118         var total = this.ds.getTotalCount();
27119         return {
27120             total : total,
27121             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27122             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27123         };
27124     },
27125
27126     // private
27127     onLoadError : function(){
27128         this.loading.enable();
27129     },
27130
27131     // private
27132     onPagingKeydown : function(e){
27133         var k = e.getKey();
27134         var d = this.getPageData();
27135         if(k == e.RETURN){
27136             var v = this.field.dom.value, pageNum;
27137             if(!v || isNaN(pageNum = parseInt(v, 10))){
27138                 this.field.dom.value = d.activePage;
27139                 return;
27140             }
27141             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27142             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27143             e.stopEvent();
27144         }
27145         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))
27146         {
27147           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27148           this.field.dom.value = pageNum;
27149           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27150           e.stopEvent();
27151         }
27152         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27153         {
27154           var v = this.field.dom.value, pageNum; 
27155           var increment = (e.shiftKey) ? 10 : 1;
27156           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27157             increment *= -1;
27158           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27159             this.field.dom.value = d.activePage;
27160             return;
27161           }
27162           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27163           {
27164             this.field.dom.value = parseInt(v, 10) + increment;
27165             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27166             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27167           }
27168           e.stopEvent();
27169         }
27170     },
27171
27172     // private
27173     beforeLoad : function(){
27174         if(this.loading){
27175             this.loading.disable();
27176         }
27177     },
27178
27179     // private
27180     onClick : function(which){
27181         var ds = this.ds;
27182         switch(which){
27183             case "first":
27184                 ds.load({params:{start: 0, limit: this.pageSize}});
27185             break;
27186             case "prev":
27187                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27188             break;
27189             case "next":
27190                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27191             break;
27192             case "last":
27193                 var total = ds.getTotalCount();
27194                 var extra = total % this.pageSize;
27195                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27196                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27197             break;
27198             case "refresh":
27199                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27200             break;
27201         }
27202     },
27203
27204     /**
27205      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27206      * @param {Roo.data.Store} store The data store to unbind
27207      */
27208     unbind : function(ds){
27209         ds.un("beforeload", this.beforeLoad, this);
27210         ds.un("load", this.onLoad, this);
27211         ds.un("loadexception", this.onLoadError, this);
27212         ds.un("remove", this.updateInfo, this);
27213         ds.un("add", this.updateInfo, this);
27214         this.ds = undefined;
27215     },
27216
27217     /**
27218      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27219      * @param {Roo.data.Store} store The data store to bind
27220      */
27221     bind : function(ds){
27222         ds.on("beforeload", this.beforeLoad, this);
27223         ds.on("load", this.onLoad, this);
27224         ds.on("loadexception", this.onLoadError, this);
27225         ds.on("remove", this.updateInfo, this);
27226         ds.on("add", this.updateInfo, this);
27227         this.ds = ds;
27228     }
27229 });/*
27230  * Based on:
27231  * Ext JS Library 1.1.1
27232  * Copyright(c) 2006-2007, Ext JS, LLC.
27233  *
27234  * Originally Released Under LGPL - original licence link has changed is not relivant.
27235  *
27236  * Fork - LGPL
27237  * <script type="text/javascript">
27238  */
27239
27240 /**
27241  * @class Roo.Resizable
27242  * @extends Roo.util.Observable
27243  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27244  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27245  * 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
27246  * the element will be wrapped for you automatically.</p>
27247  * <p>Here is the list of valid resize handles:</p>
27248  * <pre>
27249 Value   Description
27250 ------  -------------------
27251  'n'     north
27252  's'     south
27253  'e'     east
27254  'w'     west
27255  'nw'    northwest
27256  'sw'    southwest
27257  'se'    southeast
27258  'ne'    northeast
27259  'all'   all
27260 </pre>
27261  * <p>Here's an example showing the creation of a typical Resizable:</p>
27262  * <pre><code>
27263 var resizer = new Roo.Resizable("element-id", {
27264     handles: 'all',
27265     minWidth: 200,
27266     minHeight: 100,
27267     maxWidth: 500,
27268     maxHeight: 400,
27269     pinned: true
27270 });
27271 resizer.on("resize", myHandler);
27272 </code></pre>
27273  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27274  * resizer.east.setDisplayed(false);</p>
27275  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27276  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27277  * resize operation's new size (defaults to [0, 0])
27278  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27279  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27280  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27281  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27282  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27283  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27284  * @cfg {Number} width The width of the element in pixels (defaults to null)
27285  * @cfg {Number} height The height of the element in pixels (defaults to null)
27286  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27287  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27288  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27289  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27290  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27291  * in favor of the handles config option (defaults to false)
27292  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27293  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27294  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27295  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27296  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27297  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27298  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27299  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27300  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27301  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27302  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27303  * @constructor
27304  * Create a new resizable component
27305  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27306  * @param {Object} config configuration options
27307   */
27308 Roo.Resizable = function(el, config){
27309     this.el = Roo.get(el);
27310
27311     if(config && config.wrap){
27312         config.resizeChild = this.el;
27313         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27314         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27315         this.el.setStyle("overflow", "hidden");
27316         this.el.setPositioning(config.resizeChild.getPositioning());
27317         config.resizeChild.clearPositioning();
27318         if(!config.width || !config.height){
27319             var csize = config.resizeChild.getSize();
27320             this.el.setSize(csize.width, csize.height);
27321         }
27322         if(config.pinned && !config.adjustments){
27323             config.adjustments = "auto";
27324         }
27325     }
27326
27327     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27328     this.proxy.unselectable();
27329     this.proxy.enableDisplayMode('block');
27330
27331     Roo.apply(this, config);
27332
27333     if(this.pinned){
27334         this.disableTrackOver = true;
27335         this.el.addClass("x-resizable-pinned");
27336     }
27337     // if the element isn't positioned, make it relative
27338     var position = this.el.getStyle("position");
27339     if(position != "absolute" && position != "fixed"){
27340         this.el.setStyle("position", "relative");
27341     }
27342     if(!this.handles){ // no handles passed, must be legacy style
27343         this.handles = 's,e,se';
27344         if(this.multiDirectional){
27345             this.handles += ',n,w';
27346         }
27347     }
27348     if(this.handles == "all"){
27349         this.handles = "n s e w ne nw se sw";
27350     }
27351     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27352     var ps = Roo.Resizable.positions;
27353     for(var i = 0, len = hs.length; i < len; i++){
27354         if(hs[i] && ps[hs[i]]){
27355             var pos = ps[hs[i]];
27356             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27357         }
27358     }
27359     // legacy
27360     this.corner = this.southeast;
27361
27362     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27363         this.updateBox = true;
27364     }
27365
27366     this.activeHandle = null;
27367
27368     if(this.resizeChild){
27369         if(typeof this.resizeChild == "boolean"){
27370             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27371         }else{
27372             this.resizeChild = Roo.get(this.resizeChild, true);
27373         }
27374     }
27375
27376     if(this.adjustments == "auto"){
27377         var rc = this.resizeChild;
27378         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27379         if(rc && (hw || hn)){
27380             rc.position("relative");
27381             rc.setLeft(hw ? hw.el.getWidth() : 0);
27382             rc.setTop(hn ? hn.el.getHeight() : 0);
27383         }
27384         this.adjustments = [
27385             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27386             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27387         ];
27388     }
27389
27390     if(this.draggable){
27391         this.dd = this.dynamic ?
27392             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27393         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27394     }
27395
27396     // public events
27397     this.addEvents({
27398         /**
27399          * @event beforeresize
27400          * Fired before resize is allowed. Set enabled to false to cancel resize.
27401          * @param {Roo.Resizable} this
27402          * @param {Roo.EventObject} e The mousedown event
27403          */
27404         "beforeresize" : true,
27405         /**
27406          * @event resize
27407          * Fired after a resize.
27408          * @param {Roo.Resizable} this
27409          * @param {Number} width The new width
27410          * @param {Number} height The new height
27411          * @param {Roo.EventObject} e The mouseup event
27412          */
27413         "resize" : true
27414     });
27415
27416     if(this.width !== null && this.height !== null){
27417         this.resizeTo(this.width, this.height);
27418     }else{
27419         this.updateChildSize();
27420     }
27421     if(Roo.isIE){
27422         this.el.dom.style.zoom = 1;
27423     }
27424     Roo.Resizable.superclass.constructor.call(this);
27425 };
27426
27427 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27428         resizeChild : false,
27429         adjustments : [0, 0],
27430         minWidth : 5,
27431         minHeight : 5,
27432         maxWidth : 10000,
27433         maxHeight : 10000,
27434         enabled : true,
27435         animate : false,
27436         duration : .35,
27437         dynamic : false,
27438         handles : false,
27439         multiDirectional : false,
27440         disableTrackOver : false,
27441         easing : 'easeOutStrong',
27442         widthIncrement : 0,
27443         heightIncrement : 0,
27444         pinned : false,
27445         width : null,
27446         height : null,
27447         preserveRatio : false,
27448         transparent: false,
27449         minX: 0,
27450         minY: 0,
27451         draggable: false,
27452
27453         /**
27454          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27455          */
27456         constrainTo: undefined,
27457         /**
27458          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27459          */
27460         resizeRegion: undefined,
27461
27462
27463     /**
27464      * Perform a manual resize
27465      * @param {Number} width
27466      * @param {Number} height
27467      */
27468     resizeTo : function(width, height){
27469         this.el.setSize(width, height);
27470         this.updateChildSize();
27471         this.fireEvent("resize", this, width, height, null);
27472     },
27473
27474     // private
27475     startSizing : function(e, handle){
27476         this.fireEvent("beforeresize", this, e);
27477         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27478
27479             if(!this.overlay){
27480                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27481                 this.overlay.unselectable();
27482                 this.overlay.enableDisplayMode("block");
27483                 this.overlay.on("mousemove", this.onMouseMove, this);
27484                 this.overlay.on("mouseup", this.onMouseUp, this);
27485             }
27486             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27487
27488             this.resizing = true;
27489             this.startBox = this.el.getBox();
27490             this.startPoint = e.getXY();
27491             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27492                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27493
27494             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27495             this.overlay.show();
27496
27497             if(this.constrainTo) {
27498                 var ct = Roo.get(this.constrainTo);
27499                 this.resizeRegion = ct.getRegion().adjust(
27500                     ct.getFrameWidth('t'),
27501                     ct.getFrameWidth('l'),
27502                     -ct.getFrameWidth('b'),
27503                     -ct.getFrameWidth('r')
27504                 );
27505             }
27506
27507             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27508             this.proxy.show();
27509             this.proxy.setBox(this.startBox);
27510             if(!this.dynamic){
27511                 this.proxy.setStyle('visibility', 'visible');
27512             }
27513         }
27514     },
27515
27516     // private
27517     onMouseDown : function(handle, e){
27518         if(this.enabled){
27519             e.stopEvent();
27520             this.activeHandle = handle;
27521             this.startSizing(e, handle);
27522         }
27523     },
27524
27525     // private
27526     onMouseUp : function(e){
27527         var size = this.resizeElement();
27528         this.resizing = false;
27529         this.handleOut();
27530         this.overlay.hide();
27531         this.proxy.hide();
27532         this.fireEvent("resize", this, size.width, size.height, e);
27533     },
27534
27535     // private
27536     updateChildSize : function(){
27537         if(this.resizeChild){
27538             var el = this.el;
27539             var child = this.resizeChild;
27540             var adj = this.adjustments;
27541             if(el.dom.offsetWidth){
27542                 var b = el.getSize(true);
27543                 child.setSize(b.width+adj[0], b.height+adj[1]);
27544             }
27545             // Second call here for IE
27546             // The first call enables instant resizing and
27547             // the second call corrects scroll bars if they
27548             // exist
27549             if(Roo.isIE){
27550                 setTimeout(function(){
27551                     if(el.dom.offsetWidth){
27552                         var b = el.getSize(true);
27553                         child.setSize(b.width+adj[0], b.height+adj[1]);
27554                     }
27555                 }, 10);
27556             }
27557         }
27558     },
27559
27560     // private
27561     snap : function(value, inc, min){
27562         if(!inc || !value) return value;
27563         var newValue = value;
27564         var m = value % inc;
27565         if(m > 0){
27566             if(m > (inc/2)){
27567                 newValue = value + (inc-m);
27568             }else{
27569                 newValue = value - m;
27570             }
27571         }
27572         return Math.max(min, newValue);
27573     },
27574
27575     // private
27576     resizeElement : function(){
27577         var box = this.proxy.getBox();
27578         if(this.updateBox){
27579             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27580         }else{
27581             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27582         }
27583         this.updateChildSize();
27584         if(!this.dynamic){
27585             this.proxy.hide();
27586         }
27587         return box;
27588     },
27589
27590     // private
27591     constrain : function(v, diff, m, mx){
27592         if(v - diff < m){
27593             diff = v - m;
27594         }else if(v - diff > mx){
27595             diff = mx - v;
27596         }
27597         return diff;
27598     },
27599
27600     // private
27601     onMouseMove : function(e){
27602         if(this.enabled){
27603             try{// try catch so if something goes wrong the user doesn't get hung
27604
27605             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27606                 return;
27607             }
27608
27609             //var curXY = this.startPoint;
27610             var curSize = this.curSize || this.startBox;
27611             var x = this.startBox.x, y = this.startBox.y;
27612             var ox = x, oy = y;
27613             var w = curSize.width, h = curSize.height;
27614             var ow = w, oh = h;
27615             var mw = this.minWidth, mh = this.minHeight;
27616             var mxw = this.maxWidth, mxh = this.maxHeight;
27617             var wi = this.widthIncrement;
27618             var hi = this.heightIncrement;
27619
27620             var eventXY = e.getXY();
27621             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27622             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27623
27624             var pos = this.activeHandle.position;
27625
27626             switch(pos){
27627                 case "east":
27628                     w += diffX;
27629                     w = Math.min(Math.max(mw, w), mxw);
27630                     break;
27631                 case "south":
27632                     h += diffY;
27633                     h = Math.min(Math.max(mh, h), mxh);
27634                     break;
27635                 case "southeast":
27636                     w += diffX;
27637                     h += diffY;
27638                     w = Math.min(Math.max(mw, w), mxw);
27639                     h = Math.min(Math.max(mh, h), mxh);
27640                     break;
27641                 case "north":
27642                     diffY = this.constrain(h, diffY, mh, mxh);
27643                     y += diffY;
27644                     h -= diffY;
27645                     break;
27646                 case "west":
27647                     diffX = this.constrain(w, diffX, mw, mxw);
27648                     x += diffX;
27649                     w -= diffX;
27650                     break;
27651                 case "northeast":
27652                     w += diffX;
27653                     w = Math.min(Math.max(mw, w), mxw);
27654                     diffY = this.constrain(h, diffY, mh, mxh);
27655                     y += diffY;
27656                     h -= diffY;
27657                     break;
27658                 case "northwest":
27659                     diffX = this.constrain(w, diffX, mw, mxw);
27660                     diffY = this.constrain(h, diffY, mh, mxh);
27661                     y += diffY;
27662                     h -= diffY;
27663                     x += diffX;
27664                     w -= diffX;
27665                     break;
27666                case "southwest":
27667                     diffX = this.constrain(w, diffX, mw, mxw);
27668                     h += diffY;
27669                     h = Math.min(Math.max(mh, h), mxh);
27670                     x += diffX;
27671                     w -= diffX;
27672                     break;
27673             }
27674
27675             var sw = this.snap(w, wi, mw);
27676             var sh = this.snap(h, hi, mh);
27677             if(sw != w || sh != h){
27678                 switch(pos){
27679                     case "northeast":
27680                         y -= sh - h;
27681                     break;
27682                     case "north":
27683                         y -= sh - h;
27684                         break;
27685                     case "southwest":
27686                         x -= sw - w;
27687                     break;
27688                     case "west":
27689                         x -= sw - w;
27690                         break;
27691                     case "northwest":
27692                         x -= sw - w;
27693                         y -= sh - h;
27694                     break;
27695                 }
27696                 w = sw;
27697                 h = sh;
27698             }
27699
27700             if(this.preserveRatio){
27701                 switch(pos){
27702                     case "southeast":
27703                     case "east":
27704                         h = oh * (w/ow);
27705                         h = Math.min(Math.max(mh, h), mxh);
27706                         w = ow * (h/oh);
27707                        break;
27708                     case "south":
27709                         w = ow * (h/oh);
27710                         w = Math.min(Math.max(mw, w), mxw);
27711                         h = oh * (w/ow);
27712                         break;
27713                     case "northeast":
27714                         w = ow * (h/oh);
27715                         w = Math.min(Math.max(mw, w), mxw);
27716                         h = oh * (w/ow);
27717                     break;
27718                     case "north":
27719                         var tw = w;
27720                         w = ow * (h/oh);
27721                         w = Math.min(Math.max(mw, w), mxw);
27722                         h = oh * (w/ow);
27723                         x += (tw - w) / 2;
27724                         break;
27725                     case "southwest":
27726                         h = oh * (w/ow);
27727                         h = Math.min(Math.max(mh, h), mxh);
27728                         var tw = w;
27729                         w = ow * (h/oh);
27730                         x += tw - w;
27731                         break;
27732                     case "west":
27733                         var th = h;
27734                         h = oh * (w/ow);
27735                         h = Math.min(Math.max(mh, h), mxh);
27736                         y += (th - h) / 2;
27737                         var tw = w;
27738                         w = ow * (h/oh);
27739                         x += tw - w;
27740                        break;
27741                     case "northwest":
27742                         var tw = w;
27743                         var th = h;
27744                         h = oh * (w/ow);
27745                         h = Math.min(Math.max(mh, h), mxh);
27746                         w = ow * (h/oh);
27747                         y += th - h;
27748                          x += tw - w;
27749                        break;
27750
27751                 }
27752             }
27753             this.proxy.setBounds(x, y, w, h);
27754             if(this.dynamic){
27755                 this.resizeElement();
27756             }
27757             }catch(e){}
27758         }
27759     },
27760
27761     // private
27762     handleOver : function(){
27763         if(this.enabled){
27764             this.el.addClass("x-resizable-over");
27765         }
27766     },
27767
27768     // private
27769     handleOut : function(){
27770         if(!this.resizing){
27771             this.el.removeClass("x-resizable-over");
27772         }
27773     },
27774
27775     /**
27776      * Returns the element this component is bound to.
27777      * @return {Roo.Element}
27778      */
27779     getEl : function(){
27780         return this.el;
27781     },
27782
27783     /**
27784      * Returns the resizeChild element (or null).
27785      * @return {Roo.Element}
27786      */
27787     getResizeChild : function(){
27788         return this.resizeChild;
27789     },
27790
27791     /**
27792      * Destroys this resizable. If the element was wrapped and
27793      * removeEl is not true then the element remains.
27794      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27795      */
27796     destroy : function(removeEl){
27797         this.proxy.remove();
27798         if(this.overlay){
27799             this.overlay.removeAllListeners();
27800             this.overlay.remove();
27801         }
27802         var ps = Roo.Resizable.positions;
27803         for(var k in ps){
27804             if(typeof ps[k] != "function" && this[ps[k]]){
27805                 var h = this[ps[k]];
27806                 h.el.removeAllListeners();
27807                 h.el.remove();
27808             }
27809         }
27810         if(removeEl){
27811             this.el.update("");
27812             this.el.remove();
27813         }
27814     }
27815 });
27816
27817 // private
27818 // hash to map config positions to true positions
27819 Roo.Resizable.positions = {
27820     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27821 };
27822
27823 // private
27824 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27825     if(!this.tpl){
27826         // only initialize the template if resizable is used
27827         var tpl = Roo.DomHelper.createTemplate(
27828             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27829         );
27830         tpl.compile();
27831         Roo.Resizable.Handle.prototype.tpl = tpl;
27832     }
27833     this.position = pos;
27834     this.rz = rz;
27835     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27836     this.el.unselectable();
27837     if(transparent){
27838         this.el.setOpacity(0);
27839     }
27840     this.el.on("mousedown", this.onMouseDown, this);
27841     if(!disableTrackOver){
27842         this.el.on("mouseover", this.onMouseOver, this);
27843         this.el.on("mouseout", this.onMouseOut, this);
27844     }
27845 };
27846
27847 // private
27848 Roo.Resizable.Handle.prototype = {
27849     afterResize : function(rz){
27850         // do nothing
27851     },
27852     // private
27853     onMouseDown : function(e){
27854         this.rz.onMouseDown(this, e);
27855     },
27856     // private
27857     onMouseOver : function(e){
27858         this.rz.handleOver(this, e);
27859     },
27860     // private
27861     onMouseOut : function(e){
27862         this.rz.handleOut(this, e);
27863     }
27864 };/*
27865  * Based on:
27866  * Ext JS Library 1.1.1
27867  * Copyright(c) 2006-2007, Ext JS, LLC.
27868  *
27869  * Originally Released Under LGPL - original licence link has changed is not relivant.
27870  *
27871  * Fork - LGPL
27872  * <script type="text/javascript">
27873  */
27874
27875 /**
27876  * @class Roo.Editor
27877  * @extends Roo.Component
27878  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27879  * @constructor
27880  * Create a new Editor
27881  * @param {Roo.form.Field} field The Field object (or descendant)
27882  * @param {Object} config The config object
27883  */
27884 Roo.Editor = function(field, config){
27885     Roo.Editor.superclass.constructor.call(this, config);
27886     this.field = field;
27887     this.addEvents({
27888         /**
27889              * @event beforestartedit
27890              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27891              * false from the handler of this event.
27892              * @param {Editor} this
27893              * @param {Roo.Element} boundEl The underlying element bound to this editor
27894              * @param {Mixed} value The field value being set
27895              */
27896         "beforestartedit" : true,
27897         /**
27898              * @event startedit
27899              * Fires when this editor is displayed
27900              * @param {Roo.Element} boundEl The underlying element bound to this editor
27901              * @param {Mixed} value The starting field value
27902              */
27903         "startedit" : true,
27904         /**
27905              * @event beforecomplete
27906              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27907              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27908              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27909              * event will not fire since no edit actually occurred.
27910              * @param {Editor} this
27911              * @param {Mixed} value The current field value
27912              * @param {Mixed} startValue The original field value
27913              */
27914         "beforecomplete" : true,
27915         /**
27916              * @event complete
27917              * Fires after editing is complete and any changed value has been written to the underlying field.
27918              * @param {Editor} this
27919              * @param {Mixed} value The current field value
27920              * @param {Mixed} startValue The original field value
27921              */
27922         "complete" : true,
27923         /**
27924          * @event specialkey
27925          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27926          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27927          * @param {Roo.form.Field} this
27928          * @param {Roo.EventObject} e The event object
27929          */
27930         "specialkey" : true
27931     });
27932 };
27933
27934 Roo.extend(Roo.Editor, Roo.Component, {
27935     /**
27936      * @cfg {Boolean/String} autosize
27937      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27938      * or "height" to adopt the height only (defaults to false)
27939      */
27940     /**
27941      * @cfg {Boolean} revertInvalid
27942      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27943      * validation fails (defaults to true)
27944      */
27945     /**
27946      * @cfg {Boolean} ignoreNoChange
27947      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27948      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27949      * will never be ignored.
27950      */
27951     /**
27952      * @cfg {Boolean} hideEl
27953      * False to keep the bound element visible while the editor is displayed (defaults to true)
27954      */
27955     /**
27956      * @cfg {Mixed} value
27957      * The data value of the underlying field (defaults to "")
27958      */
27959     value : "",
27960     /**
27961      * @cfg {String} alignment
27962      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27963      */
27964     alignment: "c-c?",
27965     /**
27966      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27967      * for bottom-right shadow (defaults to "frame")
27968      */
27969     shadow : "frame",
27970     /**
27971      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27972      */
27973     constrain : false,
27974     /**
27975      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27976      */
27977     completeOnEnter : false,
27978     /**
27979      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27980      */
27981     cancelOnEsc : false,
27982     /**
27983      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27984      */
27985     updateEl : false,
27986
27987     // private
27988     onRender : function(ct, position){
27989         this.el = new Roo.Layer({
27990             shadow: this.shadow,
27991             cls: "x-editor",
27992             parentEl : ct,
27993             shim : this.shim,
27994             shadowOffset:4,
27995             id: this.id,
27996             constrain: this.constrain
27997         });
27998         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
27999         if(this.field.msgTarget != 'title'){
28000             this.field.msgTarget = 'qtip';
28001         }
28002         this.field.render(this.el);
28003         if(Roo.isGecko){
28004             this.field.el.dom.setAttribute('autocomplete', 'off');
28005         }
28006         this.field.on("specialkey", this.onSpecialKey, this);
28007         if(this.swallowKeys){
28008             this.field.el.swallowEvent(['keydown','keypress']);
28009         }
28010         this.field.show();
28011         this.field.on("blur", this.onBlur, this);
28012         if(this.field.grow){
28013             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28014         }
28015     },
28016
28017     onSpecialKey : function(field, e){
28018         if(this.completeOnEnter && e.getKey() == e.ENTER){
28019             e.stopEvent();
28020             this.completeEdit();
28021         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28022             this.cancelEdit();
28023         }else{
28024             this.fireEvent('specialkey', field, e);
28025         }
28026     },
28027
28028     /**
28029      * Starts the editing process and shows the editor.
28030      * @param {String/HTMLElement/Element} el The element to edit
28031      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28032       * to the innerHTML of el.
28033      */
28034     startEdit : function(el, value){
28035         if(this.editing){
28036             this.completeEdit();
28037         }
28038         this.boundEl = Roo.get(el);
28039         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28040         if(!this.rendered){
28041             this.render(this.parentEl || document.body);
28042         }
28043         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28044             return;
28045         }
28046         this.startValue = v;
28047         this.field.setValue(v);
28048         if(this.autoSize){
28049             var sz = this.boundEl.getSize();
28050             switch(this.autoSize){
28051                 case "width":
28052                 this.setSize(sz.width,  "");
28053                 break;
28054                 case "height":
28055                 this.setSize("",  sz.height);
28056                 break;
28057                 default:
28058                 this.setSize(sz.width,  sz.height);
28059             }
28060         }
28061         this.el.alignTo(this.boundEl, this.alignment);
28062         this.editing = true;
28063         if(Roo.QuickTips){
28064             Roo.QuickTips.disable();
28065         }
28066         this.show();
28067     },
28068
28069     /**
28070      * Sets the height and width of this editor.
28071      * @param {Number} width The new width
28072      * @param {Number} height The new height
28073      */
28074     setSize : function(w, h){
28075         this.field.setSize(w, h);
28076         if(this.el){
28077             this.el.sync();
28078         }
28079     },
28080
28081     /**
28082      * Realigns the editor to the bound field based on the current alignment config value.
28083      */
28084     realign : function(){
28085         this.el.alignTo(this.boundEl, this.alignment);
28086     },
28087
28088     /**
28089      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28090      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28091      */
28092     completeEdit : function(remainVisible){
28093         if(!this.editing){
28094             return;
28095         }
28096         var v = this.getValue();
28097         if(this.revertInvalid !== false && !this.field.isValid()){
28098             v = this.startValue;
28099             this.cancelEdit(true);
28100         }
28101         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28102             this.editing = false;
28103             this.hide();
28104             return;
28105         }
28106         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28107             this.editing = false;
28108             if(this.updateEl && this.boundEl){
28109                 this.boundEl.update(v);
28110             }
28111             if(remainVisible !== true){
28112                 this.hide();
28113             }
28114             this.fireEvent("complete", this, v, this.startValue);
28115         }
28116     },
28117
28118     // private
28119     onShow : function(){
28120         this.el.show();
28121         if(this.hideEl !== false){
28122             this.boundEl.hide();
28123         }
28124         this.field.show();
28125         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28126             this.fixIEFocus = true;
28127             this.deferredFocus.defer(50, this);
28128         }else{
28129             this.field.focus();
28130         }
28131         this.fireEvent("startedit", this.boundEl, this.startValue);
28132     },
28133
28134     deferredFocus : function(){
28135         if(this.editing){
28136             this.field.focus();
28137         }
28138     },
28139
28140     /**
28141      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28142      * reverted to the original starting value.
28143      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28144      * cancel (defaults to false)
28145      */
28146     cancelEdit : function(remainVisible){
28147         if(this.editing){
28148             this.setValue(this.startValue);
28149             if(remainVisible !== true){
28150                 this.hide();
28151             }
28152         }
28153     },
28154
28155     // private
28156     onBlur : function(){
28157         if(this.allowBlur !== true && this.editing){
28158             this.completeEdit();
28159         }
28160     },
28161
28162     // private
28163     onHide : function(){
28164         if(this.editing){
28165             this.completeEdit();
28166             return;
28167         }
28168         this.field.blur();
28169         if(this.field.collapse){
28170             this.field.collapse();
28171         }
28172         this.el.hide();
28173         if(this.hideEl !== false){
28174             this.boundEl.show();
28175         }
28176         if(Roo.QuickTips){
28177             Roo.QuickTips.enable();
28178         }
28179     },
28180
28181     /**
28182      * Sets the data value of the editor
28183      * @param {Mixed} value Any valid value supported by the underlying field
28184      */
28185     setValue : function(v){
28186         this.field.setValue(v);
28187     },
28188
28189     /**
28190      * Gets the data value of the editor
28191      * @return {Mixed} The data value
28192      */
28193     getValue : function(){
28194         return this.field.getValue();
28195     }
28196 });/*
28197  * Based on:
28198  * Ext JS Library 1.1.1
28199  * Copyright(c) 2006-2007, Ext JS, LLC.
28200  *
28201  * Originally Released Under LGPL - original licence link has changed is not relivant.
28202  *
28203  * Fork - LGPL
28204  * <script type="text/javascript">
28205  */
28206  
28207 /**
28208  * @class Roo.BasicDialog
28209  * @extends Roo.util.Observable
28210  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28211  * <pre><code>
28212 var dlg = new Roo.BasicDialog("my-dlg", {
28213     height: 200,
28214     width: 300,
28215     minHeight: 100,
28216     minWidth: 150,
28217     modal: true,
28218     proxyDrag: true,
28219     shadow: true
28220 });
28221 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28222 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28223 dlg.addButton('Cancel', dlg.hide, dlg);
28224 dlg.show();
28225 </code></pre>
28226   <b>A Dialog should always be a direct child of the body element.</b>
28227  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28228  * @cfg {String} title Default text to display in the title bar (defaults to null)
28229  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28230  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28231  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28232  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28233  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28234  * (defaults to null with no animation)
28235  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28236  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28237  * property for valid values (defaults to 'all')
28238  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28239  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28240  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28241  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28242  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28243  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28244  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28245  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28246  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28247  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28248  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28249  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28250  * draggable = true (defaults to false)
28251  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28252  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28253  * shadow (defaults to false)
28254  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28255  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28256  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28257  * @cfg {Array} buttons Array of buttons
28258  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28259  * @constructor
28260  * Create a new BasicDialog.
28261  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28262  * @param {Object} config Configuration options
28263  */
28264 Roo.BasicDialog = function(el, config){
28265     this.el = Roo.get(el);
28266     var dh = Roo.DomHelper;
28267     if(!this.el && config && config.autoCreate){
28268         if(typeof config.autoCreate == "object"){
28269             if(!config.autoCreate.id){
28270                 config.autoCreate.id = el;
28271             }
28272             this.el = dh.append(document.body,
28273                         config.autoCreate, true);
28274         }else{
28275             this.el = dh.append(document.body,
28276                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28277         }
28278     }
28279     el = this.el;
28280     el.setDisplayed(true);
28281     el.hide = this.hideAction;
28282     this.id = el.id;
28283     el.addClass("x-dlg");
28284
28285     Roo.apply(this, config);
28286
28287     this.proxy = el.createProxy("x-dlg-proxy");
28288     this.proxy.hide = this.hideAction;
28289     this.proxy.setOpacity(.5);
28290     this.proxy.hide();
28291
28292     if(config.width){
28293         el.setWidth(config.width);
28294     }
28295     if(config.height){
28296         el.setHeight(config.height);
28297     }
28298     this.size = el.getSize();
28299     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28300         this.xy = [config.x,config.y];
28301     }else{
28302         this.xy = el.getCenterXY(true);
28303     }
28304     /** The header element @type Roo.Element */
28305     this.header = el.child("> .x-dlg-hd");
28306     /** The body element @type Roo.Element */
28307     this.body = el.child("> .x-dlg-bd");
28308     /** The footer element @type Roo.Element */
28309     this.footer = el.child("> .x-dlg-ft");
28310
28311     if(!this.header){
28312         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28313     }
28314     if(!this.body){
28315         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28316     }
28317
28318     this.header.unselectable();
28319     if(this.title){
28320         this.header.update(this.title);
28321     }
28322     // this element allows the dialog to be focused for keyboard event
28323     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28324     this.focusEl.swallowEvent("click", true);
28325
28326     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28327
28328     // wrap the body and footer for special rendering
28329     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28330     if(this.footer){
28331         this.bwrap.dom.appendChild(this.footer.dom);
28332     }
28333
28334     this.bg = this.el.createChild({
28335         tag: "div", cls:"x-dlg-bg",
28336         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28337     });
28338     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28339
28340
28341     if(this.autoScroll !== false && !this.autoTabs){
28342         this.body.setStyle("overflow", "auto");
28343     }
28344
28345     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28346
28347     if(this.closable !== false){
28348         this.el.addClass("x-dlg-closable");
28349         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28350         this.close.on("click", this.closeClick, this);
28351         this.close.addClassOnOver("x-dlg-close-over");
28352     }
28353     if(this.collapsible !== false){
28354         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28355         this.collapseBtn.on("click", this.collapseClick, this);
28356         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28357         this.header.on("dblclick", this.collapseClick, this);
28358     }
28359     if(this.resizable !== false){
28360         this.el.addClass("x-dlg-resizable");
28361         this.resizer = new Roo.Resizable(el, {
28362             minWidth: this.minWidth || 80,
28363             minHeight:this.minHeight || 80,
28364             handles: this.resizeHandles || "all",
28365             pinned: true
28366         });
28367         this.resizer.on("beforeresize", this.beforeResize, this);
28368         this.resizer.on("resize", this.onResize, this);
28369     }
28370     if(this.draggable !== false){
28371         el.addClass("x-dlg-draggable");
28372         if (!this.proxyDrag) {
28373             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28374         }
28375         else {
28376             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28377         }
28378         dd.setHandleElId(this.header.id);
28379         dd.endDrag = this.endMove.createDelegate(this);
28380         dd.startDrag = this.startMove.createDelegate(this);
28381         dd.onDrag = this.onDrag.createDelegate(this);
28382         dd.scroll = false;
28383         this.dd = dd;
28384     }
28385     if(this.modal){
28386         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28387         this.mask.enableDisplayMode("block");
28388         this.mask.hide();
28389         this.el.addClass("x-dlg-modal");
28390     }
28391     if(this.shadow){
28392         this.shadow = new Roo.Shadow({
28393             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28394             offset : this.shadowOffset
28395         });
28396     }else{
28397         this.shadowOffset = 0;
28398     }
28399     if(Roo.useShims && this.shim !== false){
28400         this.shim = this.el.createShim();
28401         this.shim.hide = this.hideAction;
28402         this.shim.hide();
28403     }else{
28404         this.shim = false;
28405     }
28406     if(this.autoTabs){
28407         this.initTabs();
28408     }
28409     if (this.buttons) { 
28410         var bts= this.buttons;
28411         this.buttons = [];
28412         Roo.each(bts, function(b) {
28413             this.addButton(b);
28414         }, this);
28415     }
28416     
28417     
28418     this.addEvents({
28419         /**
28420          * @event keydown
28421          * Fires when a key is pressed
28422          * @param {Roo.BasicDialog} this
28423          * @param {Roo.EventObject} e
28424          */
28425         "keydown" : true,
28426         /**
28427          * @event move
28428          * Fires when this dialog is moved by the user.
28429          * @param {Roo.BasicDialog} this
28430          * @param {Number} x The new page X
28431          * @param {Number} y The new page Y
28432          */
28433         "move" : true,
28434         /**
28435          * @event resize
28436          * Fires when this dialog is resized by the user.
28437          * @param {Roo.BasicDialog} this
28438          * @param {Number} width The new width
28439          * @param {Number} height The new height
28440          */
28441         "resize" : true,
28442         /**
28443          * @event beforehide
28444          * Fires before this dialog is hidden.
28445          * @param {Roo.BasicDialog} this
28446          */
28447         "beforehide" : true,
28448         /**
28449          * @event hide
28450          * Fires when this dialog is hidden.
28451          * @param {Roo.BasicDialog} this
28452          */
28453         "hide" : true,
28454         /**
28455          * @event beforeshow
28456          * Fires before this dialog is shown.
28457          * @param {Roo.BasicDialog} this
28458          */
28459         "beforeshow" : true,
28460         /**
28461          * @event show
28462          * Fires when this dialog is shown.
28463          * @param {Roo.BasicDialog} this
28464          */
28465         "show" : true
28466     });
28467     el.on("keydown", this.onKeyDown, this);
28468     el.on("mousedown", this.toFront, this);
28469     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28470     this.el.hide();
28471     Roo.DialogManager.register(this);
28472     Roo.BasicDialog.superclass.constructor.call(this);
28473 };
28474
28475 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28476     shadowOffset: Roo.isIE ? 6 : 5,
28477     minHeight: 80,
28478     minWidth: 200,
28479     minButtonWidth: 75,
28480     defaultButton: null,
28481     buttonAlign: "right",
28482     tabTag: 'div',
28483     firstShow: true,
28484
28485     /**
28486      * Sets the dialog title text
28487      * @param {String} text The title text to display
28488      * @return {Roo.BasicDialog} this
28489      */
28490     setTitle : function(text){
28491         this.header.update(text);
28492         return this;
28493     },
28494
28495     // private
28496     closeClick : function(){
28497         this.hide();
28498     },
28499
28500     // private
28501     collapseClick : function(){
28502         this[this.collapsed ? "expand" : "collapse"]();
28503     },
28504
28505     /**
28506      * Collapses the dialog to its minimized state (only the title bar is visible).
28507      * Equivalent to the user clicking the collapse dialog button.
28508      */
28509     collapse : function(){
28510         if(!this.collapsed){
28511             this.collapsed = true;
28512             this.el.addClass("x-dlg-collapsed");
28513             this.restoreHeight = this.el.getHeight();
28514             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28515         }
28516     },
28517
28518     /**
28519      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28520      * clicking the expand dialog button.
28521      */
28522     expand : function(){
28523         if(this.collapsed){
28524             this.collapsed = false;
28525             this.el.removeClass("x-dlg-collapsed");
28526             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28527         }
28528     },
28529
28530     /**
28531      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28532      * @return {Roo.TabPanel} The tabs component
28533      */
28534     initTabs : function(){
28535         var tabs = this.getTabs();
28536         while(tabs.getTab(0)){
28537             tabs.removeTab(0);
28538         }
28539         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28540             var dom = el.dom;
28541             tabs.addTab(Roo.id(dom), dom.title);
28542             dom.title = "";
28543         });
28544         tabs.activate(0);
28545         return tabs;
28546     },
28547
28548     // private
28549     beforeResize : function(){
28550         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28551     },
28552
28553     // private
28554     onResize : function(){
28555         this.refreshSize();
28556         this.syncBodyHeight();
28557         this.adjustAssets();
28558         this.focus();
28559         this.fireEvent("resize", this, this.size.width, this.size.height);
28560     },
28561
28562     // private
28563     onKeyDown : function(e){
28564         if(this.isVisible()){
28565             this.fireEvent("keydown", this, e);
28566         }
28567     },
28568
28569     /**
28570      * Resizes the dialog.
28571      * @param {Number} width
28572      * @param {Number} height
28573      * @return {Roo.BasicDialog} this
28574      */
28575     resizeTo : function(width, height){
28576         this.el.setSize(width, height);
28577         this.size = {width: width, height: height};
28578         this.syncBodyHeight();
28579         if(this.fixedcenter){
28580             this.center();
28581         }
28582         if(this.isVisible()){
28583             this.constrainXY();
28584             this.adjustAssets();
28585         }
28586         this.fireEvent("resize", this, width, height);
28587         return this;
28588     },
28589
28590
28591     /**
28592      * Resizes the dialog to fit the specified content size.
28593      * @param {Number} width
28594      * @param {Number} height
28595      * @return {Roo.BasicDialog} this
28596      */
28597     setContentSize : function(w, h){
28598         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28599         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28600         //if(!this.el.isBorderBox()){
28601             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28602             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28603         //}
28604         if(this.tabs){
28605             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28606             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28607         }
28608         this.resizeTo(w, h);
28609         return this;
28610     },
28611
28612     /**
28613      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28614      * executed in response to a particular key being pressed while the dialog is active.
28615      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28616      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28617      * @param {Function} fn The function to call
28618      * @param {Object} scope (optional) The scope of the function
28619      * @return {Roo.BasicDialog} this
28620      */
28621     addKeyListener : function(key, fn, scope){
28622         var keyCode, shift, ctrl, alt;
28623         if(typeof key == "object" && !(key instanceof Array)){
28624             keyCode = key["key"];
28625             shift = key["shift"];
28626             ctrl = key["ctrl"];
28627             alt = key["alt"];
28628         }else{
28629             keyCode = key;
28630         }
28631         var handler = function(dlg, e){
28632             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28633                 var k = e.getKey();
28634                 if(keyCode instanceof Array){
28635                     for(var i = 0, len = keyCode.length; i < len; i++){
28636                         if(keyCode[i] == k){
28637                           fn.call(scope || window, dlg, k, e);
28638                           return;
28639                         }
28640                     }
28641                 }else{
28642                     if(k == keyCode){
28643                         fn.call(scope || window, dlg, k, e);
28644                     }
28645                 }
28646             }
28647         };
28648         this.on("keydown", handler);
28649         return this;
28650     },
28651
28652     /**
28653      * Returns the TabPanel component (creates it if it doesn't exist).
28654      * Note: If you wish to simply check for the existence of tabs without creating them,
28655      * check for a null 'tabs' property.
28656      * @return {Roo.TabPanel} The tabs component
28657      */
28658     getTabs : function(){
28659         if(!this.tabs){
28660             this.el.addClass("x-dlg-auto-tabs");
28661             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28662             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28663         }
28664         return this.tabs;
28665     },
28666
28667     /**
28668      * Adds a button to the footer section of the dialog.
28669      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28670      * object or a valid Roo.DomHelper element config
28671      * @param {Function} handler The function called when the button is clicked
28672      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28673      * @return {Roo.Button} The new button
28674      */
28675     addButton : function(config, handler, scope){
28676         var dh = Roo.DomHelper;
28677         if(!this.footer){
28678             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28679         }
28680         if(!this.btnContainer){
28681             var tb = this.footer.createChild({
28682
28683                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28684                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28685             }, null, true);
28686             this.btnContainer = tb.firstChild.firstChild.firstChild;
28687         }
28688         var bconfig = {
28689             handler: handler,
28690             scope: scope,
28691             minWidth: this.minButtonWidth,
28692             hideParent:true
28693         };
28694         if(typeof config == "string"){
28695             bconfig.text = config;
28696         }else{
28697             if(config.tag){
28698                 bconfig.dhconfig = config;
28699             }else{
28700                 Roo.apply(bconfig, config);
28701             }
28702         }
28703         var fc = false;
28704         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28705             bconfig.position = Math.max(0, bconfig.position);
28706             fc = this.btnContainer.childNodes[bconfig.position];
28707         }
28708          
28709         var btn = new Roo.Button(
28710             fc ? 
28711                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28712                 : this.btnContainer.appendChild(document.createElement("td")),
28713             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28714             bconfig
28715         );
28716         this.syncBodyHeight();
28717         if(!this.buttons){
28718             /**
28719              * Array of all the buttons that have been added to this dialog via addButton
28720              * @type Array
28721              */
28722             this.buttons = [];
28723         }
28724         this.buttons.push(btn);
28725         return btn;
28726     },
28727
28728     /**
28729      * Sets the default button to be focused when the dialog is displayed.
28730      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28731      * @return {Roo.BasicDialog} this
28732      */
28733     setDefaultButton : function(btn){
28734         this.defaultButton = btn;
28735         return this;
28736     },
28737
28738     // private
28739     getHeaderFooterHeight : function(safe){
28740         var height = 0;
28741         if(this.header){
28742            height += this.header.getHeight();
28743         }
28744         if(this.footer){
28745            var fm = this.footer.getMargins();
28746             height += (this.footer.getHeight()+fm.top+fm.bottom);
28747         }
28748         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28749         height += this.centerBg.getPadding("tb");
28750         return height;
28751     },
28752
28753     // private
28754     syncBodyHeight : function(){
28755         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28756         var height = this.size.height - this.getHeaderFooterHeight(false);
28757         bd.setHeight(height-bd.getMargins("tb"));
28758         var hh = this.header.getHeight();
28759         var h = this.size.height-hh;
28760         cb.setHeight(h);
28761         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28762         bw.setHeight(h-cb.getPadding("tb"));
28763         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28764         bd.setWidth(bw.getWidth(true));
28765         if(this.tabs){
28766             this.tabs.syncHeight();
28767             if(Roo.isIE){
28768                 this.tabs.el.repaint();
28769             }
28770         }
28771     },
28772
28773     /**
28774      * Restores the previous state of the dialog if Roo.state is configured.
28775      * @return {Roo.BasicDialog} this
28776      */
28777     restoreState : function(){
28778         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28779         if(box && box.width){
28780             this.xy = [box.x, box.y];
28781             this.resizeTo(box.width, box.height);
28782         }
28783         return this;
28784     },
28785
28786     // private
28787     beforeShow : function(){
28788         this.expand();
28789         if(this.fixedcenter){
28790             this.xy = this.el.getCenterXY(true);
28791         }
28792         if(this.modal){
28793             Roo.get(document.body).addClass("x-body-masked");
28794             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28795             this.mask.show();
28796         }
28797         this.constrainXY();
28798     },
28799
28800     // private
28801     animShow : function(){
28802         var b = Roo.get(this.animateTarget, true).getBox();
28803         this.proxy.setSize(b.width, b.height);
28804         this.proxy.setLocation(b.x, b.y);
28805         this.proxy.show();
28806         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28807                     true, .35, this.showEl.createDelegate(this));
28808     },
28809
28810     /**
28811      * Shows the dialog.
28812      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28813      * @return {Roo.BasicDialog} this
28814      */
28815     show : function(animateTarget){
28816         if (this.fireEvent("beforeshow", this) === false){
28817             return;
28818         }
28819         if(this.syncHeightBeforeShow){
28820             this.syncBodyHeight();
28821         }else if(this.firstShow){
28822             this.firstShow = false;
28823             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28824         }
28825         this.animateTarget = animateTarget || this.animateTarget;
28826         if(!this.el.isVisible()){
28827             this.beforeShow();
28828             if(this.animateTarget){
28829                 this.animShow();
28830             }else{
28831                 this.showEl();
28832             }
28833         }
28834         return this;
28835     },
28836
28837     // private
28838     showEl : function(){
28839         this.proxy.hide();
28840         this.el.setXY(this.xy);
28841         this.el.show();
28842         this.adjustAssets(true);
28843         this.toFront();
28844         this.focus();
28845         // IE peekaboo bug - fix found by Dave Fenwick
28846         if(Roo.isIE){
28847             this.el.repaint();
28848         }
28849         this.fireEvent("show", this);
28850     },
28851
28852     /**
28853      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28854      * dialog itself will receive focus.
28855      */
28856     focus : function(){
28857         if(this.defaultButton){
28858             this.defaultButton.focus();
28859         }else{
28860             this.focusEl.focus();
28861         }
28862     },
28863
28864     // private
28865     constrainXY : function(){
28866         if(this.constraintoviewport !== false){
28867             if(!this.viewSize){
28868                 if(this.container){
28869                     var s = this.container.getSize();
28870                     this.viewSize = [s.width, s.height];
28871                 }else{
28872                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28873                 }
28874             }
28875             var s = Roo.get(this.container||document).getScroll();
28876
28877             var x = this.xy[0], y = this.xy[1];
28878             var w = this.size.width, h = this.size.height;
28879             var vw = this.viewSize[0], vh = this.viewSize[1];
28880             // only move it if it needs it
28881             var moved = false;
28882             // first validate right/bottom
28883             if(x + w > vw+s.left){
28884                 x = vw - w;
28885                 moved = true;
28886             }
28887             if(y + h > vh+s.top){
28888                 y = vh - h;
28889                 moved = true;
28890             }
28891             // then make sure top/left isn't negative
28892             if(x < s.left){
28893                 x = s.left;
28894                 moved = true;
28895             }
28896             if(y < s.top){
28897                 y = s.top;
28898                 moved = true;
28899             }
28900             if(moved){
28901                 // cache xy
28902                 this.xy = [x, y];
28903                 if(this.isVisible()){
28904                     this.el.setLocation(x, y);
28905                     this.adjustAssets();
28906                 }
28907             }
28908         }
28909     },
28910
28911     // private
28912     onDrag : function(){
28913         if(!this.proxyDrag){
28914             this.xy = this.el.getXY();
28915             this.adjustAssets();
28916         }
28917     },
28918
28919     // private
28920     adjustAssets : function(doShow){
28921         var x = this.xy[0], y = this.xy[1];
28922         var w = this.size.width, h = this.size.height;
28923         if(doShow === true){
28924             if(this.shadow){
28925                 this.shadow.show(this.el);
28926             }
28927             if(this.shim){
28928                 this.shim.show();
28929             }
28930         }
28931         if(this.shadow && this.shadow.isVisible()){
28932             this.shadow.show(this.el);
28933         }
28934         if(this.shim && this.shim.isVisible()){
28935             this.shim.setBounds(x, y, w, h);
28936         }
28937     },
28938
28939     // private
28940     adjustViewport : function(w, h){
28941         if(!w || !h){
28942             w = Roo.lib.Dom.getViewWidth();
28943             h = Roo.lib.Dom.getViewHeight();
28944         }
28945         // cache the size
28946         this.viewSize = [w, h];
28947         if(this.modal && this.mask.isVisible()){
28948             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28949             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28950         }
28951         if(this.isVisible()){
28952             this.constrainXY();
28953         }
28954     },
28955
28956     /**
28957      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28958      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28959      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28960      */
28961     destroy : function(removeEl){
28962         if(this.isVisible()){
28963             this.animateTarget = null;
28964             this.hide();
28965         }
28966         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28967         if(this.tabs){
28968             this.tabs.destroy(removeEl);
28969         }
28970         Roo.destroy(
28971              this.shim,
28972              this.proxy,
28973              this.resizer,
28974              this.close,
28975              this.mask
28976         );
28977         if(this.dd){
28978             this.dd.unreg();
28979         }
28980         if(this.buttons){
28981            for(var i = 0, len = this.buttons.length; i < len; i++){
28982                this.buttons[i].destroy();
28983            }
28984         }
28985         this.el.removeAllListeners();
28986         if(removeEl === true){
28987             this.el.update("");
28988             this.el.remove();
28989         }
28990         Roo.DialogManager.unregister(this);
28991     },
28992
28993     // private
28994     startMove : function(){
28995         if(this.proxyDrag){
28996             this.proxy.show();
28997         }
28998         if(this.constraintoviewport !== false){
28999             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29000         }
29001     },
29002
29003     // private
29004     endMove : function(){
29005         if(!this.proxyDrag){
29006             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29007         }else{
29008             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29009             this.proxy.hide();
29010         }
29011         this.refreshSize();
29012         this.adjustAssets();
29013         this.focus();
29014         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29015     },
29016
29017     /**
29018      * Brings this dialog to the front of any other visible dialogs
29019      * @return {Roo.BasicDialog} this
29020      */
29021     toFront : function(){
29022         Roo.DialogManager.bringToFront(this);
29023         return this;
29024     },
29025
29026     /**
29027      * Sends this dialog to the back (under) of any other visible dialogs
29028      * @return {Roo.BasicDialog} this
29029      */
29030     toBack : function(){
29031         Roo.DialogManager.sendToBack(this);
29032         return this;
29033     },
29034
29035     /**
29036      * Centers this dialog in the viewport
29037      * @return {Roo.BasicDialog} this
29038      */
29039     center : function(){
29040         var xy = this.el.getCenterXY(true);
29041         this.moveTo(xy[0], xy[1]);
29042         return this;
29043     },
29044
29045     /**
29046      * Moves the dialog's top-left corner to the specified point
29047      * @param {Number} x
29048      * @param {Number} y
29049      * @return {Roo.BasicDialog} this
29050      */
29051     moveTo : function(x, y){
29052         this.xy = [x,y];
29053         if(this.isVisible()){
29054             this.el.setXY(this.xy);
29055             this.adjustAssets();
29056         }
29057         return this;
29058     },
29059
29060     /**
29061      * Aligns the dialog to the specified element
29062      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29063      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29064      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29065      * @return {Roo.BasicDialog} this
29066      */
29067     alignTo : function(element, position, offsets){
29068         this.xy = this.el.getAlignToXY(element, position, offsets);
29069         if(this.isVisible()){
29070             this.el.setXY(this.xy);
29071             this.adjustAssets();
29072         }
29073         return this;
29074     },
29075
29076     /**
29077      * Anchors an element to another element and realigns it when the window is resized.
29078      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29079      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29080      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29081      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29082      * is a number, it is used as the buffer delay (defaults to 50ms).
29083      * @return {Roo.BasicDialog} this
29084      */
29085     anchorTo : function(el, alignment, offsets, monitorScroll){
29086         var action = function(){
29087             this.alignTo(el, alignment, offsets);
29088         };
29089         Roo.EventManager.onWindowResize(action, this);
29090         var tm = typeof monitorScroll;
29091         if(tm != 'undefined'){
29092             Roo.EventManager.on(window, 'scroll', action, this,
29093                 {buffer: tm == 'number' ? monitorScroll : 50});
29094         }
29095         action.call(this);
29096         return this;
29097     },
29098
29099     /**
29100      * Returns true if the dialog is visible
29101      * @return {Boolean}
29102      */
29103     isVisible : function(){
29104         return this.el.isVisible();
29105     },
29106
29107     // private
29108     animHide : function(callback){
29109         var b = Roo.get(this.animateTarget).getBox();
29110         this.proxy.show();
29111         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29112         this.el.hide();
29113         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29114                     this.hideEl.createDelegate(this, [callback]));
29115     },
29116
29117     /**
29118      * Hides the dialog.
29119      * @param {Function} callback (optional) Function to call when the dialog is hidden
29120      * @return {Roo.BasicDialog} this
29121      */
29122     hide : function(callback){
29123         if (this.fireEvent("beforehide", this) === false){
29124             return;
29125         }
29126         if(this.shadow){
29127             this.shadow.hide();
29128         }
29129         if(this.shim) {
29130           this.shim.hide();
29131         }
29132         if(this.animateTarget){
29133            this.animHide(callback);
29134         }else{
29135             this.el.hide();
29136             this.hideEl(callback);
29137         }
29138         return this;
29139     },
29140
29141     // private
29142     hideEl : function(callback){
29143         this.proxy.hide();
29144         if(this.modal){
29145             this.mask.hide();
29146             Roo.get(document.body).removeClass("x-body-masked");
29147         }
29148         this.fireEvent("hide", this);
29149         if(typeof callback == "function"){
29150             callback();
29151         }
29152     },
29153
29154     // private
29155     hideAction : function(){
29156         this.setLeft("-10000px");
29157         this.setTop("-10000px");
29158         this.setStyle("visibility", "hidden");
29159     },
29160
29161     // private
29162     refreshSize : function(){
29163         this.size = this.el.getSize();
29164         this.xy = this.el.getXY();
29165         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29166     },
29167
29168     // private
29169     // z-index is managed by the DialogManager and may be overwritten at any time
29170     setZIndex : function(index){
29171         if(this.modal){
29172             this.mask.setStyle("z-index", index);
29173         }
29174         if(this.shim){
29175             this.shim.setStyle("z-index", ++index);
29176         }
29177         if(this.shadow){
29178             this.shadow.setZIndex(++index);
29179         }
29180         this.el.setStyle("z-index", ++index);
29181         if(this.proxy){
29182             this.proxy.setStyle("z-index", ++index);
29183         }
29184         if(this.resizer){
29185             this.resizer.proxy.setStyle("z-index", ++index);
29186         }
29187
29188         this.lastZIndex = index;
29189     },
29190
29191     /**
29192      * Returns the element for this dialog
29193      * @return {Roo.Element} The underlying dialog Element
29194      */
29195     getEl : function(){
29196         return this.el;
29197     }
29198 });
29199
29200 /**
29201  * @class Roo.DialogManager
29202  * Provides global access to BasicDialogs that have been created and
29203  * support for z-indexing (layering) multiple open dialogs.
29204  */
29205 Roo.DialogManager = function(){
29206     var list = {};
29207     var accessList = [];
29208     var front = null;
29209
29210     // private
29211     var sortDialogs = function(d1, d2){
29212         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29213     };
29214
29215     // private
29216     var orderDialogs = function(){
29217         accessList.sort(sortDialogs);
29218         var seed = Roo.DialogManager.zseed;
29219         for(var i = 0, len = accessList.length; i < len; i++){
29220             var dlg = accessList[i];
29221             if(dlg){
29222                 dlg.setZIndex(seed + (i*10));
29223             }
29224         }
29225     };
29226
29227     return {
29228         /**
29229          * The starting z-index for BasicDialogs (defaults to 9000)
29230          * @type Number The z-index value
29231          */
29232         zseed : 9000,
29233
29234         // private
29235         register : function(dlg){
29236             list[dlg.id] = dlg;
29237             accessList.push(dlg);
29238         },
29239
29240         // private
29241         unregister : function(dlg){
29242             delete list[dlg.id];
29243             var i=0;
29244             var len=0;
29245             if(!accessList.indexOf){
29246                 for(  i = 0, len = accessList.length; i < len; i++){
29247                     if(accessList[i] == dlg){
29248                         accessList.splice(i, 1);
29249                         return;
29250                     }
29251                 }
29252             }else{
29253                  i = accessList.indexOf(dlg);
29254                 if(i != -1){
29255                     accessList.splice(i, 1);
29256                 }
29257             }
29258         },
29259
29260         /**
29261          * Gets a registered dialog by id
29262          * @param {String/Object} id The id of the dialog or a dialog
29263          * @return {Roo.BasicDialog} this
29264          */
29265         get : function(id){
29266             return typeof id == "object" ? id : list[id];
29267         },
29268
29269         /**
29270          * Brings the specified dialog to the front
29271          * @param {String/Object} dlg The id of the dialog or a dialog
29272          * @return {Roo.BasicDialog} this
29273          */
29274         bringToFront : function(dlg){
29275             dlg = this.get(dlg);
29276             if(dlg != front){
29277                 front = dlg;
29278                 dlg._lastAccess = new Date().getTime();
29279                 orderDialogs();
29280             }
29281             return dlg;
29282         },
29283
29284         /**
29285          * Sends the specified dialog to the back
29286          * @param {String/Object} dlg The id of the dialog or a dialog
29287          * @return {Roo.BasicDialog} this
29288          */
29289         sendToBack : function(dlg){
29290             dlg = this.get(dlg);
29291             dlg._lastAccess = -(new Date().getTime());
29292             orderDialogs();
29293             return dlg;
29294         },
29295
29296         /**
29297          * Hides all dialogs
29298          */
29299         hideAll : function(){
29300             for(var id in list){
29301                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29302                     list[id].hide();
29303                 }
29304             }
29305         }
29306     };
29307 }();
29308
29309 /**
29310  * @class Roo.LayoutDialog
29311  * @extends Roo.BasicDialog
29312  * Dialog which provides adjustments for working with a layout in a Dialog.
29313  * Add your necessary layout config options to the dialog's config.<br>
29314  * Example usage (including a nested layout):
29315  * <pre><code>
29316 if(!dialog){
29317     dialog = new Roo.LayoutDialog("download-dlg", {
29318         modal: true,
29319         width:600,
29320         height:450,
29321         shadow:true,
29322         minWidth:500,
29323         minHeight:350,
29324         autoTabs:true,
29325         proxyDrag:true,
29326         // layout config merges with the dialog config
29327         center:{
29328             tabPosition: "top",
29329             alwaysShowTabs: true
29330         }
29331     });
29332     dialog.addKeyListener(27, dialog.hide, dialog);
29333     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29334     dialog.addButton("Build It!", this.getDownload, this);
29335
29336     // we can even add nested layouts
29337     var innerLayout = new Roo.BorderLayout("dl-inner", {
29338         east: {
29339             initialSize: 200,
29340             autoScroll:true,
29341             split:true
29342         },
29343         center: {
29344             autoScroll:true
29345         }
29346     });
29347     innerLayout.beginUpdate();
29348     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29349     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29350     innerLayout.endUpdate(true);
29351
29352     var layout = dialog.getLayout();
29353     layout.beginUpdate();
29354     layout.add("center", new Roo.ContentPanel("standard-panel",
29355                         {title: "Download the Source", fitToFrame:true}));
29356     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29357                {title: "Build your own roo.js"}));
29358     layout.getRegion("center").showPanel(sp);
29359     layout.endUpdate();
29360 }
29361 </code></pre>
29362     * @constructor
29363     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29364     * @param {Object} config configuration options
29365   */
29366 Roo.LayoutDialog = function(el, cfg){
29367     
29368     var config=  cfg;
29369     if (typeof(cfg) == 'undefined') {
29370         config = Roo.apply({}, el);
29371         el = Roo.get( document.documentElement || document.body).createChild();
29372         //config.autoCreate = true;
29373     }
29374     
29375     
29376     config.autoTabs = false;
29377     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29378     this.body.setStyle({overflow:"hidden", position:"relative"});
29379     this.layout = new Roo.BorderLayout(this.body.dom, config);
29380     this.layout.monitorWindowResize = false;
29381     this.el.addClass("x-dlg-auto-layout");
29382     // fix case when center region overwrites center function
29383     this.center = Roo.BasicDialog.prototype.center;
29384     this.on("show", this.layout.layout, this.layout, true);
29385     if (config.items) {
29386         var xitems = config.items;
29387         delete config.items;
29388         Roo.each(xitems, this.addxtype, this);
29389     }
29390     
29391     
29392 };
29393 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29394     /**
29395      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29396      * @deprecated
29397      */
29398     endUpdate : function(){
29399         this.layout.endUpdate();
29400     },
29401
29402     /**
29403      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29404      *  @deprecated
29405      */
29406     beginUpdate : function(){
29407         this.layout.beginUpdate();
29408     },
29409
29410     /**
29411      * Get the BorderLayout for this dialog
29412      * @return {Roo.BorderLayout}
29413      */
29414     getLayout : function(){
29415         return this.layout;
29416     },
29417
29418     showEl : function(){
29419         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29420         if(Roo.isIE7){
29421             this.layout.layout();
29422         }
29423     },
29424
29425     // private
29426     // Use the syncHeightBeforeShow config option to control this automatically
29427     syncBodyHeight : function(){
29428         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29429         if(this.layout){this.layout.layout();}
29430     },
29431     
29432       /**
29433      * Add an xtype element (actually adds to the layout.)
29434      * @return {Object} xdata xtype object data.
29435      */
29436     
29437     addxtype : function(c) {
29438         return this.layout.addxtype(c);
29439     }
29440 });/*
29441  * Based on:
29442  * Ext JS Library 1.1.1
29443  * Copyright(c) 2006-2007, Ext JS, LLC.
29444  *
29445  * Originally Released Under LGPL - original licence link has changed is not relivant.
29446  *
29447  * Fork - LGPL
29448  * <script type="text/javascript">
29449  */
29450  
29451 /**
29452  * @class Roo.MessageBox
29453  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29454  * Example usage:
29455  *<pre><code>
29456 // Basic alert:
29457 Roo.Msg.alert('Status', 'Changes saved successfully.');
29458
29459 // Prompt for user data:
29460 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29461     if (btn == 'ok'){
29462         // process text value...
29463     }
29464 });
29465
29466 // Show a dialog using config options:
29467 Roo.Msg.show({
29468    title:'Save Changes?',
29469    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29470    buttons: Roo.Msg.YESNOCANCEL,
29471    fn: processResult,
29472    animEl: 'elId'
29473 });
29474 </code></pre>
29475  * @singleton
29476  */
29477 Roo.MessageBox = function(){
29478     var dlg, opt, mask, waitTimer;
29479     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29480     var buttons, activeTextEl, bwidth;
29481
29482     // private
29483     var handleButton = function(button){
29484         dlg.hide();
29485         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29486     };
29487
29488     // private
29489     var handleHide = function(){
29490         if(opt && opt.cls){
29491             dlg.el.removeClass(opt.cls);
29492         }
29493         if(waitTimer){
29494             Roo.TaskMgr.stop(waitTimer);
29495             waitTimer = null;
29496         }
29497     };
29498
29499     // private
29500     var updateButtons = function(b){
29501         var width = 0;
29502         if(!b){
29503             buttons["ok"].hide();
29504             buttons["cancel"].hide();
29505             buttons["yes"].hide();
29506             buttons["no"].hide();
29507             dlg.footer.dom.style.display = 'none';
29508             return width;
29509         }
29510         dlg.footer.dom.style.display = '';
29511         for(var k in buttons){
29512             if(typeof buttons[k] != "function"){
29513                 if(b[k]){
29514                     buttons[k].show();
29515                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29516                     width += buttons[k].el.getWidth()+15;
29517                 }else{
29518                     buttons[k].hide();
29519                 }
29520             }
29521         }
29522         return width;
29523     };
29524
29525     // private
29526     var handleEsc = function(d, k, e){
29527         if(opt && opt.closable !== false){
29528             dlg.hide();
29529         }
29530         if(e){
29531             e.stopEvent();
29532         }
29533     };
29534
29535     return {
29536         /**
29537          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29538          * @return {Roo.BasicDialog} The BasicDialog element
29539          */
29540         getDialog : function(){
29541            if(!dlg){
29542                 dlg = new Roo.BasicDialog("x-msg-box", {
29543                     autoCreate : true,
29544                     shadow: true,
29545                     draggable: true,
29546                     resizable:false,
29547                     constraintoviewport:false,
29548                     fixedcenter:true,
29549                     collapsible : false,
29550                     shim:true,
29551                     modal: true,
29552                     width:400, height:100,
29553                     buttonAlign:"center",
29554                     closeClick : function(){
29555                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29556                             handleButton("no");
29557                         }else{
29558                             handleButton("cancel");
29559                         }
29560                     }
29561                 });
29562                 dlg.on("hide", handleHide);
29563                 mask = dlg.mask;
29564                 dlg.addKeyListener(27, handleEsc);
29565                 buttons = {};
29566                 var bt = this.buttonText;
29567                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29568                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29569                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29570                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29571                 bodyEl = dlg.body.createChild({
29572
29573                     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>'
29574                 });
29575                 msgEl = bodyEl.dom.firstChild;
29576                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29577                 textboxEl.enableDisplayMode();
29578                 textboxEl.addKeyListener([10,13], function(){
29579                     if(dlg.isVisible() && opt && opt.buttons){
29580                         if(opt.buttons.ok){
29581                             handleButton("ok");
29582                         }else if(opt.buttons.yes){
29583                             handleButton("yes");
29584                         }
29585                     }
29586                 });
29587                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29588                 textareaEl.enableDisplayMode();
29589                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29590                 progressEl.enableDisplayMode();
29591                 var pf = progressEl.dom.firstChild;
29592                 if (pf) {
29593                     pp = Roo.get(pf.firstChild);
29594                     pp.setHeight(pf.offsetHeight);
29595                 }
29596                 
29597             }
29598             return dlg;
29599         },
29600
29601         /**
29602          * Updates the message box body text
29603          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29604          * the XHTML-compliant non-breaking space character '&amp;#160;')
29605          * @return {Roo.MessageBox} This message box
29606          */
29607         updateText : function(text){
29608             if(!dlg.isVisible() && !opt.width){
29609                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29610             }
29611             msgEl.innerHTML = text || '&#160;';
29612             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29613                         Math.max(opt.minWidth || this.minWidth, bwidth));
29614             if(opt.prompt){
29615                 activeTextEl.setWidth(w);
29616             }
29617             if(dlg.isVisible()){
29618                 dlg.fixedcenter = false;
29619             }
29620             dlg.setContentSize(w, bodyEl.getHeight());
29621             if(dlg.isVisible()){
29622                 dlg.fixedcenter = true;
29623             }
29624             return this;
29625         },
29626
29627         /**
29628          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29629          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29630          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29631          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29632          * @return {Roo.MessageBox} This message box
29633          */
29634         updateProgress : function(value, text){
29635             if(text){
29636                 this.updateText(text);
29637             }
29638             if (pp) { // weird bug on my firefox - for some reason this is not defined
29639                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29640             }
29641             return this;
29642         },        
29643
29644         /**
29645          * Returns true if the message box is currently displayed
29646          * @return {Boolean} True if the message box is visible, else false
29647          */
29648         isVisible : function(){
29649             return dlg && dlg.isVisible();  
29650         },
29651
29652         /**
29653          * Hides the message box if it is displayed
29654          */
29655         hide : function(){
29656             if(this.isVisible()){
29657                 dlg.hide();
29658             }  
29659         },
29660
29661         /**
29662          * Displays a new message box, or reinitializes an existing message box, based on the config options
29663          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29664          * The following config object properties are supported:
29665          * <pre>
29666 Property    Type             Description
29667 ----------  ---------------  ------------------------------------------------------------------------------------
29668 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29669                                    closes (defaults to undefined)
29670 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29671                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29672 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29673                                    progress and wait dialogs will ignore this property and always hide the
29674                                    close button as they can only be closed programmatically.
29675 cls               String           A custom CSS class to apply to the message box element
29676 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29677                                    displayed (defaults to 75)
29678 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29679                                    function will be btn (the name of the button that was clicked, if applicable,
29680                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29681                                    Progress and wait dialogs will ignore this option since they do not respond to
29682                                    user actions and can only be closed programmatically, so any required function
29683                                    should be called by the same code after it closes the dialog.
29684 icon              String           A CSS class that provides a background image to be used as an icon for
29685                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29686 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29687 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29688 modal             Boolean          False to allow user interaction with the page while the message box is
29689                                    displayed (defaults to true)
29690 msg               String           A string that will replace the existing message box body text (defaults
29691                                    to the XHTML-compliant non-breaking space character '&#160;')
29692 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29693 progress          Boolean          True to display a progress bar (defaults to false)
29694 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29695 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29696 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29697 title             String           The title text
29698 value             String           The string value to set into the active textbox element if displayed
29699 wait              Boolean          True to display a progress bar (defaults to false)
29700 width             Number           The width of the dialog in pixels
29701 </pre>
29702          *
29703          * Example usage:
29704          * <pre><code>
29705 Roo.Msg.show({
29706    title: 'Address',
29707    msg: 'Please enter your address:',
29708    width: 300,
29709    buttons: Roo.MessageBox.OKCANCEL,
29710    multiline: true,
29711    fn: saveAddress,
29712    animEl: 'addAddressBtn'
29713 });
29714 </code></pre>
29715          * @param {Object} config Configuration options
29716          * @return {Roo.MessageBox} This message box
29717          */
29718         show : function(options){
29719             if(this.isVisible()){
29720                 this.hide();
29721             }
29722             var d = this.getDialog();
29723             opt = options;
29724             d.setTitle(opt.title || "&#160;");
29725             d.close.setDisplayed(opt.closable !== false);
29726             activeTextEl = textboxEl;
29727             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29728             if(opt.prompt){
29729                 if(opt.multiline){
29730                     textboxEl.hide();
29731                     textareaEl.show();
29732                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29733                         opt.multiline : this.defaultTextHeight);
29734                     activeTextEl = textareaEl;
29735                 }else{
29736                     textboxEl.show();
29737                     textareaEl.hide();
29738                 }
29739             }else{
29740                 textboxEl.hide();
29741                 textareaEl.hide();
29742             }
29743             progressEl.setDisplayed(opt.progress === true);
29744             this.updateProgress(0);
29745             activeTextEl.dom.value = opt.value || "";
29746             if(opt.prompt){
29747                 dlg.setDefaultButton(activeTextEl);
29748             }else{
29749                 var bs = opt.buttons;
29750                 var db = null;
29751                 if(bs && bs.ok){
29752                     db = buttons["ok"];
29753                 }else if(bs && bs.yes){
29754                     db = buttons["yes"];
29755                 }
29756                 dlg.setDefaultButton(db);
29757             }
29758             bwidth = updateButtons(opt.buttons);
29759             this.updateText(opt.msg);
29760             if(opt.cls){
29761                 d.el.addClass(opt.cls);
29762             }
29763             d.proxyDrag = opt.proxyDrag === true;
29764             d.modal = opt.modal !== false;
29765             d.mask = opt.modal !== false ? mask : false;
29766             if(!d.isVisible()){
29767                 // force it to the end of the z-index stack so it gets a cursor in FF
29768                 document.body.appendChild(dlg.el.dom);
29769                 d.animateTarget = null;
29770                 d.show(options.animEl);
29771             }
29772             return this;
29773         },
29774
29775         /**
29776          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29777          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29778          * and closing the message box when the process is complete.
29779          * @param {String} title The title bar text
29780          * @param {String} msg The message box body text
29781          * @return {Roo.MessageBox} This message box
29782          */
29783         progress : function(title, msg){
29784             this.show({
29785                 title : title,
29786                 msg : msg,
29787                 buttons: false,
29788                 progress:true,
29789                 closable:false,
29790                 minWidth: this.minProgressWidth,
29791                 modal : true
29792             });
29793             return this;
29794         },
29795
29796         /**
29797          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29798          * If a callback function is passed it will be called after the user clicks the button, and the
29799          * id of the button that was clicked will be passed as the only parameter to the callback
29800          * (could also be the top-right close button).
29801          * @param {String} title The title bar text
29802          * @param {String} msg The message box body text
29803          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29804          * @param {Object} scope (optional) The scope of the callback function
29805          * @return {Roo.MessageBox} This message box
29806          */
29807         alert : function(title, msg, fn, scope){
29808             this.show({
29809                 title : title,
29810                 msg : msg,
29811                 buttons: this.OK,
29812                 fn: fn,
29813                 scope : scope,
29814                 modal : true
29815             });
29816             return this;
29817         },
29818
29819         /**
29820          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29821          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29822          * You are responsible for closing the message box when the process is complete.
29823          * @param {String} msg The message box body text
29824          * @param {String} title (optional) The title bar text
29825          * @return {Roo.MessageBox} This message box
29826          */
29827         wait : function(msg, title){
29828             this.show({
29829                 title : title,
29830                 msg : msg,
29831                 buttons: false,
29832                 closable:false,
29833                 progress:true,
29834                 modal:true,
29835                 width:300,
29836                 wait:true
29837             });
29838             waitTimer = Roo.TaskMgr.start({
29839                 run: function(i){
29840                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29841                 },
29842                 interval: 1000
29843             });
29844             return this;
29845         },
29846
29847         /**
29848          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29849          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29850          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29851          * @param {String} title The title bar text
29852          * @param {String} msg The message box body text
29853          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29854          * @param {Object} scope (optional) The scope of the callback function
29855          * @return {Roo.MessageBox} This message box
29856          */
29857         confirm : function(title, msg, fn, scope){
29858             this.show({
29859                 title : title,
29860                 msg : msg,
29861                 buttons: this.YESNO,
29862                 fn: fn,
29863                 scope : scope,
29864                 modal : true
29865             });
29866             return this;
29867         },
29868
29869         /**
29870          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29871          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29872          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29873          * (could also be the top-right close button) and the text that was entered will be passed as the two
29874          * parameters to the callback.
29875          * @param {String} title The title bar text
29876          * @param {String} msg The message box body text
29877          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29878          * @param {Object} scope (optional) The scope of the callback function
29879          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29880          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29881          * @return {Roo.MessageBox} This message box
29882          */
29883         prompt : function(title, msg, fn, scope, multiline){
29884             this.show({
29885                 title : title,
29886                 msg : msg,
29887                 buttons: this.OKCANCEL,
29888                 fn: fn,
29889                 minWidth:250,
29890                 scope : scope,
29891                 prompt:true,
29892                 multiline: multiline,
29893                 modal : true
29894             });
29895             return this;
29896         },
29897
29898         /**
29899          * Button config that displays a single OK button
29900          * @type Object
29901          */
29902         OK : {ok:true},
29903         /**
29904          * Button config that displays Yes and No buttons
29905          * @type Object
29906          */
29907         YESNO : {yes:true, no:true},
29908         /**
29909          * Button config that displays OK and Cancel buttons
29910          * @type Object
29911          */
29912         OKCANCEL : {ok:true, cancel:true},
29913         /**
29914          * Button config that displays Yes, No and Cancel buttons
29915          * @type Object
29916          */
29917         YESNOCANCEL : {yes:true, no:true, cancel:true},
29918
29919         /**
29920          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29921          * @type Number
29922          */
29923         defaultTextHeight : 75,
29924         /**
29925          * The maximum width in pixels of the message box (defaults to 600)
29926          * @type Number
29927          */
29928         maxWidth : 600,
29929         /**
29930          * The minimum width in pixels of the message box (defaults to 100)
29931          * @type Number
29932          */
29933         minWidth : 100,
29934         /**
29935          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29936          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29937          * @type Number
29938          */
29939         minProgressWidth : 250,
29940         /**
29941          * An object containing the default button text strings that can be overriden for localized language support.
29942          * Supported properties are: ok, cancel, yes and no.
29943          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29944          * @type Object
29945          */
29946         buttonText : {
29947             ok : "OK",
29948             cancel : "Cancel",
29949             yes : "Yes",
29950             no : "No"
29951         }
29952     };
29953 }();
29954
29955 /**
29956  * Shorthand for {@link Roo.MessageBox}
29957  */
29958 Roo.Msg = Roo.MessageBox;/*
29959  * Based on:
29960  * Ext JS Library 1.1.1
29961  * Copyright(c) 2006-2007, Ext JS, LLC.
29962  *
29963  * Originally Released Under LGPL - original licence link has changed is not relivant.
29964  *
29965  * Fork - LGPL
29966  * <script type="text/javascript">
29967  */
29968 /**
29969  * @class Roo.QuickTips
29970  * Provides attractive and customizable tooltips for any element.
29971  * @singleton
29972  */
29973 Roo.QuickTips = function(){
29974     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
29975     var ce, bd, xy, dd;
29976     var visible = false, disabled = true, inited = false;
29977     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
29978     
29979     var onOver = function(e){
29980         if(disabled){
29981             return;
29982         }
29983         var t = e.getTarget();
29984         if(!t || t.nodeType !== 1 || t == document || t == document.body){
29985             return;
29986         }
29987         if(ce && t == ce.el){
29988             clearTimeout(hideProc);
29989             return;
29990         }
29991         if(t && tagEls[t.id]){
29992             tagEls[t.id].el = t;
29993             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
29994             return;
29995         }
29996         var ttp, et = Roo.fly(t);
29997         var ns = cfg.namespace;
29998         if(tm.interceptTitles && t.title){
29999             ttp = t.title;
30000             t.qtip = ttp;
30001             t.removeAttribute("title");
30002             e.preventDefault();
30003         }else{
30004             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30005         }
30006         if(ttp){
30007             showProc = show.defer(tm.showDelay, tm, [{
30008                 el: t, 
30009                 text: ttp, 
30010                 width: et.getAttributeNS(ns, cfg.width),
30011                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30012                 title: et.getAttributeNS(ns, cfg.title),
30013                     cls: et.getAttributeNS(ns, cfg.cls)
30014             }]);
30015         }
30016     };
30017     
30018     var onOut = function(e){
30019         clearTimeout(showProc);
30020         var t = e.getTarget();
30021         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30022             hideProc = setTimeout(hide, tm.hideDelay);
30023         }
30024     };
30025     
30026     var onMove = function(e){
30027         if(disabled){
30028             return;
30029         }
30030         xy = e.getXY();
30031         xy[1] += 18;
30032         if(tm.trackMouse && ce){
30033             el.setXY(xy);
30034         }
30035     };
30036     
30037     var onDown = function(e){
30038         clearTimeout(showProc);
30039         clearTimeout(hideProc);
30040         if(!e.within(el)){
30041             if(tm.hideOnClick){
30042                 hide();
30043                 tm.disable();
30044                 tm.enable.defer(100, tm);
30045             }
30046         }
30047     };
30048     
30049     var getPad = function(){
30050         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30051     };
30052
30053     var show = function(o){
30054         if(disabled){
30055             return;
30056         }
30057         clearTimeout(dismissProc);
30058         ce = o;
30059         if(removeCls){ // in case manually hidden
30060             el.removeClass(removeCls);
30061             removeCls = null;
30062         }
30063         if(ce.cls){
30064             el.addClass(ce.cls);
30065             removeCls = ce.cls;
30066         }
30067         if(ce.title){
30068             tipTitle.update(ce.title);
30069             tipTitle.show();
30070         }else{
30071             tipTitle.update('');
30072             tipTitle.hide();
30073         }
30074         el.dom.style.width  = tm.maxWidth+'px';
30075         //tipBody.dom.style.width = '';
30076         tipBodyText.update(o.text);
30077         var p = getPad(), w = ce.width;
30078         if(!w){
30079             var td = tipBodyText.dom;
30080             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30081             if(aw > tm.maxWidth){
30082                 w = tm.maxWidth;
30083             }else if(aw < tm.minWidth){
30084                 w = tm.minWidth;
30085             }else{
30086                 w = aw;
30087             }
30088         }
30089         //tipBody.setWidth(w);
30090         el.setWidth(parseInt(w, 10) + p);
30091         if(ce.autoHide === false){
30092             close.setDisplayed(true);
30093             if(dd){
30094                 dd.unlock();
30095             }
30096         }else{
30097             close.setDisplayed(false);
30098             if(dd){
30099                 dd.lock();
30100             }
30101         }
30102         if(xy){
30103             el.avoidY = xy[1]-18;
30104             el.setXY(xy);
30105         }
30106         if(tm.animate){
30107             el.setOpacity(.1);
30108             el.setStyle("visibility", "visible");
30109             el.fadeIn({callback: afterShow});
30110         }else{
30111             afterShow();
30112         }
30113     };
30114     
30115     var afterShow = function(){
30116         if(ce){
30117             el.show();
30118             esc.enable();
30119             if(tm.autoDismiss && ce.autoHide !== false){
30120                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30121             }
30122         }
30123     };
30124     
30125     var hide = function(noanim){
30126         clearTimeout(dismissProc);
30127         clearTimeout(hideProc);
30128         ce = null;
30129         if(el.isVisible()){
30130             esc.disable();
30131             if(noanim !== true && tm.animate){
30132                 el.fadeOut({callback: afterHide});
30133             }else{
30134                 afterHide();
30135             } 
30136         }
30137     };
30138     
30139     var afterHide = function(){
30140         el.hide();
30141         if(removeCls){
30142             el.removeClass(removeCls);
30143             removeCls = null;
30144         }
30145     };
30146     
30147     return {
30148         /**
30149         * @cfg {Number} minWidth
30150         * The minimum width of the quick tip (defaults to 40)
30151         */
30152        minWidth : 40,
30153         /**
30154         * @cfg {Number} maxWidth
30155         * The maximum width of the quick tip (defaults to 300)
30156         */
30157        maxWidth : 300,
30158         /**
30159         * @cfg {Boolean} interceptTitles
30160         * True to automatically use the element's DOM title value if available (defaults to false)
30161         */
30162        interceptTitles : false,
30163         /**
30164         * @cfg {Boolean} trackMouse
30165         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30166         */
30167        trackMouse : false,
30168         /**
30169         * @cfg {Boolean} hideOnClick
30170         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30171         */
30172        hideOnClick : true,
30173         /**
30174         * @cfg {Number} showDelay
30175         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30176         */
30177        showDelay : 500,
30178         /**
30179         * @cfg {Number} hideDelay
30180         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30181         */
30182        hideDelay : 200,
30183         /**
30184         * @cfg {Boolean} autoHide
30185         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30186         * Used in conjunction with hideDelay.
30187         */
30188        autoHide : true,
30189         /**
30190         * @cfg {Boolean}
30191         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30192         * (defaults to true).  Used in conjunction with autoDismissDelay.
30193         */
30194        autoDismiss : true,
30195         /**
30196         * @cfg {Number}
30197         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30198         */
30199        autoDismissDelay : 5000,
30200        /**
30201         * @cfg {Boolean} animate
30202         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30203         */
30204        animate : false,
30205
30206        /**
30207         * @cfg {String} title
30208         * Title text to display (defaults to '').  This can be any valid HTML markup.
30209         */
30210         title: '',
30211        /**
30212         * @cfg {String} text
30213         * Body text to display (defaults to '').  This can be any valid HTML markup.
30214         */
30215         text : '',
30216        /**
30217         * @cfg {String} cls
30218         * A CSS class to apply to the base quick tip element (defaults to '').
30219         */
30220         cls : '',
30221        /**
30222         * @cfg {Number} width
30223         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30224         * minWidth or maxWidth.
30225         */
30226         width : null,
30227
30228     /**
30229      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30230      * or display QuickTips in a page.
30231      */
30232        init : function(){
30233           tm = Roo.QuickTips;
30234           cfg = tm.tagConfig;
30235           if(!inited){
30236               if(!Roo.isReady){ // allow calling of init() before onReady
30237                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30238                   return;
30239               }
30240               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30241               el.fxDefaults = {stopFx: true};
30242               // maximum custom styling
30243               //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>');
30244               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>');              
30245               tipTitle = el.child('h3');
30246               tipTitle.enableDisplayMode("block");
30247               tipBody = el.child('div.x-tip-bd');
30248               tipBodyText = el.child('div.x-tip-bd-inner');
30249               //bdLeft = el.child('div.x-tip-bd-left');
30250               //bdRight = el.child('div.x-tip-bd-right');
30251               close = el.child('div.x-tip-close');
30252               close.enableDisplayMode("block");
30253               close.on("click", hide);
30254               var d = Roo.get(document);
30255               d.on("mousedown", onDown);
30256               d.on("mouseover", onOver);
30257               d.on("mouseout", onOut);
30258               d.on("mousemove", onMove);
30259               esc = d.addKeyListener(27, hide);
30260               esc.disable();
30261               if(Roo.dd.DD){
30262                   dd = el.initDD("default", null, {
30263                       onDrag : function(){
30264                           el.sync();  
30265                       }
30266                   });
30267                   dd.setHandleElId(tipTitle.id);
30268                   dd.lock();
30269               }
30270               inited = true;
30271           }
30272           this.enable(); 
30273        },
30274
30275     /**
30276      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30277      * are supported:
30278      * <pre>
30279 Property    Type                   Description
30280 ----------  ---------------------  ------------------------------------------------------------------------
30281 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30282      * </ul>
30283      * @param {Object} config The config object
30284      */
30285        register : function(config){
30286            var cs = config instanceof Array ? config : arguments;
30287            for(var i = 0, len = cs.length; i < len; i++) {
30288                var c = cs[i];
30289                var target = c.target;
30290                if(target){
30291                    if(target instanceof Array){
30292                        for(var j = 0, jlen = target.length; j < jlen; j++){
30293                            tagEls[target[j]] = c;
30294                        }
30295                    }else{
30296                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30297                    }
30298                }
30299            }
30300        },
30301
30302     /**
30303      * Removes this quick tip from its element and destroys it.
30304      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30305      */
30306        unregister : function(el){
30307            delete tagEls[Roo.id(el)];
30308        },
30309
30310     /**
30311      * Enable this quick tip.
30312      */
30313        enable : function(){
30314            if(inited && disabled){
30315                locks.pop();
30316                if(locks.length < 1){
30317                    disabled = false;
30318                }
30319            }
30320        },
30321
30322     /**
30323      * Disable this quick tip.
30324      */
30325        disable : function(){
30326           disabled = true;
30327           clearTimeout(showProc);
30328           clearTimeout(hideProc);
30329           clearTimeout(dismissProc);
30330           if(ce){
30331               hide(true);
30332           }
30333           locks.push(1);
30334        },
30335
30336     /**
30337      * Returns true if the quick tip is enabled, else false.
30338      */
30339        isEnabled : function(){
30340             return !disabled;
30341        },
30342
30343         // private
30344        tagConfig : {
30345            namespace : "ext",
30346            attribute : "qtip",
30347            width : "width",
30348            target : "target",
30349            title : "qtitle",
30350            hide : "hide",
30351            cls : "qclass"
30352        }
30353    };
30354 }();
30355
30356 // backwards compat
30357 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30358  * Based on:
30359  * Ext JS Library 1.1.1
30360  * Copyright(c) 2006-2007, Ext JS, LLC.
30361  *
30362  * Originally Released Under LGPL - original licence link has changed is not relivant.
30363  *
30364  * Fork - LGPL
30365  * <script type="text/javascript">
30366  */
30367  
30368
30369 /**
30370  * @class Roo.tree.TreePanel
30371  * @extends Roo.data.Tree
30372
30373  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30374  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30375  * @cfg {Boolean} enableDD true to enable drag and drop
30376  * @cfg {Boolean} enableDrag true to enable just drag
30377  * @cfg {Boolean} enableDrop true to enable just drop
30378  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30379  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30380  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30381  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30382  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30383  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30384  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30385  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30386  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30387  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30388  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30389  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30390  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30391  * @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>
30392  * @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>
30393  * 
30394  * @constructor
30395  * @param {String/HTMLElement/Element} el The container element
30396  * @param {Object} config
30397  */
30398 Roo.tree.TreePanel = function(el, config){
30399     var root = false;
30400     var loader = false;
30401     if (config.root) {
30402         root = config.root;
30403         delete config.root;
30404     }
30405     if (config.loader) {
30406         loader = config.loader;
30407         delete config.loader;
30408     }
30409     
30410     Roo.apply(this, config);
30411     Roo.tree.TreePanel.superclass.constructor.call(this);
30412     this.el = Roo.get(el);
30413     this.el.addClass('x-tree');
30414     //console.log(root);
30415     if (root) {
30416         this.setRootNode( Roo.factory(root, Roo.tree));
30417     }
30418     if (loader) {
30419         this.loader = Roo.factory(loader, Roo.tree);
30420     }
30421    /**
30422     * Read-only. The id of the container element becomes this TreePanel's id.
30423     */
30424    this.id = this.el.id;
30425    this.addEvents({
30426         /**
30427         * @event beforeload
30428         * Fires before a node is loaded, return false to cancel
30429         * @param {Node} node The node being loaded
30430         */
30431         "beforeload" : true,
30432         /**
30433         * @event load
30434         * Fires when a node is loaded
30435         * @param {Node} node The node that was loaded
30436         */
30437         "load" : true,
30438         /**
30439         * @event textchange
30440         * Fires when the text for a node is changed
30441         * @param {Node} node The node
30442         * @param {String} text The new text
30443         * @param {String} oldText The old text
30444         */
30445         "textchange" : true,
30446         /**
30447         * @event beforeexpand
30448         * Fires before a node is expanded, return false to cancel.
30449         * @param {Node} node The node
30450         * @param {Boolean} deep
30451         * @param {Boolean} anim
30452         */
30453         "beforeexpand" : true,
30454         /**
30455         * @event beforecollapse
30456         * Fires before a node is collapsed, return false to cancel.
30457         * @param {Node} node The node
30458         * @param {Boolean} deep
30459         * @param {Boolean} anim
30460         */
30461         "beforecollapse" : true,
30462         /**
30463         * @event expand
30464         * Fires when a node is expanded
30465         * @param {Node} node The node
30466         */
30467         "expand" : true,
30468         /**
30469         * @event disabledchange
30470         * Fires when the disabled status of a node changes
30471         * @param {Node} node The node
30472         * @param {Boolean} disabled
30473         */
30474         "disabledchange" : true,
30475         /**
30476         * @event collapse
30477         * Fires when a node is collapsed
30478         * @param {Node} node The node
30479         */
30480         "collapse" : true,
30481         /**
30482         * @event beforeclick
30483         * Fires before click processing on a node. Return false to cancel the default action.
30484         * @param {Node} node The node
30485         * @param {Roo.EventObject} e The event object
30486         */
30487         "beforeclick":true,
30488         /**
30489         * @event checkchange
30490         * Fires when a node with a checkbox's checked property changes
30491         * @param {Node} this This node
30492         * @param {Boolean} checked
30493         */
30494         "checkchange":true,
30495         /**
30496         * @event click
30497         * Fires when a node is clicked
30498         * @param {Node} node The node
30499         * @param {Roo.EventObject} e The event object
30500         */
30501         "click":true,
30502         /**
30503         * @event dblclick
30504         * Fires when a node is double clicked
30505         * @param {Node} node The node
30506         * @param {Roo.EventObject} e The event object
30507         */
30508         "dblclick":true,
30509         /**
30510         * @event contextmenu
30511         * Fires when a node is right clicked
30512         * @param {Node} node The node
30513         * @param {Roo.EventObject} e The event object
30514         */
30515         "contextmenu":true,
30516         /**
30517         * @event beforechildrenrendered
30518         * Fires right before the child nodes for a node are rendered
30519         * @param {Node} node The node
30520         */
30521         "beforechildrenrendered":true,
30522        /**
30523              * @event startdrag
30524              * Fires when a node starts being dragged
30525              * @param {Roo.tree.TreePanel} this
30526              * @param {Roo.tree.TreeNode} node
30527              * @param {event} e The raw browser event
30528              */ 
30529             "startdrag" : true,
30530             /**
30531              * @event enddrag
30532              * Fires when a drag operation is complete
30533              * @param {Roo.tree.TreePanel} this
30534              * @param {Roo.tree.TreeNode} node
30535              * @param {event} e The raw browser event
30536              */
30537             "enddrag" : true,
30538             /**
30539              * @event dragdrop
30540              * Fires when a dragged node is dropped on a valid DD target
30541              * @param {Roo.tree.TreePanel} this
30542              * @param {Roo.tree.TreeNode} node
30543              * @param {DD} dd The dd it was dropped on
30544              * @param {event} e The raw browser event
30545              */
30546             "dragdrop" : true,
30547             /**
30548              * @event beforenodedrop
30549              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30550              * passed to handlers has the following properties:<br />
30551              * <ul style="padding:5px;padding-left:16px;">
30552              * <li>tree - The TreePanel</li>
30553              * <li>target - The node being targeted for the drop</li>
30554              * <li>data - The drag data from the drag source</li>
30555              * <li>point - The point of the drop - append, above or below</li>
30556              * <li>source - The drag source</li>
30557              * <li>rawEvent - Raw mouse event</li>
30558              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30559              * to be inserted by setting them on this object.</li>
30560              * <li>cancel - Set this to true to cancel the drop.</li>
30561              * </ul>
30562              * @param {Object} dropEvent
30563              */
30564             "beforenodedrop" : true,
30565             /**
30566              * @event nodedrop
30567              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30568              * passed to handlers has the following properties:<br />
30569              * <ul style="padding:5px;padding-left:16px;">
30570              * <li>tree - The TreePanel</li>
30571              * <li>target - The node being targeted for the drop</li>
30572              * <li>data - The drag data from the drag source</li>
30573              * <li>point - The point of the drop - append, above or below</li>
30574              * <li>source - The drag source</li>
30575              * <li>rawEvent - Raw mouse event</li>
30576              * <li>dropNode - Dropped node(s).</li>
30577              * </ul>
30578              * @param {Object} dropEvent
30579              */
30580             "nodedrop" : true,
30581              /**
30582              * @event nodedragover
30583              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30584              * passed to handlers has the following properties:<br />
30585              * <ul style="padding:5px;padding-left:16px;">
30586              * <li>tree - The TreePanel</li>
30587              * <li>target - The node being targeted for the drop</li>
30588              * <li>data - The drag data from the drag source</li>
30589              * <li>point - The point of the drop - append, above or below</li>
30590              * <li>source - The drag source</li>
30591              * <li>rawEvent - Raw mouse event</li>
30592              * <li>dropNode - Drop node(s) provided by the source.</li>
30593              * <li>cancel - Set this to true to signal drop not allowed.</li>
30594              * </ul>
30595              * @param {Object} dragOverEvent
30596              */
30597             "nodedragover" : true
30598         
30599    });
30600    if(this.singleExpand){
30601        this.on("beforeexpand", this.restrictExpand, this);
30602    }
30603 };
30604 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30605     rootVisible : true,
30606     animate: Roo.enableFx,
30607     lines : true,
30608     enableDD : false,
30609     hlDrop : Roo.enableFx,
30610   
30611     renderer: false,
30612     
30613     rendererTip: false,
30614     // private
30615     restrictExpand : function(node){
30616         var p = node.parentNode;
30617         if(p){
30618             if(p.expandedChild && p.expandedChild.parentNode == p){
30619                 p.expandedChild.collapse();
30620             }
30621             p.expandedChild = node;
30622         }
30623     },
30624
30625     // private override
30626     setRootNode : function(node){
30627         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30628         if(!this.rootVisible){
30629             node.ui = new Roo.tree.RootTreeNodeUI(node);
30630         }
30631         return node;
30632     },
30633
30634     /**
30635      * Returns the container element for this TreePanel
30636      */
30637     getEl : function(){
30638         return this.el;
30639     },
30640
30641     /**
30642      * Returns the default TreeLoader for this TreePanel
30643      */
30644     getLoader : function(){
30645         return this.loader;
30646     },
30647
30648     /**
30649      * Expand all nodes
30650      */
30651     expandAll : function(){
30652         this.root.expand(true);
30653     },
30654
30655     /**
30656      * Collapse all nodes
30657      */
30658     collapseAll : function(){
30659         this.root.collapse(true);
30660     },
30661
30662     /**
30663      * Returns the selection model used by this TreePanel
30664      */
30665     getSelectionModel : function(){
30666         if(!this.selModel){
30667             this.selModel = new Roo.tree.DefaultSelectionModel();
30668         }
30669         return this.selModel;
30670     },
30671
30672     /**
30673      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30674      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30675      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30676      * @return {Array}
30677      */
30678     getChecked : function(a, startNode){
30679         startNode = startNode || this.root;
30680         var r = [];
30681         var f = function(){
30682             if(this.attributes.checked){
30683                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30684             }
30685         }
30686         startNode.cascade(f);
30687         return r;
30688     },
30689
30690     /**
30691      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30692      * @param {String} path
30693      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30694      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30695      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30696      */
30697     expandPath : function(path, attr, callback){
30698         attr = attr || "id";
30699         var keys = path.split(this.pathSeparator);
30700         var curNode = this.root;
30701         if(curNode.attributes[attr] != keys[1]){ // invalid root
30702             if(callback){
30703                 callback(false, null);
30704             }
30705             return;
30706         }
30707         var index = 1;
30708         var f = function(){
30709             if(++index == keys.length){
30710                 if(callback){
30711                     callback(true, curNode);
30712                 }
30713                 return;
30714             }
30715             var c = curNode.findChild(attr, keys[index]);
30716             if(!c){
30717                 if(callback){
30718                     callback(false, curNode);
30719                 }
30720                 return;
30721             }
30722             curNode = c;
30723             c.expand(false, false, f);
30724         };
30725         curNode.expand(false, false, f);
30726     },
30727
30728     /**
30729      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30730      * @param {String} path
30731      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30732      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30733      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30734      */
30735     selectPath : function(path, attr, callback){
30736         attr = attr || "id";
30737         var keys = path.split(this.pathSeparator);
30738         var v = keys.pop();
30739         if(keys.length > 0){
30740             var f = function(success, node){
30741                 if(success && node){
30742                     var n = node.findChild(attr, v);
30743                     if(n){
30744                         n.select();
30745                         if(callback){
30746                             callback(true, n);
30747                         }
30748                     }else if(callback){
30749                         callback(false, n);
30750                     }
30751                 }else{
30752                     if(callback){
30753                         callback(false, n);
30754                     }
30755                 }
30756             };
30757             this.expandPath(keys.join(this.pathSeparator), attr, f);
30758         }else{
30759             this.root.select();
30760             if(callback){
30761                 callback(true, this.root);
30762             }
30763         }
30764     },
30765
30766     getTreeEl : function(){
30767         return this.el;
30768     },
30769
30770     /**
30771      * Trigger rendering of this TreePanel
30772      */
30773     render : function(){
30774         if (this.innerCt) {
30775             return this; // stop it rendering more than once!!
30776         }
30777         
30778         this.innerCt = this.el.createChild({tag:"ul",
30779                cls:"x-tree-root-ct " +
30780                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30781
30782         if(this.containerScroll){
30783             Roo.dd.ScrollManager.register(this.el);
30784         }
30785         if((this.enableDD || this.enableDrop) && !this.dropZone){
30786            /**
30787             * The dropZone used by this tree if drop is enabled
30788             * @type Roo.tree.TreeDropZone
30789             */
30790              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30791                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30792            });
30793         }
30794         if((this.enableDD || this.enableDrag) && !this.dragZone){
30795            /**
30796             * The dragZone used by this tree if drag is enabled
30797             * @type Roo.tree.TreeDragZone
30798             */
30799             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30800                ddGroup: this.ddGroup || "TreeDD",
30801                scroll: this.ddScroll
30802            });
30803         }
30804         this.getSelectionModel().init(this);
30805         if (!this.root) {
30806             console.log("ROOT not set in tree");
30807             return;
30808         }
30809         this.root.render();
30810         if(!this.rootVisible){
30811             this.root.renderChildren();
30812         }
30813         return this;
30814     }
30815 });/*
30816  * Based on:
30817  * Ext JS Library 1.1.1
30818  * Copyright(c) 2006-2007, Ext JS, LLC.
30819  *
30820  * Originally Released Under LGPL - original licence link has changed is not relivant.
30821  *
30822  * Fork - LGPL
30823  * <script type="text/javascript">
30824  */
30825  
30826
30827 /**
30828  * @class Roo.tree.DefaultSelectionModel
30829  * @extends Roo.util.Observable
30830  * The default single selection for a TreePanel.
30831  */
30832 Roo.tree.DefaultSelectionModel = function(){
30833    this.selNode = null;
30834    
30835    this.addEvents({
30836        /**
30837         * @event selectionchange
30838         * Fires when the selected node changes
30839         * @param {DefaultSelectionModel} this
30840         * @param {TreeNode} node the new selection
30841         */
30842        "selectionchange" : true,
30843
30844        /**
30845         * @event beforeselect
30846         * Fires before the selected node changes, return false to cancel the change
30847         * @param {DefaultSelectionModel} this
30848         * @param {TreeNode} node the new selection
30849         * @param {TreeNode} node the old selection
30850         */
30851        "beforeselect" : true
30852    });
30853 };
30854
30855 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30856     init : function(tree){
30857         this.tree = tree;
30858         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30859         tree.on("click", this.onNodeClick, this);
30860     },
30861     
30862     onNodeClick : function(node, e){
30863         if (e.ctrlKey && this.selNode == node)  {
30864             this.unselect(node);
30865             return;
30866         }
30867         this.select(node);
30868     },
30869     
30870     /**
30871      * Select a node.
30872      * @param {TreeNode} node The node to select
30873      * @return {TreeNode} The selected node
30874      */
30875     select : function(node){
30876         var last = this.selNode;
30877         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30878             if(last){
30879                 last.ui.onSelectedChange(false);
30880             }
30881             this.selNode = node;
30882             node.ui.onSelectedChange(true);
30883             this.fireEvent("selectionchange", this, node, last);
30884         }
30885         return node;
30886     },
30887     
30888     /**
30889      * Deselect a node.
30890      * @param {TreeNode} node The node to unselect
30891      */
30892     unselect : function(node){
30893         if(this.selNode == node){
30894             this.clearSelections();
30895         }    
30896     },
30897     
30898     /**
30899      * Clear all selections
30900      */
30901     clearSelections : function(){
30902         var n = this.selNode;
30903         if(n){
30904             n.ui.onSelectedChange(false);
30905             this.selNode = null;
30906             this.fireEvent("selectionchange", this, null);
30907         }
30908         return n;
30909     },
30910     
30911     /**
30912      * Get the selected node
30913      * @return {TreeNode} The selected node
30914      */
30915     getSelectedNode : function(){
30916         return this.selNode;    
30917     },
30918     
30919     /**
30920      * Returns true if the node is selected
30921      * @param {TreeNode} node The node to check
30922      * @return {Boolean}
30923      */
30924     isSelected : function(node){
30925         return this.selNode == node;  
30926     },
30927
30928     /**
30929      * Selects the node above the selected node in the tree, intelligently walking the nodes
30930      * @return TreeNode The new selection
30931      */
30932     selectPrevious : function(){
30933         var s = this.selNode || this.lastSelNode;
30934         if(!s){
30935             return null;
30936         }
30937         var ps = s.previousSibling;
30938         if(ps){
30939             if(!ps.isExpanded() || ps.childNodes.length < 1){
30940                 return this.select(ps);
30941             } else{
30942                 var lc = ps.lastChild;
30943                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30944                     lc = lc.lastChild;
30945                 }
30946                 return this.select(lc);
30947             }
30948         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30949             return this.select(s.parentNode);
30950         }
30951         return null;
30952     },
30953
30954     /**
30955      * Selects the node above the selected node in the tree, intelligently walking the nodes
30956      * @return TreeNode The new selection
30957      */
30958     selectNext : function(){
30959         var s = this.selNode || this.lastSelNode;
30960         if(!s){
30961             return null;
30962         }
30963         if(s.firstChild && s.isExpanded()){
30964              return this.select(s.firstChild);
30965          }else if(s.nextSibling){
30966              return this.select(s.nextSibling);
30967          }else if(s.parentNode){
30968             var newS = null;
30969             s.parentNode.bubble(function(){
30970                 if(this.nextSibling){
30971                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
30972                     return false;
30973                 }
30974             });
30975             return newS;
30976          }
30977         return null;
30978     },
30979
30980     onKeyDown : function(e){
30981         var s = this.selNode || this.lastSelNode;
30982         // undesirable, but required
30983         var sm = this;
30984         if(!s){
30985             return;
30986         }
30987         var k = e.getKey();
30988         switch(k){
30989              case e.DOWN:
30990                  e.stopEvent();
30991                  this.selectNext();
30992              break;
30993              case e.UP:
30994                  e.stopEvent();
30995                  this.selectPrevious();
30996              break;
30997              case e.RIGHT:
30998                  e.preventDefault();
30999                  if(s.hasChildNodes()){
31000                      if(!s.isExpanded()){
31001                          s.expand();
31002                      }else if(s.firstChild){
31003                          this.select(s.firstChild, e);
31004                      }
31005                  }
31006              break;
31007              case e.LEFT:
31008                  e.preventDefault();
31009                  if(s.hasChildNodes() && s.isExpanded()){
31010                      s.collapse();
31011                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31012                      this.select(s.parentNode, e);
31013                  }
31014              break;
31015         };
31016     }
31017 });
31018
31019 /**
31020  * @class Roo.tree.MultiSelectionModel
31021  * @extends Roo.util.Observable
31022  * Multi selection for a TreePanel.
31023  */
31024 Roo.tree.MultiSelectionModel = function(){
31025    this.selNodes = [];
31026    this.selMap = {};
31027    this.addEvents({
31028        /**
31029         * @event selectionchange
31030         * Fires when the selected nodes change
31031         * @param {MultiSelectionModel} this
31032         * @param {Array} nodes Array of the selected nodes
31033         */
31034        "selectionchange" : true
31035    });
31036 };
31037
31038 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31039     init : function(tree){
31040         this.tree = tree;
31041         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31042         tree.on("click", this.onNodeClick, this);
31043     },
31044     
31045     onNodeClick : function(node, e){
31046         this.select(node, e, e.ctrlKey);
31047     },
31048     
31049     /**
31050      * Select a node.
31051      * @param {TreeNode} node The node to select
31052      * @param {EventObject} e (optional) An event associated with the selection
31053      * @param {Boolean} keepExisting True to retain existing selections
31054      * @return {TreeNode} The selected node
31055      */
31056     select : function(node, e, keepExisting){
31057         if(keepExisting !== true){
31058             this.clearSelections(true);
31059         }
31060         if(this.isSelected(node)){
31061             this.lastSelNode = node;
31062             return node;
31063         }
31064         this.selNodes.push(node);
31065         this.selMap[node.id] = node;
31066         this.lastSelNode = node;
31067         node.ui.onSelectedChange(true);
31068         this.fireEvent("selectionchange", this, this.selNodes);
31069         return node;
31070     },
31071     
31072     /**
31073      * Deselect a node.
31074      * @param {TreeNode} node The node to unselect
31075      */
31076     unselect : function(node){
31077         if(this.selMap[node.id]){
31078             node.ui.onSelectedChange(false);
31079             var sn = this.selNodes;
31080             var index = -1;
31081             if(sn.indexOf){
31082                 index = sn.indexOf(node);
31083             }else{
31084                 for(var i = 0, len = sn.length; i < len; i++){
31085                     if(sn[i] == node){
31086                         index = i;
31087                         break;
31088                     }
31089                 }
31090             }
31091             if(index != -1){
31092                 this.selNodes.splice(index, 1);
31093             }
31094             delete this.selMap[node.id];
31095             this.fireEvent("selectionchange", this, this.selNodes);
31096         }
31097     },
31098     
31099     /**
31100      * Clear all selections
31101      */
31102     clearSelections : function(suppressEvent){
31103         var sn = this.selNodes;
31104         if(sn.length > 0){
31105             for(var i = 0, len = sn.length; i < len; i++){
31106                 sn[i].ui.onSelectedChange(false);
31107             }
31108             this.selNodes = [];
31109             this.selMap = {};
31110             if(suppressEvent !== true){
31111                 this.fireEvent("selectionchange", this, this.selNodes);
31112             }
31113         }
31114     },
31115     
31116     /**
31117      * Returns true if the node is selected
31118      * @param {TreeNode} node The node to check
31119      * @return {Boolean}
31120      */
31121     isSelected : function(node){
31122         return this.selMap[node.id] ? true : false;  
31123     },
31124     
31125     /**
31126      * Returns an array of the selected nodes
31127      * @return {Array}
31128      */
31129     getSelectedNodes : function(){
31130         return this.selNodes;    
31131     },
31132
31133     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31134
31135     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31136
31137     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31138 });/*
31139  * Based on:
31140  * Ext JS Library 1.1.1
31141  * Copyright(c) 2006-2007, Ext JS, LLC.
31142  *
31143  * Originally Released Under LGPL - original licence link has changed is not relivant.
31144  *
31145  * Fork - LGPL
31146  * <script type="text/javascript">
31147  */
31148  
31149 /**
31150  * @class Roo.tree.TreeNode
31151  * @extends Roo.data.Node
31152  * @cfg {String} text The text for this node
31153  * @cfg {Boolean} expanded true to start the node expanded
31154  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31155  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31156  * @cfg {Boolean} disabled true to start the node disabled
31157  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31158  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31159  * @cfg {String} cls A css class to be added to the node
31160  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31161  * @cfg {String} href URL of the link used for the node (defaults to #)
31162  * @cfg {String} hrefTarget target frame for the link
31163  * @cfg {String} qtip An Ext QuickTip for the node
31164  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31165  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31166  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31167  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31168  * (defaults to undefined with no checkbox rendered)
31169  * @constructor
31170  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31171  */
31172 Roo.tree.TreeNode = function(attributes){
31173     attributes = attributes || {};
31174     if(typeof attributes == "string"){
31175         attributes = {text: attributes};
31176     }
31177     this.childrenRendered = false;
31178     this.rendered = false;
31179     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31180     this.expanded = attributes.expanded === true;
31181     this.isTarget = attributes.isTarget !== false;
31182     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31183     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31184
31185     /**
31186      * Read-only. The text for this node. To change it use setText().
31187      * @type String
31188      */
31189     this.text = attributes.text;
31190     /**
31191      * True if this node is disabled.
31192      * @type Boolean
31193      */
31194     this.disabled = attributes.disabled === true;
31195
31196     this.addEvents({
31197         /**
31198         * @event textchange
31199         * Fires when the text for this node is changed
31200         * @param {Node} this This node
31201         * @param {String} text The new text
31202         * @param {String} oldText The old text
31203         */
31204         "textchange" : true,
31205         /**
31206         * @event beforeexpand
31207         * Fires before this node is expanded, return false to cancel.
31208         * @param {Node} this This node
31209         * @param {Boolean} deep
31210         * @param {Boolean} anim
31211         */
31212         "beforeexpand" : true,
31213         /**
31214         * @event beforecollapse
31215         * Fires before this node is collapsed, return false to cancel.
31216         * @param {Node} this This node
31217         * @param {Boolean} deep
31218         * @param {Boolean} anim
31219         */
31220         "beforecollapse" : true,
31221         /**
31222         * @event expand
31223         * Fires when this node is expanded
31224         * @param {Node} this This node
31225         */
31226         "expand" : true,
31227         /**
31228         * @event disabledchange
31229         * Fires when the disabled status of this node changes
31230         * @param {Node} this This node
31231         * @param {Boolean} disabled
31232         */
31233         "disabledchange" : true,
31234         /**
31235         * @event collapse
31236         * Fires when this node is collapsed
31237         * @param {Node} this This node
31238         */
31239         "collapse" : true,
31240         /**
31241         * @event beforeclick
31242         * Fires before click processing. Return false to cancel the default action.
31243         * @param {Node} this This node
31244         * @param {Roo.EventObject} e The event object
31245         */
31246         "beforeclick":true,
31247         /**
31248         * @event checkchange
31249         * Fires when a node with a checkbox's checked property changes
31250         * @param {Node} this This node
31251         * @param {Boolean} checked
31252         */
31253         "checkchange":true,
31254         /**
31255         * @event click
31256         * Fires when this node is clicked
31257         * @param {Node} this This node
31258         * @param {Roo.EventObject} e The event object
31259         */
31260         "click":true,
31261         /**
31262         * @event dblclick
31263         * Fires when this node is double clicked
31264         * @param {Node} this This node
31265         * @param {Roo.EventObject} e The event object
31266         */
31267         "dblclick":true,
31268         /**
31269         * @event contextmenu
31270         * Fires when this node is right clicked
31271         * @param {Node} this This node
31272         * @param {Roo.EventObject} e The event object
31273         */
31274         "contextmenu":true,
31275         /**
31276         * @event beforechildrenrendered
31277         * Fires right before the child nodes for this node are rendered
31278         * @param {Node} this This node
31279         */
31280         "beforechildrenrendered":true
31281     });
31282
31283     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31284
31285     /**
31286      * Read-only. The UI for this node
31287      * @type TreeNodeUI
31288      */
31289     this.ui = new uiClass(this);
31290 };
31291 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31292     preventHScroll: true,
31293     /**
31294      * Returns true if this node is expanded
31295      * @return {Boolean}
31296      */
31297     isExpanded : function(){
31298         return this.expanded;
31299     },
31300
31301     /**
31302      * Returns the UI object for this node
31303      * @return {TreeNodeUI}
31304      */
31305     getUI : function(){
31306         return this.ui;
31307     },
31308
31309     // private override
31310     setFirstChild : function(node){
31311         var of = this.firstChild;
31312         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31313         if(this.childrenRendered && of && node != of){
31314             of.renderIndent(true, true);
31315         }
31316         if(this.rendered){
31317             this.renderIndent(true, true);
31318         }
31319     },
31320
31321     // private override
31322     setLastChild : function(node){
31323         var ol = this.lastChild;
31324         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31325         if(this.childrenRendered && ol && node != ol){
31326             ol.renderIndent(true, true);
31327         }
31328         if(this.rendered){
31329             this.renderIndent(true, true);
31330         }
31331     },
31332
31333     // these methods are overridden to provide lazy rendering support
31334     // private override
31335     appendChild : function(){
31336         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31337         if(node && this.childrenRendered){
31338             node.render();
31339         }
31340         this.ui.updateExpandIcon();
31341         return node;
31342     },
31343
31344     // private override
31345     removeChild : function(node){
31346         this.ownerTree.getSelectionModel().unselect(node);
31347         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31348         // if it's been rendered remove dom node
31349         if(this.childrenRendered){
31350             node.ui.remove();
31351         }
31352         if(this.childNodes.length < 1){
31353             this.collapse(false, false);
31354         }else{
31355             this.ui.updateExpandIcon();
31356         }
31357         if(!this.firstChild) {
31358             this.childrenRendered = false;
31359         }
31360         return node;
31361     },
31362
31363     // private override
31364     insertBefore : function(node, refNode){
31365         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31366         if(newNode && refNode && this.childrenRendered){
31367             node.render();
31368         }
31369         this.ui.updateExpandIcon();
31370         return newNode;
31371     },
31372
31373     /**
31374      * Sets the text for this node
31375      * @param {String} text
31376      */
31377     setText : function(text){
31378         var oldText = this.text;
31379         this.text = text;
31380         this.attributes.text = text;
31381         if(this.rendered){ // event without subscribing
31382             this.ui.onTextChange(this, text, oldText);
31383         }
31384         this.fireEvent("textchange", this, text, oldText);
31385     },
31386
31387     /**
31388      * Triggers selection of this node
31389      */
31390     select : function(){
31391         this.getOwnerTree().getSelectionModel().select(this);
31392     },
31393
31394     /**
31395      * Triggers deselection of this node
31396      */
31397     unselect : function(){
31398         this.getOwnerTree().getSelectionModel().unselect(this);
31399     },
31400
31401     /**
31402      * Returns true if this node is selected
31403      * @return {Boolean}
31404      */
31405     isSelected : function(){
31406         return this.getOwnerTree().getSelectionModel().isSelected(this);
31407     },
31408
31409     /**
31410      * Expand this node.
31411      * @param {Boolean} deep (optional) True to expand all children as well
31412      * @param {Boolean} anim (optional) false to cancel the default animation
31413      * @param {Function} callback (optional) A callback to be called when
31414      * expanding this node completes (does not wait for deep expand to complete).
31415      * Called with 1 parameter, this node.
31416      */
31417     expand : function(deep, anim, callback){
31418         if(!this.expanded){
31419             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31420                 return;
31421             }
31422             if(!this.childrenRendered){
31423                 this.renderChildren();
31424             }
31425             this.expanded = true;
31426             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31427                 this.ui.animExpand(function(){
31428                     this.fireEvent("expand", this);
31429                     if(typeof callback == "function"){
31430                         callback(this);
31431                     }
31432                     if(deep === true){
31433                         this.expandChildNodes(true);
31434                     }
31435                 }.createDelegate(this));
31436                 return;
31437             }else{
31438                 this.ui.expand();
31439                 this.fireEvent("expand", this);
31440                 if(typeof callback == "function"){
31441                     callback(this);
31442                 }
31443             }
31444         }else{
31445            if(typeof callback == "function"){
31446                callback(this);
31447            }
31448         }
31449         if(deep === true){
31450             this.expandChildNodes(true);
31451         }
31452     },
31453
31454     isHiddenRoot : function(){
31455         return this.isRoot && !this.getOwnerTree().rootVisible;
31456     },
31457
31458     /**
31459      * Collapse this node.
31460      * @param {Boolean} deep (optional) True to collapse all children as well
31461      * @param {Boolean} anim (optional) false to cancel the default animation
31462      */
31463     collapse : function(deep, anim){
31464         if(this.expanded && !this.isHiddenRoot()){
31465             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31466                 return;
31467             }
31468             this.expanded = false;
31469             if((this.getOwnerTree().animate && anim !== false) || anim){
31470                 this.ui.animCollapse(function(){
31471                     this.fireEvent("collapse", this);
31472                     if(deep === true){
31473                         this.collapseChildNodes(true);
31474                     }
31475                 }.createDelegate(this));
31476                 return;
31477             }else{
31478                 this.ui.collapse();
31479                 this.fireEvent("collapse", this);
31480             }
31481         }
31482         if(deep === true){
31483             var cs = this.childNodes;
31484             for(var i = 0, len = cs.length; i < len; i++) {
31485                 cs[i].collapse(true, false);
31486             }
31487         }
31488     },
31489
31490     // private
31491     delayedExpand : function(delay){
31492         if(!this.expandProcId){
31493             this.expandProcId = this.expand.defer(delay, this);
31494         }
31495     },
31496
31497     // private
31498     cancelExpand : function(){
31499         if(this.expandProcId){
31500             clearTimeout(this.expandProcId);
31501         }
31502         this.expandProcId = false;
31503     },
31504
31505     /**
31506      * Toggles expanded/collapsed state of the node
31507      */
31508     toggle : function(){
31509         if(this.expanded){
31510             this.collapse();
31511         }else{
31512             this.expand();
31513         }
31514     },
31515
31516     /**
31517      * Ensures all parent nodes are expanded
31518      */
31519     ensureVisible : function(callback){
31520         var tree = this.getOwnerTree();
31521         tree.expandPath(this.parentNode.getPath(), false, function(){
31522             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31523             Roo.callback(callback);
31524         }.createDelegate(this));
31525     },
31526
31527     /**
31528      * Expand all child nodes
31529      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31530      */
31531     expandChildNodes : function(deep){
31532         var cs = this.childNodes;
31533         for(var i = 0, len = cs.length; i < len; i++) {
31534                 cs[i].expand(deep);
31535         }
31536     },
31537
31538     /**
31539      * Collapse all child nodes
31540      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31541      */
31542     collapseChildNodes : function(deep){
31543         var cs = this.childNodes;
31544         for(var i = 0, len = cs.length; i < len; i++) {
31545                 cs[i].collapse(deep);
31546         }
31547     },
31548
31549     /**
31550      * Disables this node
31551      */
31552     disable : function(){
31553         this.disabled = true;
31554         this.unselect();
31555         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31556             this.ui.onDisableChange(this, true);
31557         }
31558         this.fireEvent("disabledchange", this, true);
31559     },
31560
31561     /**
31562      * Enables this node
31563      */
31564     enable : function(){
31565         this.disabled = false;
31566         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31567             this.ui.onDisableChange(this, false);
31568         }
31569         this.fireEvent("disabledchange", this, false);
31570     },
31571
31572     // private
31573     renderChildren : function(suppressEvent){
31574         if(suppressEvent !== false){
31575             this.fireEvent("beforechildrenrendered", this);
31576         }
31577         var cs = this.childNodes;
31578         for(var i = 0, len = cs.length; i < len; i++){
31579             cs[i].render(true);
31580         }
31581         this.childrenRendered = true;
31582     },
31583
31584     // private
31585     sort : function(fn, scope){
31586         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31587         if(this.childrenRendered){
31588             var cs = this.childNodes;
31589             for(var i = 0, len = cs.length; i < len; i++){
31590                 cs[i].render(true);
31591             }
31592         }
31593     },
31594
31595     // private
31596     render : function(bulkRender){
31597         this.ui.render(bulkRender);
31598         if(!this.rendered){
31599             this.rendered = true;
31600             if(this.expanded){
31601                 this.expanded = false;
31602                 this.expand(false, false);
31603             }
31604         }
31605     },
31606
31607     // private
31608     renderIndent : function(deep, refresh){
31609         if(refresh){
31610             this.ui.childIndent = null;
31611         }
31612         this.ui.renderIndent();
31613         if(deep === true && this.childrenRendered){
31614             var cs = this.childNodes;
31615             for(var i = 0, len = cs.length; i < len; i++){
31616                 cs[i].renderIndent(true, refresh);
31617             }
31618         }
31619     }
31620 });/*
31621  * Based on:
31622  * Ext JS Library 1.1.1
31623  * Copyright(c) 2006-2007, Ext JS, LLC.
31624  *
31625  * Originally Released Under LGPL - original licence link has changed is not relivant.
31626  *
31627  * Fork - LGPL
31628  * <script type="text/javascript">
31629  */
31630  
31631 /**
31632  * @class Roo.tree.AsyncTreeNode
31633  * @extends Roo.tree.TreeNode
31634  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31635  * @constructor
31636  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31637  */
31638  Roo.tree.AsyncTreeNode = function(config){
31639     this.loaded = false;
31640     this.loading = false;
31641     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31642     /**
31643     * @event beforeload
31644     * Fires before this node is loaded, return false to cancel
31645     * @param {Node} this This node
31646     */
31647     this.addEvents({'beforeload':true, 'load': true});
31648     /**
31649     * @event load
31650     * Fires when this node is loaded
31651     * @param {Node} this This node
31652     */
31653     /**
31654      * The loader used by this node (defaults to using the tree's defined loader)
31655      * @type TreeLoader
31656      * @property loader
31657      */
31658 };
31659 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31660     expand : function(deep, anim, callback){
31661         if(this.loading){ // if an async load is already running, waiting til it's done
31662             var timer;
31663             var f = function(){
31664                 if(!this.loading){ // done loading
31665                     clearInterval(timer);
31666                     this.expand(deep, anim, callback);
31667                 }
31668             }.createDelegate(this);
31669             timer = setInterval(f, 200);
31670             return;
31671         }
31672         if(!this.loaded){
31673             if(this.fireEvent("beforeload", this) === false){
31674                 return;
31675             }
31676             this.loading = true;
31677             this.ui.beforeLoad(this);
31678             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31679             if(loader){
31680                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31681                 return;
31682             }
31683         }
31684         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31685     },
31686     
31687     /**
31688      * Returns true if this node is currently loading
31689      * @return {Boolean}
31690      */
31691     isLoading : function(){
31692         return this.loading;  
31693     },
31694     
31695     loadComplete : function(deep, anim, callback){
31696         this.loading = false;
31697         this.loaded = true;
31698         this.ui.afterLoad(this);
31699         this.fireEvent("load", this);
31700         this.expand(deep, anim, callback);
31701     },
31702     
31703     /**
31704      * Returns true if this node has been loaded
31705      * @return {Boolean}
31706      */
31707     isLoaded : function(){
31708         return this.loaded;
31709     },
31710     
31711     hasChildNodes : function(){
31712         if(!this.isLeaf() && !this.loaded){
31713             return true;
31714         }else{
31715             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31716         }
31717     },
31718
31719     /**
31720      * Trigger a reload for this node
31721      * @param {Function} callback
31722      */
31723     reload : function(callback){
31724         this.collapse(false, false);
31725         while(this.firstChild){
31726             this.removeChild(this.firstChild);
31727         }
31728         this.childrenRendered = false;
31729         this.loaded = false;
31730         if(this.isHiddenRoot()){
31731             this.expanded = false;
31732         }
31733         this.expand(false, false, callback);
31734     }
31735 });/*
31736  * Based on:
31737  * Ext JS Library 1.1.1
31738  * Copyright(c) 2006-2007, Ext JS, LLC.
31739  *
31740  * Originally Released Under LGPL - original licence link has changed is not relivant.
31741  *
31742  * Fork - LGPL
31743  * <script type="text/javascript">
31744  */
31745  
31746 /**
31747  * @class Roo.tree.TreeNodeUI
31748  * @constructor
31749  * @param {Object} node The node to render
31750  * The TreeNode UI implementation is separate from the
31751  * tree implementation. Unless you are customizing the tree UI,
31752  * you should never have to use this directly.
31753  */
31754 Roo.tree.TreeNodeUI = function(node){
31755     this.node = node;
31756     this.rendered = false;
31757     this.animating = false;
31758     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31759 };
31760
31761 Roo.tree.TreeNodeUI.prototype = {
31762     removeChild : function(node){
31763         if(this.rendered){
31764             this.ctNode.removeChild(node.ui.getEl());
31765         }
31766     },
31767
31768     beforeLoad : function(){
31769          this.addClass("x-tree-node-loading");
31770     },
31771
31772     afterLoad : function(){
31773          this.removeClass("x-tree-node-loading");
31774     },
31775
31776     onTextChange : function(node, text, oldText){
31777         if(this.rendered){
31778             this.textNode.innerHTML = text;
31779         }
31780     },
31781
31782     onDisableChange : function(node, state){
31783         this.disabled = state;
31784         if(state){
31785             this.addClass("x-tree-node-disabled");
31786         }else{
31787             this.removeClass("x-tree-node-disabled");
31788         }
31789     },
31790
31791     onSelectedChange : function(state){
31792         if(state){
31793             this.focus();
31794             this.addClass("x-tree-selected");
31795         }else{
31796             //this.blur();
31797             this.removeClass("x-tree-selected");
31798         }
31799     },
31800
31801     onMove : function(tree, node, oldParent, newParent, index, refNode){
31802         this.childIndent = null;
31803         if(this.rendered){
31804             var targetNode = newParent.ui.getContainer();
31805             if(!targetNode){//target not rendered
31806                 this.holder = document.createElement("div");
31807                 this.holder.appendChild(this.wrap);
31808                 return;
31809             }
31810             var insertBefore = refNode ? refNode.ui.getEl() : null;
31811             if(insertBefore){
31812                 targetNode.insertBefore(this.wrap, insertBefore);
31813             }else{
31814                 targetNode.appendChild(this.wrap);
31815             }
31816             this.node.renderIndent(true);
31817         }
31818     },
31819
31820     addClass : function(cls){
31821         if(this.elNode){
31822             Roo.fly(this.elNode).addClass(cls);
31823         }
31824     },
31825
31826     removeClass : function(cls){
31827         if(this.elNode){
31828             Roo.fly(this.elNode).removeClass(cls);
31829         }
31830     },
31831
31832     remove : function(){
31833         if(this.rendered){
31834             this.holder = document.createElement("div");
31835             this.holder.appendChild(this.wrap);
31836         }
31837     },
31838
31839     fireEvent : function(){
31840         return this.node.fireEvent.apply(this.node, arguments);
31841     },
31842
31843     initEvents : function(){
31844         this.node.on("move", this.onMove, this);
31845         var E = Roo.EventManager;
31846         var a = this.anchor;
31847
31848         var el = Roo.fly(a, '_treeui');
31849
31850         if(Roo.isOpera){ // opera render bug ignores the CSS
31851             el.setStyle("text-decoration", "none");
31852         }
31853
31854         el.on("click", this.onClick, this);
31855         el.on("dblclick", this.onDblClick, this);
31856
31857         if(this.checkbox){
31858             Roo.EventManager.on(this.checkbox,
31859                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31860         }
31861
31862         el.on("contextmenu", this.onContextMenu, this);
31863
31864         var icon = Roo.fly(this.iconNode);
31865         icon.on("click", this.onClick, this);
31866         icon.on("dblclick", this.onDblClick, this);
31867         icon.on("contextmenu", this.onContextMenu, this);
31868         E.on(this.ecNode, "click", this.ecClick, this, true);
31869
31870         if(this.node.disabled){
31871             this.addClass("x-tree-node-disabled");
31872         }
31873         if(this.node.hidden){
31874             this.addClass("x-tree-node-disabled");
31875         }
31876         var ot = this.node.getOwnerTree();
31877         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31878         if(dd && (!this.node.isRoot || ot.rootVisible)){
31879             Roo.dd.Registry.register(this.elNode, {
31880                 node: this.node,
31881                 handles: this.getDDHandles(),
31882                 isHandle: false
31883             });
31884         }
31885     },
31886
31887     getDDHandles : function(){
31888         return [this.iconNode, this.textNode];
31889     },
31890
31891     hide : function(){
31892         if(this.rendered){
31893             this.wrap.style.display = "none";
31894         }
31895     },
31896
31897     show : function(){
31898         if(this.rendered){
31899             this.wrap.style.display = "";
31900         }
31901     },
31902
31903     onContextMenu : function(e){
31904         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31905             e.preventDefault();
31906             this.focus();
31907             this.fireEvent("contextmenu", this.node, e);
31908         }
31909     },
31910
31911     onClick : function(e){
31912         if(this.dropping){
31913             e.stopEvent();
31914             return;
31915         }
31916         if(this.fireEvent("beforeclick", this.node, e) !== false){
31917             if(!this.disabled && this.node.attributes.href){
31918                 this.fireEvent("click", this.node, e);
31919                 return;
31920             }
31921             e.preventDefault();
31922             if(this.disabled){
31923                 return;
31924             }
31925
31926             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31927                 this.node.toggle();
31928             }
31929
31930             this.fireEvent("click", this.node, e);
31931         }else{
31932             e.stopEvent();
31933         }
31934     },
31935
31936     onDblClick : function(e){
31937         e.preventDefault();
31938         if(this.disabled){
31939             return;
31940         }
31941         if(this.checkbox){
31942             this.toggleCheck();
31943         }
31944         if(!this.animating && this.node.hasChildNodes()){
31945             this.node.toggle();
31946         }
31947         this.fireEvent("dblclick", this.node, e);
31948     },
31949
31950     onCheckChange : function(){
31951         var checked = this.checkbox.checked;
31952         this.node.attributes.checked = checked;
31953         this.fireEvent('checkchange', this.node, checked);
31954     },
31955
31956     ecClick : function(e){
31957         if(!this.animating && this.node.hasChildNodes()){
31958             this.node.toggle();
31959         }
31960     },
31961
31962     startDrop : function(){
31963         this.dropping = true;
31964     },
31965
31966     // delayed drop so the click event doesn't get fired on a drop
31967     endDrop : function(){
31968        setTimeout(function(){
31969            this.dropping = false;
31970        }.createDelegate(this), 50);
31971     },
31972
31973     expand : function(){
31974         this.updateExpandIcon();
31975         this.ctNode.style.display = "";
31976     },
31977
31978     focus : function(){
31979         if(!this.node.preventHScroll){
31980             try{this.anchor.focus();
31981             }catch(e){}
31982         }else if(!Roo.isIE){
31983             try{
31984                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
31985                 var l = noscroll.scrollLeft;
31986                 this.anchor.focus();
31987                 noscroll.scrollLeft = l;
31988             }catch(e){}
31989         }
31990     },
31991
31992     toggleCheck : function(value){
31993         var cb = this.checkbox;
31994         if(cb){
31995             cb.checked = (value === undefined ? !cb.checked : value);
31996         }
31997     },
31998
31999     blur : function(){
32000         try{
32001             this.anchor.blur();
32002         }catch(e){}
32003     },
32004
32005     animExpand : function(callback){
32006         var ct = Roo.get(this.ctNode);
32007         ct.stopFx();
32008         if(!this.node.hasChildNodes()){
32009             this.updateExpandIcon();
32010             this.ctNode.style.display = "";
32011             Roo.callback(callback);
32012             return;
32013         }
32014         this.animating = true;
32015         this.updateExpandIcon();
32016
32017         ct.slideIn('t', {
32018            callback : function(){
32019                this.animating = false;
32020                Roo.callback(callback);
32021             },
32022             scope: this,
32023             duration: this.node.ownerTree.duration || .25
32024         });
32025     },
32026
32027     highlight : function(){
32028         var tree = this.node.getOwnerTree();
32029         Roo.fly(this.wrap).highlight(
32030             tree.hlColor || "C3DAF9",
32031             {endColor: tree.hlBaseColor}
32032         );
32033     },
32034
32035     collapse : function(){
32036         this.updateExpandIcon();
32037         this.ctNode.style.display = "none";
32038     },
32039
32040     animCollapse : function(callback){
32041         var ct = Roo.get(this.ctNode);
32042         ct.enableDisplayMode('block');
32043         ct.stopFx();
32044
32045         this.animating = true;
32046         this.updateExpandIcon();
32047
32048         ct.slideOut('t', {
32049             callback : function(){
32050                this.animating = false;
32051                Roo.callback(callback);
32052             },
32053             scope: this,
32054             duration: this.node.ownerTree.duration || .25
32055         });
32056     },
32057
32058     getContainer : function(){
32059         return this.ctNode;
32060     },
32061
32062     getEl : function(){
32063         return this.wrap;
32064     },
32065
32066     appendDDGhost : function(ghostNode){
32067         ghostNode.appendChild(this.elNode.cloneNode(true));
32068     },
32069
32070     getDDRepairXY : function(){
32071         return Roo.lib.Dom.getXY(this.iconNode);
32072     },
32073
32074     onRender : function(){
32075         this.render();
32076     },
32077
32078     render : function(bulkRender){
32079         var n = this.node, a = n.attributes;
32080         var targetNode = n.parentNode ?
32081               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32082
32083         if(!this.rendered){
32084             this.rendered = true;
32085
32086             this.renderElements(n, a, targetNode, bulkRender);
32087
32088             if(a.qtip){
32089                if(this.textNode.setAttributeNS){
32090                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32091                    if(a.qtipTitle){
32092                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32093                    }
32094                }else{
32095                    this.textNode.setAttribute("ext:qtip", a.qtip);
32096                    if(a.qtipTitle){
32097                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32098                    }
32099                }
32100             }else if(a.qtipCfg){
32101                 a.qtipCfg.target = Roo.id(this.textNode);
32102                 Roo.QuickTips.register(a.qtipCfg);
32103             }
32104             this.initEvents();
32105             if(!this.node.expanded){
32106                 this.updateExpandIcon();
32107             }
32108         }else{
32109             if(bulkRender === true) {
32110                 targetNode.appendChild(this.wrap);
32111             }
32112         }
32113     },
32114
32115     renderElements : function(n, a, targetNode, bulkRender){
32116         // add some indent caching, this helps performance when rendering a large tree
32117         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32118         var t = n.getOwnerTree();
32119         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32120         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32121         var cb = typeof a.checked == 'boolean';
32122         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32123         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32124             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32125             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32126             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32127             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32128             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32129              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32130                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32131             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32132             "</li>"];
32133
32134         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32135             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32136                                 n.nextSibling.ui.getEl(), buf.join(""));
32137         }else{
32138             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32139         }
32140
32141         this.elNode = this.wrap.childNodes[0];
32142         this.ctNode = this.wrap.childNodes[1];
32143         var cs = this.elNode.childNodes;
32144         this.indentNode = cs[0];
32145         this.ecNode = cs[1];
32146         this.iconNode = cs[2];
32147         var index = 3;
32148         if(cb){
32149             this.checkbox = cs[3];
32150             index++;
32151         }
32152         this.anchor = cs[index];
32153         this.textNode = cs[index].firstChild;
32154     },
32155
32156     getAnchor : function(){
32157         return this.anchor;
32158     },
32159
32160     getTextEl : function(){
32161         return this.textNode;
32162     },
32163
32164     getIconEl : function(){
32165         return this.iconNode;
32166     },
32167
32168     isChecked : function(){
32169         return this.checkbox ? this.checkbox.checked : false;
32170     },
32171
32172     updateExpandIcon : function(){
32173         if(this.rendered){
32174             var n = this.node, c1, c2;
32175             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32176             var hasChild = n.hasChildNodes();
32177             if(hasChild){
32178                 if(n.expanded){
32179                     cls += "-minus";
32180                     c1 = "x-tree-node-collapsed";
32181                     c2 = "x-tree-node-expanded";
32182                 }else{
32183                     cls += "-plus";
32184                     c1 = "x-tree-node-expanded";
32185                     c2 = "x-tree-node-collapsed";
32186                 }
32187                 if(this.wasLeaf){
32188                     this.removeClass("x-tree-node-leaf");
32189                     this.wasLeaf = false;
32190                 }
32191                 if(this.c1 != c1 || this.c2 != c2){
32192                     Roo.fly(this.elNode).replaceClass(c1, c2);
32193                     this.c1 = c1; this.c2 = c2;
32194                 }
32195             }else{
32196                 if(!this.wasLeaf){
32197                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32198                     delete this.c1;
32199                     delete this.c2;
32200                     this.wasLeaf = true;
32201                 }
32202             }
32203             var ecc = "x-tree-ec-icon "+cls;
32204             if(this.ecc != ecc){
32205                 this.ecNode.className = ecc;
32206                 this.ecc = ecc;
32207             }
32208         }
32209     },
32210
32211     getChildIndent : function(){
32212         if(!this.childIndent){
32213             var buf = [];
32214             var p = this.node;
32215             while(p){
32216                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32217                     if(!p.isLast()) {
32218                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32219                     } else {
32220                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32221                     }
32222                 }
32223                 p = p.parentNode;
32224             }
32225             this.childIndent = buf.join("");
32226         }
32227         return this.childIndent;
32228     },
32229
32230     renderIndent : function(){
32231         if(this.rendered){
32232             var indent = "";
32233             var p = this.node.parentNode;
32234             if(p){
32235                 indent = p.ui.getChildIndent();
32236             }
32237             if(this.indentMarkup != indent){ // don't rerender if not required
32238                 this.indentNode.innerHTML = indent;
32239                 this.indentMarkup = indent;
32240             }
32241             this.updateExpandIcon();
32242         }
32243     }
32244 };
32245
32246 Roo.tree.RootTreeNodeUI = function(){
32247     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32248 };
32249 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32250     render : function(){
32251         if(!this.rendered){
32252             var targetNode = this.node.ownerTree.innerCt.dom;
32253             this.node.expanded = true;
32254             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32255             this.wrap = this.ctNode = targetNode.firstChild;
32256         }
32257     },
32258     collapse : function(){
32259     },
32260     expand : function(){
32261     }
32262 });/*
32263  * Based on:
32264  * Ext JS Library 1.1.1
32265  * Copyright(c) 2006-2007, Ext JS, LLC.
32266  *
32267  * Originally Released Under LGPL - original licence link has changed is not relivant.
32268  *
32269  * Fork - LGPL
32270  * <script type="text/javascript">
32271  */
32272 /**
32273  * @class Roo.tree.TreeLoader
32274  * @extends Roo.util.Observable
32275  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32276  * nodes from a specified URL. The response must be a javascript Array definition
32277  * who's elements are node definition objects. eg:
32278  * <pre><code>
32279    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32280     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32281 </code></pre>
32282  * <br><br>
32283  * A server request is sent, and child nodes are loaded only when a node is expanded.
32284  * The loading node's id is passed to the server under the parameter name "node" to
32285  * enable the server to produce the correct child nodes.
32286  * <br><br>
32287  * To pass extra parameters, an event handler may be attached to the "beforeload"
32288  * event, and the parameters specified in the TreeLoader's baseParams property:
32289  * <pre><code>
32290     myTreeLoader.on("beforeload", function(treeLoader, node) {
32291         this.baseParams.category = node.attributes.category;
32292     }, this);
32293 </code></pre><
32294  * This would pass an HTTP parameter called "category" to the server containing
32295  * the value of the Node's "category" attribute.
32296  * @constructor
32297  * Creates a new Treeloader.
32298  * @param {Object} config A config object containing config properties.
32299  */
32300 Roo.tree.TreeLoader = function(config){
32301     this.baseParams = {};
32302     this.requestMethod = "POST";
32303     Roo.apply(this, config);
32304
32305     this.addEvents({
32306     
32307         /**
32308          * @event beforeload
32309          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32310          * @param {Object} This TreeLoader object.
32311          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32312          * @param {Object} callback The callback function specified in the {@link #load} call.
32313          */
32314         beforeload : true,
32315         /**
32316          * @event load
32317          * Fires when the node has been successfuly loaded.
32318          * @param {Object} This TreeLoader object.
32319          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32320          * @param {Object} response The response object containing the data from the server.
32321          */
32322         load : true,
32323         /**
32324          * @event loadexception
32325          * Fires if the network request failed.
32326          * @param {Object} This TreeLoader object.
32327          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32328          * @param {Object} response The response object containing the data from the server.
32329          */
32330         loadexception : true,
32331         /**
32332          * @event create
32333          * Fires before a node is created, enabling you to return custom Node types 
32334          * @param {Object} This TreeLoader object.
32335          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32336          */
32337         create : true
32338     });
32339
32340     Roo.tree.TreeLoader.superclass.constructor.call(this);
32341 };
32342
32343 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32344     /**
32345     * @cfg {String} dataUrl The URL from which to request a Json string which
32346     * specifies an array of node definition object representing the child nodes
32347     * to be loaded.
32348     */
32349     /**
32350     * @cfg {Object} baseParams (optional) An object containing properties which
32351     * specify HTTP parameters to be passed to each request for child nodes.
32352     */
32353     /**
32354     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32355     * created by this loader. If the attributes sent by the server have an attribute in this object,
32356     * they take priority.
32357     */
32358     /**
32359     * @cfg {Object} uiProviders (optional) An object containing properties which
32360     * 
32361     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32362     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32363     * <i>uiProvider</i> attribute of a returned child node is a string rather
32364     * than a reference to a TreeNodeUI implementation, this that string value
32365     * is used as a property name in the uiProviders object. You can define the provider named
32366     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32367     */
32368     uiProviders : {},
32369
32370     /**
32371     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32372     * child nodes before loading.
32373     */
32374     clearOnLoad : true,
32375
32376     /**
32377     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32378     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32379     * Grid query { data : [ .....] }
32380     */
32381     
32382     root : false,
32383      /**
32384     * @cfg {String} queryParam (optional) 
32385     * Name of the query as it will be passed on the querystring (defaults to 'node')
32386     * eg. the request will be ?node=[id]
32387     */
32388     
32389     
32390     queryParam: false,
32391     
32392     /**
32393      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32394      * This is called automatically when a node is expanded, but may be used to reload
32395      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32396      * @param {Roo.tree.TreeNode} node
32397      * @param {Function} callback
32398      */
32399     load : function(node, callback){
32400         if(this.clearOnLoad){
32401             while(node.firstChild){
32402                 node.removeChild(node.firstChild);
32403             }
32404         }
32405         if(node.attributes.children){ // preloaded json children
32406             var cs = node.attributes.children;
32407             for(var i = 0, len = cs.length; i < len; i++){
32408                 node.appendChild(this.createNode(cs[i]));
32409             }
32410             if(typeof callback == "function"){
32411                 callback();
32412             }
32413         }else if(this.dataUrl){
32414             this.requestData(node, callback);
32415         }
32416     },
32417
32418     getParams: function(node){
32419         var buf = [], bp = this.baseParams;
32420         for(var key in bp){
32421             if(typeof bp[key] != "function"){
32422                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32423             }
32424         }
32425         var n = this.queryParam === false ? 'node' : this.queryParam;
32426         buf.push(n + "=", encodeURIComponent(node.id));
32427         return buf.join("");
32428     },
32429
32430     requestData : function(node, callback){
32431         if(this.fireEvent("beforeload", this, node, callback) !== false){
32432             this.transId = Roo.Ajax.request({
32433                 method:this.requestMethod,
32434                 url: this.dataUrl||this.url,
32435                 success: this.handleResponse,
32436                 failure: this.handleFailure,
32437                 scope: this,
32438                 argument: {callback: callback, node: node},
32439                 params: this.getParams(node)
32440             });
32441         }else{
32442             // if the load is cancelled, make sure we notify
32443             // the node that we are done
32444             if(typeof callback == "function"){
32445                 callback();
32446             }
32447         }
32448     },
32449
32450     isLoading : function(){
32451         return this.transId ? true : false;
32452     },
32453
32454     abort : function(){
32455         if(this.isLoading()){
32456             Roo.Ajax.abort(this.transId);
32457         }
32458     },
32459
32460     // private
32461     createNode : function(attr){
32462         // apply baseAttrs, nice idea Corey!
32463         if(this.baseAttrs){
32464             Roo.applyIf(attr, this.baseAttrs);
32465         }
32466         if(this.applyLoader !== false){
32467             attr.loader = this;
32468         }
32469         // uiProvider = depreciated..
32470         
32471         if(typeof(attr.uiProvider) == 'string'){
32472            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32473                 /**  eval:var:attr */ eval(attr.uiProvider);
32474         }
32475         if(typeof(this.uiProviders['default']) != 'undefined') {
32476             attr.uiProvider = this.uiProviders['default'];
32477         }
32478         
32479         this.fireEvent('create', this, attr);
32480         
32481         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32482         return(attr.leaf ?
32483                         new Roo.tree.TreeNode(attr) :
32484                         new Roo.tree.AsyncTreeNode(attr));
32485     },
32486
32487     processResponse : function(response, node, callback){
32488         var json = response.responseText;
32489         try {
32490             
32491             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32492             if (this.root !== false) {
32493                 o = o[this.root];
32494             }
32495             
32496             for(var i = 0, len = o.length; i < len; i++){
32497                 var n = this.createNode(o[i]);
32498                 if(n){
32499                     node.appendChild(n);
32500                 }
32501             }
32502             if(typeof callback == "function"){
32503                 callback(this, node);
32504             }
32505         }catch(e){
32506             this.handleFailure(response);
32507         }
32508     },
32509
32510     handleResponse : function(response){
32511         this.transId = false;
32512         var a = response.argument;
32513         this.processResponse(response, a.node, a.callback);
32514         this.fireEvent("load", this, a.node, response);
32515     },
32516
32517     handleFailure : function(response){
32518         this.transId = false;
32519         var a = response.argument;
32520         this.fireEvent("loadexception", this, a.node, response);
32521         if(typeof a.callback == "function"){
32522             a.callback(this, a.node);
32523         }
32524     }
32525 });/*
32526  * Based on:
32527  * Ext JS Library 1.1.1
32528  * Copyright(c) 2006-2007, Ext JS, LLC.
32529  *
32530  * Originally Released Under LGPL - original licence link has changed is not relivant.
32531  *
32532  * Fork - LGPL
32533  * <script type="text/javascript">
32534  */
32535
32536 /**
32537 * @class Roo.tree.TreeFilter
32538 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32539 * @param {TreePanel} tree
32540 * @param {Object} config (optional)
32541  */
32542 Roo.tree.TreeFilter = function(tree, config){
32543     this.tree = tree;
32544     this.filtered = {};
32545     Roo.apply(this, config);
32546 };
32547
32548 Roo.tree.TreeFilter.prototype = {
32549     clearBlank:false,
32550     reverse:false,
32551     autoClear:false,
32552     remove:false,
32553
32554      /**
32555      * Filter the data by a specific attribute.
32556      * @param {String/RegExp} value Either string that the attribute value
32557      * should start with or a RegExp to test against the attribute
32558      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32559      * @param {TreeNode} startNode (optional) The node to start the filter at.
32560      */
32561     filter : function(value, attr, startNode){
32562         attr = attr || "text";
32563         var f;
32564         if(typeof value == "string"){
32565             var vlen = value.length;
32566             // auto clear empty filter
32567             if(vlen == 0 && this.clearBlank){
32568                 this.clear();
32569                 return;
32570             }
32571             value = value.toLowerCase();
32572             f = function(n){
32573                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32574             };
32575         }else if(value.exec){ // regex?
32576             f = function(n){
32577                 return value.test(n.attributes[attr]);
32578             };
32579         }else{
32580             throw 'Illegal filter type, must be string or regex';
32581         }
32582         this.filterBy(f, null, startNode);
32583         },
32584
32585     /**
32586      * Filter by a function. The passed function will be called with each
32587      * node in the tree (or from the startNode). If the function returns true, the node is kept
32588      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32589      * @param {Function} fn The filter function
32590      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32591      */
32592     filterBy : function(fn, scope, startNode){
32593         startNode = startNode || this.tree.root;
32594         if(this.autoClear){
32595             this.clear();
32596         }
32597         var af = this.filtered, rv = this.reverse;
32598         var f = function(n){
32599             if(n == startNode){
32600                 return true;
32601             }
32602             if(af[n.id]){
32603                 return false;
32604             }
32605             var m = fn.call(scope || n, n);
32606             if(!m || rv){
32607                 af[n.id] = n;
32608                 n.ui.hide();
32609                 return false;
32610             }
32611             return true;
32612         };
32613         startNode.cascade(f);
32614         if(this.remove){
32615            for(var id in af){
32616                if(typeof id != "function"){
32617                    var n = af[id];
32618                    if(n && n.parentNode){
32619                        n.parentNode.removeChild(n);
32620                    }
32621                }
32622            }
32623         }
32624     },
32625
32626     /**
32627      * Clears the current filter. Note: with the "remove" option
32628      * set a filter cannot be cleared.
32629      */
32630     clear : function(){
32631         var t = this.tree;
32632         var af = this.filtered;
32633         for(var id in af){
32634             if(typeof id != "function"){
32635                 var n = af[id];
32636                 if(n){
32637                     n.ui.show();
32638                 }
32639             }
32640         }
32641         this.filtered = {};
32642     }
32643 };
32644 /*
32645  * Based on:
32646  * Ext JS Library 1.1.1
32647  * Copyright(c) 2006-2007, Ext JS, LLC.
32648  *
32649  * Originally Released Under LGPL - original licence link has changed is not relivant.
32650  *
32651  * Fork - LGPL
32652  * <script type="text/javascript">
32653  */
32654  
32655
32656 /**
32657  * @class Roo.tree.TreeSorter
32658  * Provides sorting of nodes in a TreePanel
32659  * 
32660  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32661  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32662  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32663  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32664  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32665  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32666  * @constructor
32667  * @param {TreePanel} tree
32668  * @param {Object} config
32669  */
32670 Roo.tree.TreeSorter = function(tree, config){
32671     Roo.apply(this, config);
32672     tree.on("beforechildrenrendered", this.doSort, this);
32673     tree.on("append", this.updateSort, this);
32674     tree.on("insert", this.updateSort, this);
32675     
32676     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32677     var p = this.property || "text";
32678     var sortType = this.sortType;
32679     var fs = this.folderSort;
32680     var cs = this.caseSensitive === true;
32681     var leafAttr = this.leafAttr || 'leaf';
32682
32683     this.sortFn = function(n1, n2){
32684         if(fs){
32685             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32686                 return 1;
32687             }
32688             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32689                 return -1;
32690             }
32691         }
32692         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32693         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32694         if(v1 < v2){
32695                         return dsc ? +1 : -1;
32696                 }else if(v1 > v2){
32697                         return dsc ? -1 : +1;
32698         }else{
32699                 return 0;
32700         }
32701     };
32702 };
32703
32704 Roo.tree.TreeSorter.prototype = {
32705     doSort : function(node){
32706         node.sort(this.sortFn);
32707     },
32708     
32709     compareNodes : function(n1, n2){
32710         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32711     },
32712     
32713     updateSort : function(tree, node){
32714         if(node.childrenRendered){
32715             this.doSort.defer(1, this, [node]);
32716         }
32717     }
32718 };/*
32719  * Based on:
32720  * Ext JS Library 1.1.1
32721  * Copyright(c) 2006-2007, Ext JS, LLC.
32722  *
32723  * Originally Released Under LGPL - original licence link has changed is not relivant.
32724  *
32725  * Fork - LGPL
32726  * <script type="text/javascript">
32727  */
32728
32729 if(Roo.dd.DropZone){
32730     
32731 Roo.tree.TreeDropZone = function(tree, config){
32732     this.allowParentInsert = false;
32733     this.allowContainerDrop = false;
32734     this.appendOnly = false;
32735     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32736     this.tree = tree;
32737     this.lastInsertClass = "x-tree-no-status";
32738     this.dragOverData = {};
32739 };
32740
32741 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32742     ddGroup : "TreeDD",
32743     
32744     expandDelay : 1000,
32745     
32746     expandNode : function(node){
32747         if(node.hasChildNodes() && !node.isExpanded()){
32748             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32749         }
32750     },
32751     
32752     queueExpand : function(node){
32753         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32754     },
32755     
32756     cancelExpand : function(){
32757         if(this.expandProcId){
32758             clearTimeout(this.expandProcId);
32759             this.expandProcId = false;
32760         }
32761     },
32762     
32763     isValidDropPoint : function(n, pt, dd, e, data){
32764         if(!n || !data){ return false; }
32765         var targetNode = n.node;
32766         var dropNode = data.node;
32767         // default drop rules
32768         if(!(targetNode && targetNode.isTarget && pt)){
32769             return false;
32770         }
32771         if(pt == "append" && targetNode.allowChildren === false){
32772             return false;
32773         }
32774         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32775             return false;
32776         }
32777         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32778             return false;
32779         }
32780         // reuse the object
32781         var overEvent = this.dragOverData;
32782         overEvent.tree = this.tree;
32783         overEvent.target = targetNode;
32784         overEvent.data = data;
32785         overEvent.point = pt;
32786         overEvent.source = dd;
32787         overEvent.rawEvent = e;
32788         overEvent.dropNode = dropNode;
32789         overEvent.cancel = false;  
32790         var result = this.tree.fireEvent("nodedragover", overEvent);
32791         return overEvent.cancel === false && result !== false;
32792     },
32793     
32794     getDropPoint : function(e, n, dd){
32795         var tn = n.node;
32796         if(tn.isRoot){
32797             return tn.allowChildren !== false ? "append" : false; // always append for root
32798         }
32799         var dragEl = n.ddel;
32800         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32801         var y = Roo.lib.Event.getPageY(e);
32802         var noAppend = tn.allowChildren === false || tn.isLeaf();
32803         if(this.appendOnly || tn.parentNode.allowChildren === false){
32804             return noAppend ? false : "append";
32805         }
32806         var noBelow = false;
32807         if(!this.allowParentInsert){
32808             noBelow = tn.hasChildNodes() && tn.isExpanded();
32809         }
32810         var q = (b - t) / (noAppend ? 2 : 3);
32811         if(y >= t && y < (t + q)){
32812             return "above";
32813         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32814             return "below";
32815         }else{
32816             return "append";
32817         }
32818     },
32819     
32820     onNodeEnter : function(n, dd, e, data){
32821         this.cancelExpand();
32822     },
32823     
32824     onNodeOver : function(n, dd, e, data){
32825         var pt = this.getDropPoint(e, n, dd);
32826         var node = n.node;
32827         
32828         // auto node expand check
32829         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32830             this.queueExpand(node);
32831         }else if(pt != "append"){
32832             this.cancelExpand();
32833         }
32834         
32835         // set the insert point style on the target node
32836         var returnCls = this.dropNotAllowed;
32837         if(this.isValidDropPoint(n, pt, dd, e, data)){
32838            if(pt){
32839                var el = n.ddel;
32840                var cls;
32841                if(pt == "above"){
32842                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32843                    cls = "x-tree-drag-insert-above";
32844                }else if(pt == "below"){
32845                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32846                    cls = "x-tree-drag-insert-below";
32847                }else{
32848                    returnCls = "x-tree-drop-ok-append";
32849                    cls = "x-tree-drag-append";
32850                }
32851                if(this.lastInsertClass != cls){
32852                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32853                    this.lastInsertClass = cls;
32854                }
32855            }
32856        }
32857        return returnCls;
32858     },
32859     
32860     onNodeOut : function(n, dd, e, data){
32861         this.cancelExpand();
32862         this.removeDropIndicators(n);
32863     },
32864     
32865     onNodeDrop : function(n, dd, e, data){
32866         var point = this.getDropPoint(e, n, dd);
32867         var targetNode = n.node;
32868         targetNode.ui.startDrop();
32869         if(!this.isValidDropPoint(n, point, dd, e, data)){
32870             targetNode.ui.endDrop();
32871             return false;
32872         }
32873         // first try to find the drop node
32874         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32875         var dropEvent = {
32876             tree : this.tree,
32877             target: targetNode,
32878             data: data,
32879             point: point,
32880             source: dd,
32881             rawEvent: e,
32882             dropNode: dropNode,
32883             cancel: !dropNode   
32884         };
32885         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32886         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32887             targetNode.ui.endDrop();
32888             return false;
32889         }
32890         // allow target changing
32891         targetNode = dropEvent.target;
32892         if(point == "append" && !targetNode.isExpanded()){
32893             targetNode.expand(false, null, function(){
32894                 this.completeDrop(dropEvent);
32895             }.createDelegate(this));
32896         }else{
32897             this.completeDrop(dropEvent);
32898         }
32899         return true;
32900     },
32901     
32902     completeDrop : function(de){
32903         var ns = de.dropNode, p = de.point, t = de.target;
32904         if(!(ns instanceof Array)){
32905             ns = [ns];
32906         }
32907         var n;
32908         for(var i = 0, len = ns.length; i < len; i++){
32909             n = ns[i];
32910             if(p == "above"){
32911                 t.parentNode.insertBefore(n, t);
32912             }else if(p == "below"){
32913                 t.parentNode.insertBefore(n, t.nextSibling);
32914             }else{
32915                 t.appendChild(n);
32916             }
32917         }
32918         n.ui.focus();
32919         if(this.tree.hlDrop){
32920             n.ui.highlight();
32921         }
32922         t.ui.endDrop();
32923         this.tree.fireEvent("nodedrop", de);
32924     },
32925     
32926     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32927         if(this.tree.hlDrop){
32928             dropNode.ui.focus();
32929             dropNode.ui.highlight();
32930         }
32931         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32932     },
32933     
32934     getTree : function(){
32935         return this.tree;
32936     },
32937     
32938     removeDropIndicators : function(n){
32939         if(n && n.ddel){
32940             var el = n.ddel;
32941             Roo.fly(el).removeClass([
32942                     "x-tree-drag-insert-above",
32943                     "x-tree-drag-insert-below",
32944                     "x-tree-drag-append"]);
32945             this.lastInsertClass = "_noclass";
32946         }
32947     },
32948     
32949     beforeDragDrop : function(target, e, id){
32950         this.cancelExpand();
32951         return true;
32952     },
32953     
32954     afterRepair : function(data){
32955         if(data && Roo.enableFx){
32956             data.node.ui.highlight();
32957         }
32958         this.hideProxy();
32959     }    
32960 });
32961
32962 }/*
32963  * Based on:
32964  * Ext JS Library 1.1.1
32965  * Copyright(c) 2006-2007, Ext JS, LLC.
32966  *
32967  * Originally Released Under LGPL - original licence link has changed is not relivant.
32968  *
32969  * Fork - LGPL
32970  * <script type="text/javascript">
32971  */
32972  
32973
32974 if(Roo.dd.DragZone){
32975 Roo.tree.TreeDragZone = function(tree, config){
32976     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
32977     this.tree = tree;
32978 };
32979
32980 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
32981     ddGroup : "TreeDD",
32982     
32983     onBeforeDrag : function(data, e){
32984         var n = data.node;
32985         return n && n.draggable && !n.disabled;
32986     },
32987     
32988     onInitDrag : function(e){
32989         var data = this.dragData;
32990         this.tree.getSelectionModel().select(data.node);
32991         this.proxy.update("");
32992         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
32993         this.tree.fireEvent("startdrag", this.tree, data.node, e);
32994     },
32995     
32996     getRepairXY : function(e, data){
32997         return data.node.ui.getDDRepairXY();
32998     },
32999     
33000     onEndDrag : function(data, e){
33001         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33002     },
33003     
33004     onValidDrop : function(dd, e, id){
33005         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33006         this.hideProxy();
33007     },
33008     
33009     beforeInvalidDrop : function(e, id){
33010         // this scrolls the original position back into view
33011         var sm = this.tree.getSelectionModel();
33012         sm.clearSelections();
33013         sm.select(this.dragData.node);
33014     }
33015 });
33016 }/*
33017  * Based on:
33018  * Ext JS Library 1.1.1
33019  * Copyright(c) 2006-2007, Ext JS, LLC.
33020  *
33021  * Originally Released Under LGPL - original licence link has changed is not relivant.
33022  *
33023  * Fork - LGPL
33024  * <script type="text/javascript">
33025  */
33026 /**
33027  * @class Roo.tree.TreeEditor
33028  * @extends Roo.Editor
33029  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33030  * as the editor field.
33031  * @constructor
33032  * @param {TreePanel} tree
33033  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33034  */
33035 Roo.tree.TreeEditor = function(tree, config){
33036     config = config || {};
33037     var field = config.events ? config : new Roo.form.TextField(config);
33038     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33039
33040     this.tree = tree;
33041
33042     tree.on('beforeclick', this.beforeNodeClick, this);
33043     tree.getTreeEl().on('mousedown', this.hide, this);
33044     this.on('complete', this.updateNode, this);
33045     this.on('beforestartedit', this.fitToTree, this);
33046     this.on('startedit', this.bindScroll, this, {delay:10});
33047     this.on('specialkey', this.onSpecialKey, this);
33048 };
33049
33050 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33051     /**
33052      * @cfg {String} alignment
33053      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33054      */
33055     alignment: "l-l",
33056     // inherit
33057     autoSize: false,
33058     /**
33059      * @cfg {Boolean} hideEl
33060      * True to hide the bound element while the editor is displayed (defaults to false)
33061      */
33062     hideEl : false,
33063     /**
33064      * @cfg {String} cls
33065      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33066      */
33067     cls: "x-small-editor x-tree-editor",
33068     /**
33069      * @cfg {Boolean} shim
33070      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33071      */
33072     shim:false,
33073     // inherit
33074     shadow:"frame",
33075     /**
33076      * @cfg {Number} maxWidth
33077      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33078      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33079      * scroll and client offsets into account prior to each edit.
33080      */
33081     maxWidth: 250,
33082
33083     editDelay : 350,
33084
33085     // private
33086     fitToTree : function(ed, el){
33087         var td = this.tree.getTreeEl().dom, nd = el.dom;
33088         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33089             td.scrollLeft = nd.offsetLeft;
33090         }
33091         var w = Math.min(
33092                 this.maxWidth,
33093                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33094         this.setSize(w, '');
33095     },
33096
33097     // private
33098     triggerEdit : function(node){
33099         this.completeEdit();
33100         this.editNode = node;
33101         this.startEdit(node.ui.textNode, node.text);
33102     },
33103
33104     // private
33105     bindScroll : function(){
33106         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33107     },
33108
33109     // private
33110     beforeNodeClick : function(node, e){
33111         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33112         this.lastClick = new Date();
33113         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33114             e.stopEvent();
33115             this.triggerEdit(node);
33116             return false;
33117         }
33118     },
33119
33120     // private
33121     updateNode : function(ed, value){
33122         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33123         this.editNode.setText(value);
33124     },
33125
33126     // private
33127     onHide : function(){
33128         Roo.tree.TreeEditor.superclass.onHide.call(this);
33129         if(this.editNode){
33130             this.editNode.ui.focus();
33131         }
33132     },
33133
33134     // private
33135     onSpecialKey : function(field, e){
33136         var k = e.getKey();
33137         if(k == e.ESC){
33138             e.stopEvent();
33139             this.cancelEdit();
33140         }else if(k == e.ENTER && !e.hasModifier()){
33141             e.stopEvent();
33142             this.completeEdit();
33143         }
33144     }
33145 });//<Script type="text/javascript">
33146 /*
33147  * Based on:
33148  * Ext JS Library 1.1.1
33149  * Copyright(c) 2006-2007, Ext JS, LLC.
33150  *
33151  * Originally Released Under LGPL - original licence link has changed is not relivant.
33152  *
33153  * Fork - LGPL
33154  * <script type="text/javascript">
33155  */
33156  
33157 /**
33158  * Not documented??? - probably should be...
33159  */
33160
33161 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33162     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33163     
33164     renderElements : function(n, a, targetNode, bulkRender){
33165         //consel.log("renderElements?");
33166         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33167
33168         var t = n.getOwnerTree();
33169         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33170         
33171         var cols = t.columns;
33172         var bw = t.borderWidth;
33173         var c = cols[0];
33174         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33175          var cb = typeof a.checked == "boolean";
33176         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33177         var colcls = 'x-t-' + tid + '-c0';
33178         var buf = [
33179             '<li class="x-tree-node">',
33180             
33181                 
33182                 '<div class="x-tree-node-el ', a.cls,'">',
33183                     // extran...
33184                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33185                 
33186                 
33187                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33188                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33189                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33190                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33191                            (a.iconCls ? ' '+a.iconCls : ''),
33192                            '" unselectable="on" />',
33193                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33194                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33195                              
33196                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33197                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33198                             '<span unselectable="on" qtip="' + tx + '">',
33199                              tx,
33200                              '</span></a>' ,
33201                     '</div>',
33202                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33203                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33204                  ];
33205         
33206         for(var i = 1, len = cols.length; i < len; i++){
33207             c = cols[i];
33208             colcls = 'x-t-' + tid + '-c' +i;
33209             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33210             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33211                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33212                       "</div>");
33213          }
33214          
33215          buf.push(
33216             '</a>',
33217             '<div class="x-clear"></div></div>',
33218             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33219             "</li>");
33220         
33221         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33222             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33223                                 n.nextSibling.ui.getEl(), buf.join(""));
33224         }else{
33225             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33226         }
33227         var el = this.wrap.firstChild;
33228         this.elRow = el;
33229         this.elNode = el.firstChild;
33230         this.ranchor = el.childNodes[1];
33231         this.ctNode = this.wrap.childNodes[1];
33232         var cs = el.firstChild.childNodes;
33233         this.indentNode = cs[0];
33234         this.ecNode = cs[1];
33235         this.iconNode = cs[2];
33236         var index = 3;
33237         if(cb){
33238             this.checkbox = cs[3];
33239             index++;
33240         }
33241         this.anchor = cs[index];
33242         
33243         this.textNode = cs[index].firstChild;
33244         
33245         //el.on("click", this.onClick, this);
33246         //el.on("dblclick", this.onDblClick, this);
33247         
33248         
33249        // console.log(this);
33250     },
33251     initEvents : function(){
33252         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33253         
33254             
33255         var a = this.ranchor;
33256
33257         var el = Roo.get(a);
33258
33259         if(Roo.isOpera){ // opera render bug ignores the CSS
33260             el.setStyle("text-decoration", "none");
33261         }
33262
33263         el.on("click", this.onClick, this);
33264         el.on("dblclick", this.onDblClick, this);
33265         el.on("contextmenu", this.onContextMenu, this);
33266         
33267     },
33268     
33269     /*onSelectedChange : function(state){
33270         if(state){
33271             this.focus();
33272             this.addClass("x-tree-selected");
33273         }else{
33274             //this.blur();
33275             this.removeClass("x-tree-selected");
33276         }
33277     },*/
33278     addClass : function(cls){
33279         if(this.elRow){
33280             Roo.fly(this.elRow).addClass(cls);
33281         }
33282         
33283     },
33284     
33285     
33286     removeClass : function(cls){
33287         if(this.elRow){
33288             Roo.fly(this.elRow).removeClass(cls);
33289         }
33290     }
33291
33292     
33293     
33294 });//<Script type="text/javascript">
33295
33296 /*
33297  * Based on:
33298  * Ext JS Library 1.1.1
33299  * Copyright(c) 2006-2007, Ext JS, LLC.
33300  *
33301  * Originally Released Under LGPL - original licence link has changed is not relivant.
33302  *
33303  * Fork - LGPL
33304  * <script type="text/javascript">
33305  */
33306  
33307
33308 /**
33309  * @class Roo.tree.ColumnTree
33310  * @extends Roo.data.TreePanel
33311  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33312  * @cfg {int} borderWidth  compined right/left border allowance
33313  * @constructor
33314  * @param {String/HTMLElement/Element} el The container element
33315  * @param {Object} config
33316  */
33317 Roo.tree.ColumnTree =  function(el, config)
33318 {
33319    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33320    this.addEvents({
33321         /**
33322         * @event resize
33323         * Fire this event on a container when it resizes
33324         * @param {int} w Width
33325         * @param {int} h Height
33326         */
33327        "resize" : true
33328     });
33329     this.on('resize', this.onResize, this);
33330 };
33331
33332 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33333     //lines:false,
33334     
33335     
33336     borderWidth: Roo.isBorderBox ? 0 : 2, 
33337     headEls : false,
33338     
33339     render : function(){
33340         // add the header.....
33341        
33342         Roo.tree.ColumnTree.superclass.render.apply(this);
33343         
33344         this.el.addClass('x-column-tree');
33345         
33346         this.headers = this.el.createChild(
33347             {cls:'x-tree-headers'},this.innerCt.dom);
33348    
33349         var cols = this.columns, c;
33350         var totalWidth = 0;
33351         this.headEls = [];
33352         var  len = cols.length;
33353         for(var i = 0; i < len; i++){
33354              c = cols[i];
33355              totalWidth += c.width;
33356             this.headEls.push(this.headers.createChild({
33357                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33358                  cn: {
33359                      cls:'x-tree-hd-text',
33360                      html: c.header
33361                  },
33362                  style:'width:'+(c.width-this.borderWidth)+'px;'
33363              }));
33364         }
33365         this.headers.createChild({cls:'x-clear'});
33366         // prevent floats from wrapping when clipped
33367         this.headers.setWidth(totalWidth);
33368         //this.innerCt.setWidth(totalWidth);
33369         this.innerCt.setStyle({ overflow: 'auto' });
33370         this.onResize(this.width, this.height);
33371              
33372         
33373     },
33374     onResize : function(w,h)
33375     {
33376         this.height = h;
33377         this.width = w;
33378         // resize cols..
33379         this.innerCt.setWidth(this.width);
33380         this.innerCt.setHeight(this.height-20);
33381         
33382         // headers...
33383         var cols = this.columns, c;
33384         var totalWidth = 0;
33385         var expEl = false;
33386         var len = cols.length;
33387         for(var i = 0; i < len; i++){
33388             c = cols[i];
33389             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33390                 // it's the expander..
33391                 expEl  = this.headEls[i];
33392                 continue;
33393             }
33394             totalWidth += c.width;
33395             
33396         }
33397         if (expEl) {
33398             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33399         }
33400         this.headers.setWidth(w-20);
33401
33402         
33403         
33404         
33405     }
33406 });
33407 /*
33408  * Based on:
33409  * Ext JS Library 1.1.1
33410  * Copyright(c) 2006-2007, Ext JS, LLC.
33411  *
33412  * Originally Released Under LGPL - original licence link has changed is not relivant.
33413  *
33414  * Fork - LGPL
33415  * <script type="text/javascript">
33416  */
33417  
33418 /**
33419  * @class Roo.menu.Menu
33420  * @extends Roo.util.Observable
33421  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33422  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33423  * @constructor
33424  * Creates a new Menu
33425  * @param {Object} config Configuration options
33426  */
33427 Roo.menu.Menu = function(config){
33428     Roo.apply(this, config);
33429     this.id = this.id || Roo.id();
33430     this.addEvents({
33431         /**
33432          * @event beforeshow
33433          * Fires before this menu is displayed
33434          * @param {Roo.menu.Menu} this
33435          */
33436         beforeshow : true,
33437         /**
33438          * @event beforehide
33439          * Fires before this menu is hidden
33440          * @param {Roo.menu.Menu} this
33441          */
33442         beforehide : true,
33443         /**
33444          * @event show
33445          * Fires after this menu is displayed
33446          * @param {Roo.menu.Menu} this
33447          */
33448         show : true,
33449         /**
33450          * @event hide
33451          * Fires after this menu is hidden
33452          * @param {Roo.menu.Menu} this
33453          */
33454         hide : true,
33455         /**
33456          * @event click
33457          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33458          * @param {Roo.menu.Menu} this
33459          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33460          * @param {Roo.EventObject} e
33461          */
33462         click : true,
33463         /**
33464          * @event mouseover
33465          * Fires when the mouse is hovering over this menu
33466          * @param {Roo.menu.Menu} this
33467          * @param {Roo.EventObject} e
33468          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33469          */
33470         mouseover : true,
33471         /**
33472          * @event mouseout
33473          * Fires when the mouse exits this menu
33474          * @param {Roo.menu.Menu} this
33475          * @param {Roo.EventObject} e
33476          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33477          */
33478         mouseout : true,
33479         /**
33480          * @event itemclick
33481          * Fires when a menu item contained in this menu is clicked
33482          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33483          * @param {Roo.EventObject} e
33484          */
33485         itemclick: true
33486     });
33487     if (this.registerMenu) {
33488         Roo.menu.MenuMgr.register(this);
33489     }
33490     
33491     var mis = this.items;
33492     this.items = new Roo.util.MixedCollection();
33493     if(mis){
33494         this.add.apply(this, mis);
33495     }
33496 };
33497
33498 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33499     /**
33500      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33501      */
33502     minWidth : 120,
33503     /**
33504      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33505      * for bottom-right shadow (defaults to "sides")
33506      */
33507     shadow : "sides",
33508     /**
33509      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33510      * this menu (defaults to "tl-tr?")
33511      */
33512     subMenuAlign : "tl-tr?",
33513     /**
33514      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33515      * relative to its element of origin (defaults to "tl-bl?")
33516      */
33517     defaultAlign : "tl-bl?",
33518     /**
33519      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33520      */
33521     allowOtherMenus : false,
33522     /**
33523      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33524      */
33525     registerMenu : true,
33526
33527     hidden:true,
33528
33529     // private
33530     render : function(){
33531         if(this.el){
33532             return;
33533         }
33534         var el = this.el = new Roo.Layer({
33535             cls: "x-menu",
33536             shadow:this.shadow,
33537             constrain: false,
33538             parentEl: this.parentEl || document.body,
33539             zindex:15000
33540         });
33541
33542         this.keyNav = new Roo.menu.MenuNav(this);
33543
33544         if(this.plain){
33545             el.addClass("x-menu-plain");
33546         }
33547         if(this.cls){
33548             el.addClass(this.cls);
33549         }
33550         // generic focus element
33551         this.focusEl = el.createChild({
33552             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33553         });
33554         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33555         ul.on("click", this.onClick, this);
33556         ul.on("mouseover", this.onMouseOver, this);
33557         ul.on("mouseout", this.onMouseOut, this);
33558         this.items.each(function(item){
33559             var li = document.createElement("li");
33560             li.className = "x-menu-list-item";
33561             ul.dom.appendChild(li);
33562             item.render(li, this);
33563         }, this);
33564         this.ul = ul;
33565         this.autoWidth();
33566     },
33567
33568     // private
33569     autoWidth : function(){
33570         var el = this.el, ul = this.ul;
33571         if(!el){
33572             return;
33573         }
33574         var w = this.width;
33575         if(w){
33576             el.setWidth(w);
33577         }else if(Roo.isIE){
33578             el.setWidth(this.minWidth);
33579             var t = el.dom.offsetWidth; // force recalc
33580             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33581         }
33582     },
33583
33584     // private
33585     delayAutoWidth : function(){
33586         if(this.rendered){
33587             if(!this.awTask){
33588                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33589             }
33590             this.awTask.delay(20);
33591         }
33592     },
33593
33594     // private
33595     findTargetItem : function(e){
33596         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33597         if(t && t.menuItemId){
33598             return this.items.get(t.menuItemId);
33599         }
33600     },
33601
33602     // private
33603     onClick : function(e){
33604         var t;
33605         if(t = this.findTargetItem(e)){
33606             t.onClick(e);
33607             this.fireEvent("click", this, t, e);
33608         }
33609     },
33610
33611     // private
33612     setActiveItem : function(item, autoExpand){
33613         if(item != this.activeItem){
33614             if(this.activeItem){
33615                 this.activeItem.deactivate();
33616             }
33617             this.activeItem = item;
33618             item.activate(autoExpand);
33619         }else if(autoExpand){
33620             item.expandMenu();
33621         }
33622     },
33623
33624     // private
33625     tryActivate : function(start, step){
33626         var items = this.items;
33627         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33628             var item = items.get(i);
33629             if(!item.disabled && item.canActivate){
33630                 this.setActiveItem(item, false);
33631                 return item;
33632             }
33633         }
33634         return false;
33635     },
33636
33637     // private
33638     onMouseOver : function(e){
33639         var t;
33640         if(t = this.findTargetItem(e)){
33641             if(t.canActivate && !t.disabled){
33642                 this.setActiveItem(t, true);
33643             }
33644         }
33645         this.fireEvent("mouseover", this, e, t);
33646     },
33647
33648     // private
33649     onMouseOut : function(e){
33650         var t;
33651         if(t = this.findTargetItem(e)){
33652             if(t == this.activeItem && t.shouldDeactivate(e)){
33653                 this.activeItem.deactivate();
33654                 delete this.activeItem;
33655             }
33656         }
33657         this.fireEvent("mouseout", this, e, t);
33658     },
33659
33660     /**
33661      * Read-only.  Returns true if the menu is currently displayed, else false.
33662      * @type Boolean
33663      */
33664     isVisible : function(){
33665         return this.el && !this.hidden;
33666     },
33667
33668     /**
33669      * Displays this menu relative to another element
33670      * @param {String/HTMLElement/Roo.Element} element The element to align to
33671      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33672      * the element (defaults to this.defaultAlign)
33673      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33674      */
33675     show : function(el, pos, parentMenu){
33676         this.parentMenu = parentMenu;
33677         if(!this.el){
33678             this.render();
33679         }
33680         this.fireEvent("beforeshow", this);
33681         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33682     },
33683
33684     /**
33685      * Displays this menu at a specific xy position
33686      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33687      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33688      */
33689     showAt : function(xy, parentMenu, /* private: */_e){
33690         this.parentMenu = parentMenu;
33691         if(!this.el){
33692             this.render();
33693         }
33694         if(_e !== false){
33695             this.fireEvent("beforeshow", this);
33696             xy = this.el.adjustForConstraints(xy);
33697         }
33698         this.el.setXY(xy);
33699         this.el.show();
33700         this.hidden = false;
33701         this.focus();
33702         this.fireEvent("show", this);
33703     },
33704
33705     focus : function(){
33706         if(!this.hidden){
33707             this.doFocus.defer(50, this);
33708         }
33709     },
33710
33711     doFocus : function(){
33712         if(!this.hidden){
33713             this.focusEl.focus();
33714         }
33715     },
33716
33717     /**
33718      * Hides this menu and optionally all parent menus
33719      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33720      */
33721     hide : function(deep){
33722         if(this.el && this.isVisible()){
33723             this.fireEvent("beforehide", this);
33724             if(this.activeItem){
33725                 this.activeItem.deactivate();
33726                 this.activeItem = null;
33727             }
33728             this.el.hide();
33729             this.hidden = true;
33730             this.fireEvent("hide", this);
33731         }
33732         if(deep === true && this.parentMenu){
33733             this.parentMenu.hide(true);
33734         }
33735     },
33736
33737     /**
33738      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33739      * Any of the following are valid:
33740      * <ul>
33741      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33742      * <li>An HTMLElement object which will be converted to a menu item</li>
33743      * <li>A menu item config object that will be created as a new menu item</li>
33744      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33745      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33746      * </ul>
33747      * Usage:
33748      * <pre><code>
33749 // Create the menu
33750 var menu = new Roo.menu.Menu();
33751
33752 // Create a menu item to add by reference
33753 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33754
33755 // Add a bunch of items at once using different methods.
33756 // Only the last item added will be returned.
33757 var item = menu.add(
33758     menuItem,                // add existing item by ref
33759     'Dynamic Item',          // new TextItem
33760     '-',                     // new separator
33761     { text: 'Config Item' }  // new item by config
33762 );
33763 </code></pre>
33764      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33765      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33766      */
33767     add : function(){
33768         var a = arguments, l = a.length, item;
33769         for(var i = 0; i < l; i++){
33770             var el = a[i];
33771             if(el.render){ // some kind of Item
33772                 item = this.addItem(el);
33773             }else if(typeof el == "string"){ // string
33774                 if(el == "separator" || el == "-"){
33775                     item = this.addSeparator();
33776                 }else{
33777                     item = this.addText(el);
33778                 }
33779             }else if(el.tagName || el.el){ // element
33780                 item = this.addElement(el);
33781             }else if(typeof el == "object"){ // must be menu item config?
33782                 item = this.addMenuItem(el);
33783             }
33784         }
33785         return item;
33786     },
33787
33788     /**
33789      * Returns this menu's underlying {@link Roo.Element} object
33790      * @return {Roo.Element} The element
33791      */
33792     getEl : function(){
33793         if(!this.el){
33794             this.render();
33795         }
33796         return this.el;
33797     },
33798
33799     /**
33800      * Adds a separator bar to the menu
33801      * @return {Roo.menu.Item} The menu item that was added
33802      */
33803     addSeparator : function(){
33804         return this.addItem(new Roo.menu.Separator());
33805     },
33806
33807     /**
33808      * Adds an {@link Roo.Element} object to the menu
33809      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33810      * @return {Roo.menu.Item} The menu item that was added
33811      */
33812     addElement : function(el){
33813         return this.addItem(new Roo.menu.BaseItem(el));
33814     },
33815
33816     /**
33817      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33818      * @param {Roo.menu.Item} item The menu item to add
33819      * @return {Roo.menu.Item} The menu item that was added
33820      */
33821     addItem : function(item){
33822         this.items.add(item);
33823         if(this.ul){
33824             var li = document.createElement("li");
33825             li.className = "x-menu-list-item";
33826             this.ul.dom.appendChild(li);
33827             item.render(li, this);
33828             this.delayAutoWidth();
33829         }
33830         return item;
33831     },
33832
33833     /**
33834      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33835      * @param {Object} config A MenuItem config object
33836      * @return {Roo.menu.Item} The menu item that was added
33837      */
33838     addMenuItem : function(config){
33839         if(!(config instanceof Roo.menu.Item)){
33840             if(typeof config.checked == "boolean"){ // must be check menu item config?
33841                 config = new Roo.menu.CheckItem(config);
33842             }else{
33843                 config = new Roo.menu.Item(config);
33844             }
33845         }
33846         return this.addItem(config);
33847     },
33848
33849     /**
33850      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33851      * @param {String} text The text to display in the menu item
33852      * @return {Roo.menu.Item} The menu item that was added
33853      */
33854     addText : function(text){
33855         return this.addItem(new Roo.menu.TextItem(text));
33856     },
33857
33858     /**
33859      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33860      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33861      * @param {Roo.menu.Item} item The menu item to add
33862      * @return {Roo.menu.Item} The menu item that was added
33863      */
33864     insert : function(index, item){
33865         this.items.insert(index, item);
33866         if(this.ul){
33867             var li = document.createElement("li");
33868             li.className = "x-menu-list-item";
33869             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33870             item.render(li, this);
33871             this.delayAutoWidth();
33872         }
33873         return item;
33874     },
33875
33876     /**
33877      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33878      * @param {Roo.menu.Item} item The menu item to remove
33879      */
33880     remove : function(item){
33881         this.items.removeKey(item.id);
33882         item.destroy();
33883     },
33884
33885     /**
33886      * Removes and destroys all items in the menu
33887      */
33888     removeAll : function(){
33889         var f;
33890         while(f = this.items.first()){
33891             this.remove(f);
33892         }
33893     }
33894 });
33895
33896 // MenuNav is a private utility class used internally by the Menu
33897 Roo.menu.MenuNav = function(menu){
33898     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33899     this.scope = this.menu = menu;
33900 };
33901
33902 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33903     doRelay : function(e, h){
33904         var k = e.getKey();
33905         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33906             this.menu.tryActivate(0, 1);
33907             return false;
33908         }
33909         return h.call(this.scope || this, e, this.menu);
33910     },
33911
33912     up : function(e, m){
33913         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33914             m.tryActivate(m.items.length-1, -1);
33915         }
33916     },
33917
33918     down : function(e, m){
33919         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33920             m.tryActivate(0, 1);
33921         }
33922     },
33923
33924     right : function(e, m){
33925         if(m.activeItem){
33926             m.activeItem.expandMenu(true);
33927         }
33928     },
33929
33930     left : function(e, m){
33931         m.hide();
33932         if(m.parentMenu && m.parentMenu.activeItem){
33933             m.parentMenu.activeItem.activate();
33934         }
33935     },
33936
33937     enter : function(e, m){
33938         if(m.activeItem){
33939             e.stopPropagation();
33940             m.activeItem.onClick(e);
33941             m.fireEvent("click", this, m.activeItem);
33942             return true;
33943         }
33944     }
33945 });/*
33946  * Based on:
33947  * Ext JS Library 1.1.1
33948  * Copyright(c) 2006-2007, Ext JS, LLC.
33949  *
33950  * Originally Released Under LGPL - original licence link has changed is not relivant.
33951  *
33952  * Fork - LGPL
33953  * <script type="text/javascript">
33954  */
33955  
33956 /**
33957  * @class Roo.menu.MenuMgr
33958  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33959  * @singleton
33960  */
33961 Roo.menu.MenuMgr = function(){
33962    var menus, active, groups = {}, attached = false, lastShow = new Date();
33963
33964    // private - called when first menu is created
33965    function init(){
33966        menus = {};
33967        active = new Roo.util.MixedCollection();
33968        Roo.get(document).addKeyListener(27, function(){
33969            if(active.length > 0){
33970                hideAll();
33971            }
33972        });
33973    }
33974
33975    // private
33976    function hideAll(){
33977        if(active && active.length > 0){
33978            var c = active.clone();
33979            c.each(function(m){
33980                m.hide();
33981            });
33982        }
33983    }
33984
33985    // private
33986    function onHide(m){
33987        active.remove(m);
33988        if(active.length < 1){
33989            Roo.get(document).un("mousedown", onMouseDown);
33990            attached = false;
33991        }
33992    }
33993
33994    // private
33995    function onShow(m){
33996        var last = active.last();
33997        lastShow = new Date();
33998        active.add(m);
33999        if(!attached){
34000            Roo.get(document).on("mousedown", onMouseDown);
34001            attached = true;
34002        }
34003        if(m.parentMenu){
34004           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34005           m.parentMenu.activeChild = m;
34006        }else if(last && last.isVisible()){
34007           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34008        }
34009    }
34010
34011    // private
34012    function onBeforeHide(m){
34013        if(m.activeChild){
34014            m.activeChild.hide();
34015        }
34016        if(m.autoHideTimer){
34017            clearTimeout(m.autoHideTimer);
34018            delete m.autoHideTimer;
34019        }
34020    }
34021
34022    // private
34023    function onBeforeShow(m){
34024        var pm = m.parentMenu;
34025        if(!pm && !m.allowOtherMenus){
34026            hideAll();
34027        }else if(pm && pm.activeChild && active != m){
34028            pm.activeChild.hide();
34029        }
34030    }
34031
34032    // private
34033    function onMouseDown(e){
34034        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34035            hideAll();
34036        }
34037    }
34038
34039    // private
34040    function onBeforeCheck(mi, state){
34041        if(state){
34042            var g = groups[mi.group];
34043            for(var i = 0, l = g.length; i < l; i++){
34044                if(g[i] != mi){
34045                    g[i].setChecked(false);
34046                }
34047            }
34048        }
34049    }
34050
34051    return {
34052
34053        /**
34054         * Hides all menus that are currently visible
34055         */
34056        hideAll : function(){
34057             hideAll();  
34058        },
34059
34060        // private
34061        register : function(menu){
34062            if(!menus){
34063                init();
34064            }
34065            menus[menu.id] = menu;
34066            menu.on("beforehide", onBeforeHide);
34067            menu.on("hide", onHide);
34068            menu.on("beforeshow", onBeforeShow);
34069            menu.on("show", onShow);
34070            var g = menu.group;
34071            if(g && menu.events["checkchange"]){
34072                if(!groups[g]){
34073                    groups[g] = [];
34074                }
34075                groups[g].push(menu);
34076                menu.on("checkchange", onCheck);
34077            }
34078        },
34079
34080         /**
34081          * Returns a {@link Roo.menu.Menu} object
34082          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34083          * be used to generate and return a new Menu instance.
34084          */
34085        get : function(menu){
34086            if(typeof menu == "string"){ // menu id
34087                return menus[menu];
34088            }else if(menu.events){  // menu instance
34089                return menu;
34090            }else if(typeof menu.length == 'number'){ // array of menu items?
34091                return new Roo.menu.Menu({items:menu});
34092            }else{ // otherwise, must be a config
34093                return new Roo.menu.Menu(menu);
34094            }
34095        },
34096
34097        // private
34098        unregister : function(menu){
34099            delete menus[menu.id];
34100            menu.un("beforehide", onBeforeHide);
34101            menu.un("hide", onHide);
34102            menu.un("beforeshow", onBeforeShow);
34103            menu.un("show", onShow);
34104            var g = menu.group;
34105            if(g && menu.events["checkchange"]){
34106                groups[g].remove(menu);
34107                menu.un("checkchange", onCheck);
34108            }
34109        },
34110
34111        // private
34112        registerCheckable : function(menuItem){
34113            var g = menuItem.group;
34114            if(g){
34115                if(!groups[g]){
34116                    groups[g] = [];
34117                }
34118                groups[g].push(menuItem);
34119                menuItem.on("beforecheckchange", onBeforeCheck);
34120            }
34121        },
34122
34123        // private
34124        unregisterCheckable : function(menuItem){
34125            var g = menuItem.group;
34126            if(g){
34127                groups[g].remove(menuItem);
34128                menuItem.un("beforecheckchange", onBeforeCheck);
34129            }
34130        }
34131    };
34132 }();/*
34133  * Based on:
34134  * Ext JS Library 1.1.1
34135  * Copyright(c) 2006-2007, Ext JS, LLC.
34136  *
34137  * Originally Released Under LGPL - original licence link has changed is not relivant.
34138  *
34139  * Fork - LGPL
34140  * <script type="text/javascript">
34141  */
34142  
34143
34144 /**
34145  * @class Roo.menu.BaseItem
34146  * @extends Roo.Component
34147  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34148  * management and base configuration options shared by all menu components.
34149  * @constructor
34150  * Creates a new BaseItem
34151  * @param {Object} config Configuration options
34152  */
34153 Roo.menu.BaseItem = function(config){
34154     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34155
34156     this.addEvents({
34157         /**
34158          * @event click
34159          * Fires when this item is clicked
34160          * @param {Roo.menu.BaseItem} this
34161          * @param {Roo.EventObject} e
34162          */
34163         click: true,
34164         /**
34165          * @event activate
34166          * Fires when this item is activated
34167          * @param {Roo.menu.BaseItem} this
34168          */
34169         activate : true,
34170         /**
34171          * @event deactivate
34172          * Fires when this item is deactivated
34173          * @param {Roo.menu.BaseItem} this
34174          */
34175         deactivate : true
34176     });
34177
34178     if(this.handler){
34179         this.on("click", this.handler, this.scope, true);
34180     }
34181 };
34182
34183 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34184     /**
34185      * @cfg {Function} handler
34186      * A function that will handle the click event of this menu item (defaults to undefined)
34187      */
34188     /**
34189      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34190      */
34191     canActivate : false,
34192     /**
34193      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34194      */
34195     activeClass : "x-menu-item-active",
34196     /**
34197      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34198      */
34199     hideOnClick : true,
34200     /**
34201      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34202      */
34203     hideDelay : 100,
34204
34205     // private
34206     ctype: "Roo.menu.BaseItem",
34207
34208     // private
34209     actionMode : "container",
34210
34211     // private
34212     render : function(container, parentMenu){
34213         this.parentMenu = parentMenu;
34214         Roo.menu.BaseItem.superclass.render.call(this, container);
34215         this.container.menuItemId = this.id;
34216     },
34217
34218     // private
34219     onRender : function(container, position){
34220         this.el = Roo.get(this.el);
34221         container.dom.appendChild(this.el.dom);
34222     },
34223
34224     // private
34225     onClick : function(e){
34226         if(!this.disabled && this.fireEvent("click", this, e) !== false
34227                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34228             this.handleClick(e);
34229         }else{
34230             e.stopEvent();
34231         }
34232     },
34233
34234     // private
34235     activate : function(){
34236         if(this.disabled){
34237             return false;
34238         }
34239         var li = this.container;
34240         li.addClass(this.activeClass);
34241         this.region = li.getRegion().adjust(2, 2, -2, -2);
34242         this.fireEvent("activate", this);
34243         return true;
34244     },
34245
34246     // private
34247     deactivate : function(){
34248         this.container.removeClass(this.activeClass);
34249         this.fireEvent("deactivate", this);
34250     },
34251
34252     // private
34253     shouldDeactivate : function(e){
34254         return !this.region || !this.region.contains(e.getPoint());
34255     },
34256
34257     // private
34258     handleClick : function(e){
34259         if(this.hideOnClick){
34260             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34261         }
34262     },
34263
34264     // private
34265     expandMenu : function(autoActivate){
34266         // do nothing
34267     },
34268
34269     // private
34270     hideMenu : function(){
34271         // do nothing
34272     }
34273 });/*
34274  * Based on:
34275  * Ext JS Library 1.1.1
34276  * Copyright(c) 2006-2007, Ext JS, LLC.
34277  *
34278  * Originally Released Under LGPL - original licence link has changed is not relivant.
34279  *
34280  * Fork - LGPL
34281  * <script type="text/javascript">
34282  */
34283  
34284 /**
34285  * @class Roo.menu.Adapter
34286  * @extends Roo.menu.BaseItem
34287  * 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.
34288  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34289  * @constructor
34290  * Creates a new Adapter
34291  * @param {Object} config Configuration options
34292  */
34293 Roo.menu.Adapter = function(component, config){
34294     Roo.menu.Adapter.superclass.constructor.call(this, config);
34295     this.component = component;
34296 };
34297 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34298     // private
34299     canActivate : true,
34300
34301     // private
34302     onRender : function(container, position){
34303         this.component.render(container);
34304         this.el = this.component.getEl();
34305     },
34306
34307     // private
34308     activate : function(){
34309         if(this.disabled){
34310             return false;
34311         }
34312         this.component.focus();
34313         this.fireEvent("activate", this);
34314         return true;
34315     },
34316
34317     // private
34318     deactivate : function(){
34319         this.fireEvent("deactivate", this);
34320     },
34321
34322     // private
34323     disable : function(){
34324         this.component.disable();
34325         Roo.menu.Adapter.superclass.disable.call(this);
34326     },
34327
34328     // private
34329     enable : function(){
34330         this.component.enable();
34331         Roo.menu.Adapter.superclass.enable.call(this);
34332     }
34333 });/*
34334  * Based on:
34335  * Ext JS Library 1.1.1
34336  * Copyright(c) 2006-2007, Ext JS, LLC.
34337  *
34338  * Originally Released Under LGPL - original licence link has changed is not relivant.
34339  *
34340  * Fork - LGPL
34341  * <script type="text/javascript">
34342  */
34343
34344 /**
34345  * @class Roo.menu.TextItem
34346  * @extends Roo.menu.BaseItem
34347  * Adds a static text string to a menu, usually used as either a heading or group separator.
34348  * @constructor
34349  * Creates a new TextItem
34350  * @param {String} text The text to display
34351  */
34352 Roo.menu.TextItem = function(text){
34353     this.text = text;
34354     Roo.menu.TextItem.superclass.constructor.call(this);
34355 };
34356
34357 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34358     /**
34359      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34360      */
34361     hideOnClick : false,
34362     /**
34363      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34364      */
34365     itemCls : "x-menu-text",
34366
34367     // private
34368     onRender : function(){
34369         var s = document.createElement("span");
34370         s.className = this.itemCls;
34371         s.innerHTML = this.text;
34372         this.el = s;
34373         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34374     }
34375 });/*
34376  * Based on:
34377  * Ext JS Library 1.1.1
34378  * Copyright(c) 2006-2007, Ext JS, LLC.
34379  *
34380  * Originally Released Under LGPL - original licence link has changed is not relivant.
34381  *
34382  * Fork - LGPL
34383  * <script type="text/javascript">
34384  */
34385
34386 /**
34387  * @class Roo.menu.Separator
34388  * @extends Roo.menu.BaseItem
34389  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34390  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34391  * @constructor
34392  * @param {Object} config Configuration options
34393  */
34394 Roo.menu.Separator = function(config){
34395     Roo.menu.Separator.superclass.constructor.call(this, config);
34396 };
34397
34398 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34399     /**
34400      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34401      */
34402     itemCls : "x-menu-sep",
34403     /**
34404      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34405      */
34406     hideOnClick : false,
34407
34408     // private
34409     onRender : function(li){
34410         var s = document.createElement("span");
34411         s.className = this.itemCls;
34412         s.innerHTML = "&#160;";
34413         this.el = s;
34414         li.addClass("x-menu-sep-li");
34415         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34416     }
34417 });/*
34418  * Based on:
34419  * Ext JS Library 1.1.1
34420  * Copyright(c) 2006-2007, Ext JS, LLC.
34421  *
34422  * Originally Released Under LGPL - original licence link has changed is not relivant.
34423  *
34424  * Fork - LGPL
34425  * <script type="text/javascript">
34426  */
34427 /**
34428  * @class Roo.menu.Item
34429  * @extends Roo.menu.BaseItem
34430  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34431  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34432  * activation and click handling.
34433  * @constructor
34434  * Creates a new Item
34435  * @param {Object} config Configuration options
34436  */
34437 Roo.menu.Item = function(config){
34438     Roo.menu.Item.superclass.constructor.call(this, config);
34439     if(this.menu){
34440         this.menu = Roo.menu.MenuMgr.get(this.menu);
34441     }
34442 };
34443 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34444     /**
34445      * @cfg {String} icon
34446      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34447      */
34448     /**
34449      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34450      */
34451     itemCls : "x-menu-item",
34452     /**
34453      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34454      */
34455     canActivate : true,
34456     /**
34457      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34458      */
34459     showDelay: 200,
34460     // doc'd in BaseItem
34461     hideDelay: 200,
34462
34463     // private
34464     ctype: "Roo.menu.Item",
34465     
34466     // private
34467     onRender : function(container, position){
34468         var el = document.createElement("a");
34469         el.hideFocus = true;
34470         el.unselectable = "on";
34471         el.href = this.href || "#";
34472         if(this.hrefTarget){
34473             el.target = this.hrefTarget;
34474         }
34475         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34476         el.innerHTML = String.format(
34477                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34478                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34479         this.el = el;
34480         Roo.menu.Item.superclass.onRender.call(this, container, position);
34481     },
34482
34483     /**
34484      * Sets the text to display in this menu item
34485      * @param {String} text The text to display
34486      */
34487     setText : function(text){
34488         this.text = text;
34489         if(this.rendered){
34490             this.el.update(String.format(
34491                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34492                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34493             this.parentMenu.autoWidth();
34494         }
34495     },
34496
34497     // private
34498     handleClick : function(e){
34499         if(!this.href){ // if no link defined, stop the event automatically
34500             e.stopEvent();
34501         }
34502         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34503     },
34504
34505     // private
34506     activate : function(autoExpand){
34507         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34508             this.focus();
34509             if(autoExpand){
34510                 this.expandMenu();
34511             }
34512         }
34513         return true;
34514     },
34515
34516     // private
34517     shouldDeactivate : function(e){
34518         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34519             if(this.menu && this.menu.isVisible()){
34520                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34521             }
34522             return true;
34523         }
34524         return false;
34525     },
34526
34527     // private
34528     deactivate : function(){
34529         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34530         this.hideMenu();
34531     },
34532
34533     // private
34534     expandMenu : function(autoActivate){
34535         if(!this.disabled && this.menu){
34536             clearTimeout(this.hideTimer);
34537             delete this.hideTimer;
34538             if(!this.menu.isVisible() && !this.showTimer){
34539                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34540             }else if (this.menu.isVisible() && autoActivate){
34541                 this.menu.tryActivate(0, 1);
34542             }
34543         }
34544     },
34545
34546     // private
34547     deferExpand : function(autoActivate){
34548         delete this.showTimer;
34549         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34550         if(autoActivate){
34551             this.menu.tryActivate(0, 1);
34552         }
34553     },
34554
34555     // private
34556     hideMenu : function(){
34557         clearTimeout(this.showTimer);
34558         delete this.showTimer;
34559         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34560             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34561         }
34562     },
34563
34564     // private
34565     deferHide : function(){
34566         delete this.hideTimer;
34567         this.menu.hide();
34568     }
34569 });/*
34570  * Based on:
34571  * Ext JS Library 1.1.1
34572  * Copyright(c) 2006-2007, Ext JS, LLC.
34573  *
34574  * Originally Released Under LGPL - original licence link has changed is not relivant.
34575  *
34576  * Fork - LGPL
34577  * <script type="text/javascript">
34578  */
34579  
34580 /**
34581  * @class Roo.menu.CheckItem
34582  * @extends Roo.menu.Item
34583  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34584  * @constructor
34585  * Creates a new CheckItem
34586  * @param {Object} config Configuration options
34587  */
34588 Roo.menu.CheckItem = function(config){
34589     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34590     this.addEvents({
34591         /**
34592          * @event beforecheckchange
34593          * Fires before the checked value is set, providing an opportunity to cancel if needed
34594          * @param {Roo.menu.CheckItem} this
34595          * @param {Boolean} checked The new checked value that will be set
34596          */
34597         "beforecheckchange" : true,
34598         /**
34599          * @event checkchange
34600          * Fires after the checked value has been set
34601          * @param {Roo.menu.CheckItem} this
34602          * @param {Boolean} checked The checked value that was set
34603          */
34604         "checkchange" : true
34605     });
34606     if(this.checkHandler){
34607         this.on('checkchange', this.checkHandler, this.scope);
34608     }
34609 };
34610 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34611     /**
34612      * @cfg {String} group
34613      * All check items with the same group name will automatically be grouped into a single-select
34614      * radio button group (defaults to '')
34615      */
34616     /**
34617      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34618      */
34619     itemCls : "x-menu-item x-menu-check-item",
34620     /**
34621      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34622      */
34623     groupClass : "x-menu-group-item",
34624
34625     /**
34626      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34627      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34628      * initialized with checked = true will be rendered as checked.
34629      */
34630     checked: false,
34631
34632     // private
34633     ctype: "Roo.menu.CheckItem",
34634
34635     // private
34636     onRender : function(c){
34637         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34638         if(this.group){
34639             this.el.addClass(this.groupClass);
34640         }
34641         Roo.menu.MenuMgr.registerCheckable(this);
34642         if(this.checked){
34643             this.checked = false;
34644             this.setChecked(true, true);
34645         }
34646     },
34647
34648     // private
34649     destroy : function(){
34650         if(this.rendered){
34651             Roo.menu.MenuMgr.unregisterCheckable(this);
34652         }
34653         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34654     },
34655
34656     /**
34657      * Set the checked state of this item
34658      * @param {Boolean} checked The new checked value
34659      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34660      */
34661     setChecked : function(state, suppressEvent){
34662         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34663             if(this.container){
34664                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34665             }
34666             this.checked = state;
34667             if(suppressEvent !== true){
34668                 this.fireEvent("checkchange", this, state);
34669             }
34670         }
34671     },
34672
34673     // private
34674     handleClick : function(e){
34675        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34676            this.setChecked(!this.checked);
34677        }
34678        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34679     }
34680 });/*
34681  * Based on:
34682  * Ext JS Library 1.1.1
34683  * Copyright(c) 2006-2007, Ext JS, LLC.
34684  *
34685  * Originally Released Under LGPL - original licence link has changed is not relivant.
34686  *
34687  * Fork - LGPL
34688  * <script type="text/javascript">
34689  */
34690  
34691 /**
34692  * @class Roo.menu.DateItem
34693  * @extends Roo.menu.Adapter
34694  * A menu item that wraps the {@link Roo.DatPicker} component.
34695  * @constructor
34696  * Creates a new DateItem
34697  * @param {Object} config Configuration options
34698  */
34699 Roo.menu.DateItem = function(config){
34700     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34701     /** The Roo.DatePicker object @type Roo.DatePicker */
34702     this.picker = this.component;
34703     this.addEvents({select: true});
34704     
34705     this.picker.on("render", function(picker){
34706         picker.getEl().swallowEvent("click");
34707         picker.container.addClass("x-menu-date-item");
34708     });
34709
34710     this.picker.on("select", this.onSelect, this);
34711 };
34712
34713 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34714     // private
34715     onSelect : function(picker, date){
34716         this.fireEvent("select", this, date, picker);
34717         Roo.menu.DateItem.superclass.handleClick.call(this);
34718     }
34719 });/*
34720  * Based on:
34721  * Ext JS Library 1.1.1
34722  * Copyright(c) 2006-2007, Ext JS, LLC.
34723  *
34724  * Originally Released Under LGPL - original licence link has changed is not relivant.
34725  *
34726  * Fork - LGPL
34727  * <script type="text/javascript">
34728  */
34729  
34730 /**
34731  * @class Roo.menu.ColorItem
34732  * @extends Roo.menu.Adapter
34733  * A menu item that wraps the {@link Roo.ColorPalette} component.
34734  * @constructor
34735  * Creates a new ColorItem
34736  * @param {Object} config Configuration options
34737  */
34738 Roo.menu.ColorItem = function(config){
34739     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34740     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34741     this.palette = this.component;
34742     this.relayEvents(this.palette, ["select"]);
34743     if(this.selectHandler){
34744         this.on('select', this.selectHandler, this.scope);
34745     }
34746 };
34747 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
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
34759 /**
34760  * @class Roo.menu.DateMenu
34761  * @extends Roo.menu.Menu
34762  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34763  * @constructor
34764  * Creates a new DateMenu
34765  * @param {Object} config Configuration options
34766  */
34767 Roo.menu.DateMenu = function(config){
34768     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34769     this.plain = true;
34770     var di = new Roo.menu.DateItem(config);
34771     this.add(di);
34772     /**
34773      * The {@link Roo.DatePicker} instance for this DateMenu
34774      * @type DatePicker
34775      */
34776     this.picker = di.picker;
34777     /**
34778      * @event select
34779      * @param {DatePicker} picker
34780      * @param {Date} date
34781      */
34782     this.relayEvents(di, ["select"]);
34783
34784     this.on('beforeshow', function(){
34785         if(this.picker){
34786             this.picker.hideMonthPicker(true);
34787         }
34788     }, this);
34789 };
34790 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34791     cls:'x-date-menu'
34792 });/*
34793  * Based on:
34794  * Ext JS Library 1.1.1
34795  * Copyright(c) 2006-2007, Ext JS, LLC.
34796  *
34797  * Originally Released Under LGPL - original licence link has changed is not relivant.
34798  *
34799  * Fork - LGPL
34800  * <script type="text/javascript">
34801  */
34802  
34803
34804 /**
34805  * @class Roo.menu.ColorMenu
34806  * @extends Roo.menu.Menu
34807  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34808  * @constructor
34809  * Creates a new ColorMenu
34810  * @param {Object} config Configuration options
34811  */
34812 Roo.menu.ColorMenu = function(config){
34813     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34814     this.plain = true;
34815     var ci = new Roo.menu.ColorItem(config);
34816     this.add(ci);
34817     /**
34818      * The {@link Roo.ColorPalette} instance for this ColorMenu
34819      * @type ColorPalette
34820      */
34821     this.palette = ci.palette;
34822     /**
34823      * @event select
34824      * @param {ColorPalette} palette
34825      * @param {String} color
34826      */
34827     this.relayEvents(ci, ["select"]);
34828 };
34829 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34830  * Based on:
34831  * Ext JS Library 1.1.1
34832  * Copyright(c) 2006-2007, Ext JS, LLC.
34833  *
34834  * Originally Released Under LGPL - original licence link has changed is not relivant.
34835  *
34836  * Fork - LGPL
34837  * <script type="text/javascript">
34838  */
34839  
34840 /**
34841  * @class Roo.form.Field
34842  * @extends Roo.BoxComponent
34843  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34844  * @constructor
34845  * Creates a new Field
34846  * @param {Object} config Configuration options
34847  */
34848 Roo.form.Field = function(config){
34849     Roo.form.Field.superclass.constructor.call(this, config);
34850 };
34851
34852 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34853     /**
34854      * @cfg {String} fieldLabel Label to use when rendering a form.
34855      */
34856        /**
34857      * @cfg {String} qtip Mouse over tip
34858      */
34859      
34860     /**
34861      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34862      */
34863     invalidClass : "x-form-invalid",
34864     /**
34865      * @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")
34866      */
34867     invalidText : "The value in this field is invalid",
34868     /**
34869      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34870      */
34871     focusClass : "x-form-focus",
34872     /**
34873      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34874       automatic validation (defaults to "keyup").
34875      */
34876     validationEvent : "keyup",
34877     /**
34878      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34879      */
34880     validateOnBlur : true,
34881     /**
34882      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34883      */
34884     validationDelay : 250,
34885     /**
34886      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34887      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34888      */
34889     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34890     /**
34891      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34892      */
34893     fieldClass : "x-form-field",
34894     /**
34895      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34896      *<pre>
34897 Value         Description
34898 -----------   ----------------------------------------------------------------------
34899 qtip          Display a quick tip when the user hovers over the field
34900 title         Display a default browser title attribute popup
34901 under         Add a block div beneath the field containing the error text
34902 side          Add an error icon to the right of the field with a popup on hover
34903 [element id]  Add the error text directly to the innerHTML of the specified element
34904 </pre>
34905      */
34906     msgTarget : 'qtip',
34907     /**
34908      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34909      */
34910     msgFx : 'normal',
34911
34912     /**
34913      * @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.
34914      */
34915     readOnly : false,
34916
34917     /**
34918      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34919      */
34920     disabled : false,
34921
34922     /**
34923      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34924      */
34925     inputType : undefined,
34926     
34927     /**
34928      * @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).
34929          */
34930         tabIndex : undefined,
34931         
34932     // private
34933     isFormField : true,
34934
34935     // private
34936     hasFocus : false,
34937     /**
34938      * @property {Roo.Element} fieldEl
34939      * Element Containing the rendered Field (with label etc.)
34940      */
34941     /**
34942      * @cfg {Mixed} value A value to initialize this field with.
34943      */
34944     value : undefined,
34945
34946     /**
34947      * @cfg {String} name The field's HTML name attribute.
34948      */
34949     /**
34950      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34951      */
34952
34953         // private ??
34954         initComponent : function(){
34955         Roo.form.Field.superclass.initComponent.call(this);
34956         this.addEvents({
34957             /**
34958              * @event focus
34959              * Fires when this field receives input focus.
34960              * @param {Roo.form.Field} this
34961              */
34962             focus : true,
34963             /**
34964              * @event blur
34965              * Fires when this field loses input focus.
34966              * @param {Roo.form.Field} this
34967              */
34968             blur : true,
34969             /**
34970              * @event specialkey
34971              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
34972              * {@link Roo.EventObject#getKey} to determine which key was pressed.
34973              * @param {Roo.form.Field} this
34974              * @param {Roo.EventObject} e The event object
34975              */
34976             specialkey : true,
34977             /**
34978              * @event change
34979              * Fires just before the field blurs if the field value has changed.
34980              * @param {Roo.form.Field} this
34981              * @param {Mixed} newValue The new value
34982              * @param {Mixed} oldValue The original value
34983              */
34984             change : true,
34985             /**
34986              * @event invalid
34987              * Fires after the field has been marked as invalid.
34988              * @param {Roo.form.Field} this
34989              * @param {String} msg The validation message
34990              */
34991             invalid : true,
34992             /**
34993              * @event valid
34994              * Fires after the field has been validated with no errors.
34995              * @param {Roo.form.Field} this
34996              */
34997             valid : true
34998         });
34999     },
35000
35001     /**
35002      * Returns the name attribute of the field if available
35003      * @return {String} name The field name
35004      */
35005     getName: function(){
35006          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35007     },
35008
35009     // private
35010     onRender : function(ct, position){
35011         Roo.form.Field.superclass.onRender.call(this, ct, position);
35012         if(!this.el){
35013             var cfg = this.getAutoCreate();
35014             if(!cfg.name){
35015                 cfg.name = this.name || this.id;
35016             }
35017             if(this.inputType){
35018                 cfg.type = this.inputType;
35019             }
35020             this.el = ct.createChild(cfg, position);
35021         }
35022         var type = this.el.dom.type;
35023         if(type){
35024             if(type == 'password'){
35025                 type = 'text';
35026             }
35027             this.el.addClass('x-form-'+type);
35028         }
35029         if(this.readOnly){
35030             this.el.dom.readOnly = true;
35031         }
35032         if(this.tabIndex !== undefined){
35033             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35034         }
35035
35036         this.el.addClass([this.fieldClass, this.cls]);
35037         this.initValue();
35038     },
35039
35040     /**
35041      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35042      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35043      * @return {Roo.form.Field} this
35044      */
35045     applyTo : function(target){
35046         this.allowDomMove = false;
35047         this.el = Roo.get(target);
35048         this.render(this.el.dom.parentNode);
35049         return this;
35050     },
35051
35052     // private
35053     initValue : function(){
35054         if(this.value !== undefined){
35055             this.setValue(this.value);
35056         }else if(this.el.dom.value.length > 0){
35057             this.setValue(this.el.dom.value);
35058         }
35059     },
35060
35061     /**
35062      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35063      */
35064     isDirty : function() {
35065         if(this.disabled) {
35066             return false;
35067         }
35068         return String(this.getValue()) !== String(this.originalValue);
35069     },
35070
35071     // private
35072     afterRender : function(){
35073         Roo.form.Field.superclass.afterRender.call(this);
35074         this.initEvents();
35075     },
35076
35077     // private
35078     fireKey : function(e){
35079         if(e.isNavKeyPress()){
35080             this.fireEvent("specialkey", this, e);
35081         }
35082     },
35083
35084     /**
35085      * Resets the current field value to the originally loaded value and clears any validation messages
35086      */
35087     reset : function(){
35088         this.setValue(this.originalValue);
35089         this.clearInvalid();
35090     },
35091
35092     // private
35093     initEvents : function(){
35094         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35095         this.el.on("focus", this.onFocus,  this);
35096         this.el.on("blur", this.onBlur,  this);
35097
35098         // reference to original value for reset
35099         this.originalValue = this.getValue();
35100     },
35101
35102     // private
35103     onFocus : function(){
35104         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35105             this.el.addClass(this.focusClass);
35106         }
35107         if(!this.hasFocus){
35108             this.hasFocus = true;
35109             this.startValue = this.getValue();
35110             this.fireEvent("focus", this);
35111         }
35112     },
35113
35114     beforeBlur : Roo.emptyFn,
35115
35116     // private
35117     onBlur : function(){
35118         this.beforeBlur();
35119         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35120             this.el.removeClass(this.focusClass);
35121         }
35122         this.hasFocus = false;
35123         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35124             this.validate();
35125         }
35126         var v = this.getValue();
35127         if(String(v) !== String(this.startValue)){
35128             this.fireEvent('change', this, v, this.startValue);
35129         }
35130         this.fireEvent("blur", this);
35131     },
35132
35133     /**
35134      * Returns whether or not the field value is currently valid
35135      * @param {Boolean} preventMark True to disable marking the field invalid
35136      * @return {Boolean} True if the value is valid, else false
35137      */
35138     isValid : function(preventMark){
35139         if(this.disabled){
35140             return true;
35141         }
35142         var restore = this.preventMark;
35143         this.preventMark = preventMark === true;
35144         var v = this.validateValue(this.processValue(this.getRawValue()));
35145         this.preventMark = restore;
35146         return v;
35147     },
35148
35149     /**
35150      * Validates the field value
35151      * @return {Boolean} True if the value is valid, else false
35152      */
35153     validate : function(){
35154         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35155             this.clearInvalid();
35156             return true;
35157         }
35158         return false;
35159     },
35160
35161     processValue : function(value){
35162         return value;
35163     },
35164
35165     // private
35166     // Subclasses should provide the validation implementation by overriding this
35167     validateValue : function(value){
35168         return true;
35169     },
35170
35171     /**
35172      * Mark this field as invalid
35173      * @param {String} msg The validation message
35174      */
35175     markInvalid : function(msg){
35176         if(!this.rendered || this.preventMark){ // not rendered
35177             return;
35178         }
35179         this.el.addClass(this.invalidClass);
35180         msg = msg || this.invalidText;
35181         switch(this.msgTarget){
35182             case 'qtip':
35183                 this.el.dom.qtip = msg;
35184                 this.el.dom.qclass = 'x-form-invalid-tip';
35185                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35186                     Roo.QuickTips.enable();
35187                 }
35188                 break;
35189             case 'title':
35190                 this.el.dom.title = msg;
35191                 break;
35192             case 'under':
35193                 if(!this.errorEl){
35194                     var elp = this.el.findParent('.x-form-element', 5, true);
35195                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35196                     this.errorEl.setWidth(elp.getWidth(true)-20);
35197                 }
35198                 this.errorEl.update(msg);
35199                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35200                 break;
35201             case 'side':
35202                 if(!this.errorIcon){
35203                     var elp = this.el.findParent('.x-form-element', 5, true);
35204                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35205                 }
35206                 this.alignErrorIcon();
35207                 this.errorIcon.dom.qtip = msg;
35208                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35209                 this.errorIcon.show();
35210                 this.on('resize', this.alignErrorIcon, this);
35211                 break;
35212             default:
35213                 var t = Roo.getDom(this.msgTarget);
35214                 t.innerHTML = msg;
35215                 t.style.display = this.msgDisplay;
35216                 break;
35217         }
35218         this.fireEvent('invalid', this, msg);
35219     },
35220
35221     // private
35222     alignErrorIcon : function(){
35223         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35224     },
35225
35226     /**
35227      * Clear any invalid styles/messages for this field
35228      */
35229     clearInvalid : function(){
35230         if(!this.rendered || this.preventMark){ // not rendered
35231             return;
35232         }
35233         this.el.removeClass(this.invalidClass);
35234         switch(this.msgTarget){
35235             case 'qtip':
35236                 this.el.dom.qtip = '';
35237                 break;
35238             case 'title':
35239                 this.el.dom.title = '';
35240                 break;
35241             case 'under':
35242                 if(this.errorEl){
35243                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35244                 }
35245                 break;
35246             case 'side':
35247                 if(this.errorIcon){
35248                     this.errorIcon.dom.qtip = '';
35249                     this.errorIcon.hide();
35250                     this.un('resize', this.alignErrorIcon, this);
35251                 }
35252                 break;
35253             default:
35254                 var t = Roo.getDom(this.msgTarget);
35255                 t.innerHTML = '';
35256                 t.style.display = 'none';
35257                 break;
35258         }
35259         this.fireEvent('valid', this);
35260     },
35261
35262     /**
35263      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35264      * @return {Mixed} value The field value
35265      */
35266     getRawValue : function(){
35267         var v = this.el.getValue();
35268         if(v === this.emptyText){
35269             v = '';
35270         }
35271         return v;
35272     },
35273
35274     /**
35275      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35276      * @return {Mixed} value The field value
35277      */
35278     getValue : function(){
35279         var v = this.el.getValue();
35280         if(v === this.emptyText || v === undefined){
35281             v = '';
35282         }
35283         return v;
35284     },
35285
35286     /**
35287      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35288      * @param {Mixed} value The value to set
35289      */
35290     setRawValue : function(v){
35291         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35292     },
35293
35294     /**
35295      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35296      * @param {Mixed} value The value to set
35297      */
35298     setValue : function(v){
35299         this.value = v;
35300         if(this.rendered){
35301             this.el.dom.value = (v === null || v === undefined ? '' : v);
35302             this.validate();
35303         }
35304     },
35305
35306     adjustSize : function(w, h){
35307         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35308         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35309         return s;
35310     },
35311
35312     adjustWidth : function(tag, w){
35313         tag = tag.toLowerCase();
35314         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35315             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35316                 if(tag == 'input'){
35317                     return w + 2;
35318                 }
35319                 if(tag = 'textarea'){
35320                     return w-2;
35321                 }
35322             }else if(Roo.isOpera){
35323                 if(tag == 'input'){
35324                     return w + 2;
35325                 }
35326                 if(tag = 'textarea'){
35327                     return w-2;
35328                 }
35329             }
35330         }
35331         return w;
35332     }
35333 });
35334
35335
35336 // anything other than normal should be considered experimental
35337 Roo.form.Field.msgFx = {
35338     normal : {
35339         show: function(msgEl, f){
35340             msgEl.setDisplayed('block');
35341         },
35342
35343         hide : function(msgEl, f){
35344             msgEl.setDisplayed(false).update('');
35345         }
35346     },
35347
35348     slide : {
35349         show: function(msgEl, f){
35350             msgEl.slideIn('t', {stopFx:true});
35351         },
35352
35353         hide : function(msgEl, f){
35354             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35355         }
35356     },
35357
35358     slideRight : {
35359         show: function(msgEl, f){
35360             msgEl.fixDisplay();
35361             msgEl.alignTo(f.el, 'tl-tr');
35362             msgEl.slideIn('l', {stopFx:true});
35363         },
35364
35365         hide : function(msgEl, f){
35366             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35367         }
35368     }
35369 };/*
35370  * Based on:
35371  * Ext JS Library 1.1.1
35372  * Copyright(c) 2006-2007, Ext JS, LLC.
35373  *
35374  * Originally Released Under LGPL - original licence link has changed is not relivant.
35375  *
35376  * Fork - LGPL
35377  * <script type="text/javascript">
35378  */
35379  
35380
35381 /**
35382  * @class Roo.form.TextField
35383  * @extends Roo.form.Field
35384  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35385  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35386  * @constructor
35387  * Creates a new TextField
35388  * @param {Object} config Configuration options
35389  */
35390 Roo.form.TextField = function(config){
35391     Roo.form.TextField.superclass.constructor.call(this, config);
35392     this.addEvents({
35393         /**
35394          * @event autosize
35395          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35396          * according to the default logic, but this event provides a hook for the developer to apply additional
35397          * logic at runtime to resize the field if needed.
35398              * @param {Roo.form.Field} this This text field
35399              * @param {Number} width The new field width
35400              */
35401         autosize : true
35402     });
35403 };
35404
35405 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35406     /**
35407      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35408      */
35409     grow : false,
35410     /**
35411      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35412      */
35413     growMin : 30,
35414     /**
35415      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35416      */
35417     growMax : 800,
35418     /**
35419      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35420      */
35421     vtype : null,
35422     /**
35423      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35424      */
35425     maskRe : null,
35426     /**
35427      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35428      */
35429     disableKeyFilter : false,
35430     /**
35431      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35432      */
35433     allowBlank : true,
35434     /**
35435      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35436      */
35437     minLength : 0,
35438     /**
35439      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35440      */
35441     maxLength : Number.MAX_VALUE,
35442     /**
35443      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35444      */
35445     minLengthText : "The minimum length for this field is {0}",
35446     /**
35447      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35448      */
35449     maxLengthText : "The maximum length for this field is {0}",
35450     /**
35451      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35452      */
35453     selectOnFocus : false,
35454     /**
35455      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35456      */
35457     blankText : "This field is required",
35458     /**
35459      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35460      * If available, this function will be called only after the basic validators all return true, and will be passed the
35461      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35462      */
35463     validator : null,
35464     /**
35465      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35466      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35467      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35468      */
35469     regex : null,
35470     /**
35471      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35472      */
35473     regexText : "",
35474     /**
35475      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35476      */
35477     emptyText : null,
35478     /**
35479      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35480      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35481      */
35482     emptyClass : 'x-form-empty-field',
35483
35484     // private
35485     initEvents : function(){
35486         Roo.form.TextField.superclass.initEvents.call(this);
35487         if(this.validationEvent == 'keyup'){
35488             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35489             this.el.on('keyup', this.filterValidation, this);
35490         }
35491         else if(this.validationEvent !== false){
35492             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35493         }
35494         if(this.selectOnFocus || this.emptyText){
35495             this.on("focus", this.preFocus, this);
35496             if(this.emptyText){
35497                 this.on('blur', this.postBlur, this);
35498                 this.applyEmptyText();
35499             }
35500         }
35501         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35502             this.el.on("keypress", this.filterKeys, this);
35503         }
35504         if(this.grow){
35505             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35506             this.el.on("click", this.autoSize,  this);
35507         }
35508     },
35509
35510     processValue : function(value){
35511         if(this.stripCharsRe){
35512             var newValue = value.replace(this.stripCharsRe, '');
35513             if(newValue !== value){
35514                 this.setRawValue(newValue);
35515                 return newValue;
35516             }
35517         }
35518         return value;
35519     },
35520
35521     filterValidation : function(e){
35522         if(!e.isNavKeyPress()){
35523             this.validationTask.delay(this.validationDelay);
35524         }
35525     },
35526
35527     // private
35528     onKeyUp : function(e){
35529         if(!e.isNavKeyPress()){
35530             this.autoSize();
35531         }
35532     },
35533
35534     /**
35535      * Resets the current field value to the originally-loaded value and clears any validation messages.
35536      * Also adds emptyText and emptyClass if the original value was blank.
35537      */
35538     reset : function(){
35539         Roo.form.TextField.superclass.reset.call(this);
35540         this.applyEmptyText();
35541     },
35542
35543     applyEmptyText : function(){
35544         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35545             this.setRawValue(this.emptyText);
35546             this.el.addClass(this.emptyClass);
35547         }
35548     },
35549
35550     // private
35551     preFocus : function(){
35552         if(this.emptyText){
35553             if(this.el.dom.value == this.emptyText){
35554                 this.setRawValue('');
35555             }
35556             this.el.removeClass(this.emptyClass);
35557         }
35558         if(this.selectOnFocus){
35559             this.el.dom.select();
35560         }
35561     },
35562
35563     // private
35564     postBlur : function(){
35565         this.applyEmptyText();
35566     },
35567
35568     // private
35569     filterKeys : function(e){
35570         var k = e.getKey();
35571         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35572             return;
35573         }
35574         var c = e.getCharCode(), cc = String.fromCharCode(c);
35575         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35576             return;
35577         }
35578         if(!this.maskRe.test(cc)){
35579             e.stopEvent();
35580         }
35581     },
35582
35583     setValue : function(v){
35584         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35585             this.el.removeClass(this.emptyClass);
35586         }
35587         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35588         this.applyEmptyText();
35589         this.autoSize();
35590     },
35591
35592     /**
35593      * Validates a value according to the field's validation rules and marks the field as invalid
35594      * if the validation fails
35595      * @param {Mixed} value The value to validate
35596      * @return {Boolean} True if the value is valid, else false
35597      */
35598     validateValue : function(value){
35599         if(value.length < 1 || value === this.emptyText){ // if it's blank
35600              if(this.allowBlank){
35601                 this.clearInvalid();
35602                 return true;
35603              }else{
35604                 this.markInvalid(this.blankText);
35605                 return false;
35606              }
35607         }
35608         if(value.length < this.minLength){
35609             this.markInvalid(String.format(this.minLengthText, this.minLength));
35610             return false;
35611         }
35612         if(value.length > this.maxLength){
35613             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35614             return false;
35615         }
35616         if(this.vtype){
35617             var vt = Roo.form.VTypes;
35618             if(!vt[this.vtype](value, this)){
35619                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35620                 return false;
35621             }
35622         }
35623         if(typeof this.validator == "function"){
35624             var msg = this.validator(value);
35625             if(msg !== true){
35626                 this.markInvalid(msg);
35627                 return false;
35628             }
35629         }
35630         if(this.regex && !this.regex.test(value)){
35631             this.markInvalid(this.regexText);
35632             return false;
35633         }
35634         return true;
35635     },
35636
35637     /**
35638      * Selects text in this field
35639      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35640      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35641      */
35642     selectText : function(start, end){
35643         var v = this.getRawValue();
35644         if(v.length > 0){
35645             start = start === undefined ? 0 : start;
35646             end = end === undefined ? v.length : end;
35647             var d = this.el.dom;
35648             if(d.setSelectionRange){
35649                 d.setSelectionRange(start, end);
35650             }else if(d.createTextRange){
35651                 var range = d.createTextRange();
35652                 range.moveStart("character", start);
35653                 range.moveEnd("character", v.length-end);
35654                 range.select();
35655             }
35656         }
35657     },
35658
35659     /**
35660      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35661      * This only takes effect if grow = true, and fires the autosize event.
35662      */
35663     autoSize : function(){
35664         if(!this.grow || !this.rendered){
35665             return;
35666         }
35667         if(!this.metrics){
35668             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35669         }
35670         var el = this.el;
35671         var v = el.dom.value;
35672         var d = document.createElement('div');
35673         d.appendChild(document.createTextNode(v));
35674         v = d.innerHTML;
35675         d = null;
35676         v += "&#160;";
35677         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35678         this.el.setWidth(w);
35679         this.fireEvent("autosize", this, w);
35680     }
35681 });/*
35682  * Based on:
35683  * Ext JS Library 1.1.1
35684  * Copyright(c) 2006-2007, Ext JS, LLC.
35685  *
35686  * Originally Released Under LGPL - original licence link has changed is not relivant.
35687  *
35688  * Fork - LGPL
35689  * <script type="text/javascript">
35690  */
35691  
35692 /**
35693  * @class Roo.form.Hidden
35694  * @extends Roo.form.TextField
35695  * Simple Hidden element used on forms 
35696  * 
35697  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35698  * 
35699  * @constructor
35700  * Creates a new Hidden form element.
35701  * @param {Object} config Configuration options
35702  */
35703
35704
35705
35706 // easy hidden field...
35707 Roo.form.Hidden = function(config){
35708     Roo.form.Hidden.superclass.constructor.call(this, config);
35709 };
35710   
35711 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35712     fieldLabel:      '',
35713     inputType:      'hidden',
35714     width:          50,
35715     allowBlank:     true,
35716     labelSeparator: '',
35717     hidden:         true,
35718     itemCls :       'x-form-item-display-none'
35719
35720
35721 });
35722
35723
35724 /*
35725  * Based on:
35726  * Ext JS Library 1.1.1
35727  * Copyright(c) 2006-2007, Ext JS, LLC.
35728  *
35729  * Originally Released Under LGPL - original licence link has changed is not relivant.
35730  *
35731  * Fork - LGPL
35732  * <script type="text/javascript">
35733  */
35734  
35735 /**
35736  * @class Roo.form.TriggerField
35737  * @extends Roo.form.TextField
35738  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35739  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35740  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35741  * for which you can provide a custom implementation.  For example:
35742  * <pre><code>
35743 var trigger = new Roo.form.TriggerField();
35744 trigger.onTriggerClick = myTriggerFn;
35745 trigger.applyTo('my-field');
35746 </code></pre>
35747  *
35748  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35749  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35750  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35751  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35752  * @constructor
35753  * Create a new TriggerField.
35754  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35755  * to the base TextField)
35756  */
35757 Roo.form.TriggerField = function(config){
35758     this.mimicing = false;
35759     Roo.form.TriggerField.superclass.constructor.call(this, config);
35760 };
35761
35762 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35763     /**
35764      * @cfg {String} triggerClass A CSS class to apply to the trigger
35765      */
35766     /**
35767      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35768      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35769      */
35770     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35771     /**
35772      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35773      */
35774     hideTrigger:false,
35775
35776     /** @cfg {Boolean} grow @hide */
35777     /** @cfg {Number} growMin @hide */
35778     /** @cfg {Number} growMax @hide */
35779
35780     /**
35781      * @hide 
35782      * @method
35783      */
35784     autoSize: Roo.emptyFn,
35785     // private
35786     monitorTab : true,
35787     // private
35788     deferHeight : true,
35789
35790     
35791     actionMode : 'wrap',
35792     // private
35793     onResize : function(w, h){
35794         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35795         if(typeof w == 'number'){
35796             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35797         }
35798     },
35799
35800     // private
35801     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35802
35803     // private
35804     getResizeEl : function(){
35805         return this.wrap;
35806     },
35807
35808     // private
35809     getPositionEl : function(){
35810         return this.wrap;
35811     },
35812
35813     // private
35814     alignErrorIcon : function(){
35815         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35816     },
35817
35818     // private
35819     onRender : function(ct, position){
35820         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35821         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35822         this.trigger = this.wrap.createChild(this.triggerConfig ||
35823                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35824         if(this.hideTrigger){
35825             this.trigger.setDisplayed(false);
35826         }
35827         this.initTrigger();
35828         if(!this.width){
35829             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35830         }
35831     },
35832
35833     // private
35834     initTrigger : function(){
35835         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35836         this.trigger.addClassOnOver('x-form-trigger-over');
35837         this.trigger.addClassOnClick('x-form-trigger-click');
35838     },
35839
35840     // private
35841     onDestroy : function(){
35842         if(this.trigger){
35843             this.trigger.removeAllListeners();
35844             this.trigger.remove();
35845         }
35846         if(this.wrap){
35847             this.wrap.remove();
35848         }
35849         Roo.form.TriggerField.superclass.onDestroy.call(this);
35850     },
35851
35852     // private
35853     onFocus : function(){
35854         Roo.form.TriggerField.superclass.onFocus.call(this);
35855         if(!this.mimicing){
35856             this.wrap.addClass('x-trigger-wrap-focus');
35857             this.mimicing = true;
35858             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35859             if(this.monitorTab){
35860                 this.el.on("keydown", this.checkTab, this);
35861             }
35862         }
35863     },
35864
35865     // private
35866     checkTab : function(e){
35867         if(e.getKey() == e.TAB){
35868             this.triggerBlur();
35869         }
35870     },
35871
35872     // private
35873     onBlur : function(){
35874         // do nothing
35875     },
35876
35877     // private
35878     mimicBlur : function(e, t){
35879         if(!this.wrap.contains(t) && this.validateBlur()){
35880             this.triggerBlur();
35881         }
35882     },
35883
35884     // private
35885     triggerBlur : function(){
35886         this.mimicing = false;
35887         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35888         if(this.monitorTab){
35889             this.el.un("keydown", this.checkTab, this);
35890         }
35891         this.wrap.removeClass('x-trigger-wrap-focus');
35892         Roo.form.TriggerField.superclass.onBlur.call(this);
35893     },
35894
35895     // private
35896     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35897     validateBlur : function(e, t){
35898         return true;
35899     },
35900
35901     // private
35902     onDisable : function(){
35903         Roo.form.TriggerField.superclass.onDisable.call(this);
35904         if(this.wrap){
35905             this.wrap.addClass('x-item-disabled');
35906         }
35907     },
35908
35909     // private
35910     onEnable : function(){
35911         Roo.form.TriggerField.superclass.onEnable.call(this);
35912         if(this.wrap){
35913             this.wrap.removeClass('x-item-disabled');
35914         }
35915     },
35916
35917     // private
35918     onShow : function(){
35919         var ae = this.getActionEl();
35920         
35921         if(ae){
35922             ae.dom.style.display = '';
35923             ae.dom.style.visibility = 'visible';
35924         }
35925     },
35926
35927     // private
35928     
35929     onHide : function(){
35930         var ae = this.getActionEl();
35931         ae.dom.style.display = 'none';
35932     },
35933
35934     /**
35935      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35936      * by an implementing function.
35937      * @method
35938      * @param {EventObject} e
35939      */
35940     onTriggerClick : Roo.emptyFn
35941 });
35942
35943 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35944 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35945 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35946 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35947     initComponent : function(){
35948         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35949
35950         this.triggerConfig = {
35951             tag:'span', cls:'x-form-twin-triggers', cn:[
35952             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35953             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35954         ]};
35955     },
35956
35957     getTrigger : function(index){
35958         return this.triggers[index];
35959     },
35960
35961     initTrigger : function(){
35962         var ts = this.trigger.select('.x-form-trigger', true);
35963         this.wrap.setStyle('overflow', 'hidden');
35964         var triggerField = this;
35965         ts.each(function(t, all, index){
35966             t.hide = function(){
35967                 var w = triggerField.wrap.getWidth();
35968                 this.dom.style.display = 'none';
35969                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35970             };
35971             t.show = function(){
35972                 var w = triggerField.wrap.getWidth();
35973                 this.dom.style.display = '';
35974                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35975             };
35976             var triggerIndex = 'Trigger'+(index+1);
35977
35978             if(this['hide'+triggerIndex]){
35979                 t.dom.style.display = 'none';
35980             }
35981             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
35982             t.addClassOnOver('x-form-trigger-over');
35983             t.addClassOnClick('x-form-trigger-click');
35984         }, this);
35985         this.triggers = ts.elements;
35986     },
35987
35988     onTrigger1Click : Roo.emptyFn,
35989     onTrigger2Click : Roo.emptyFn
35990 });/*
35991  * Based on:
35992  * Ext JS Library 1.1.1
35993  * Copyright(c) 2006-2007, Ext JS, LLC.
35994  *
35995  * Originally Released Under LGPL - original licence link has changed is not relivant.
35996  *
35997  * Fork - LGPL
35998  * <script type="text/javascript">
35999  */
36000  
36001 /**
36002  * @class Roo.form.TextArea
36003  * @extends Roo.form.TextField
36004  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36005  * support for auto-sizing.
36006  * @constructor
36007  * Creates a new TextArea
36008  * @param {Object} config Configuration options
36009  */
36010 Roo.form.TextArea = function(config){
36011     Roo.form.TextArea.superclass.constructor.call(this, config);
36012     // these are provided exchanges for backwards compat
36013     // minHeight/maxHeight were replaced by growMin/growMax to be
36014     // compatible with TextField growing config values
36015     if(this.minHeight !== undefined){
36016         this.growMin = this.minHeight;
36017     }
36018     if(this.maxHeight !== undefined){
36019         this.growMax = this.maxHeight;
36020     }
36021 };
36022
36023 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36024     /**
36025      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36026      */
36027     growMin : 60,
36028     /**
36029      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36030      */
36031     growMax: 1000,
36032     /**
36033      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36034      * in the field (equivalent to setting overflow: hidden, defaults to false)
36035      */
36036     preventScrollbars: false,
36037     /**
36038      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36039      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36040      */
36041
36042     // private
36043     onRender : function(ct, position){
36044         if(!this.el){
36045             this.defaultAutoCreate = {
36046                 tag: "textarea",
36047                 style:"width:300px;height:60px;",
36048                 autocomplete: "off"
36049             };
36050         }
36051         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36052         if(this.grow){
36053             this.textSizeEl = Roo.DomHelper.append(document.body, {
36054                 tag: "pre", cls: "x-form-grow-sizer"
36055             });
36056             if(this.preventScrollbars){
36057                 this.el.setStyle("overflow", "hidden");
36058             }
36059             this.el.setHeight(this.growMin);
36060         }
36061     },
36062
36063     onDestroy : function(){
36064         if(this.textSizeEl){
36065             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36066         }
36067         Roo.form.TextArea.superclass.onDestroy.call(this);
36068     },
36069
36070     // private
36071     onKeyUp : function(e){
36072         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36073             this.autoSize();
36074         }
36075     },
36076
36077     /**
36078      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36079      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36080      */
36081     autoSize : function(){
36082         if(!this.grow || !this.textSizeEl){
36083             return;
36084         }
36085         var el = this.el;
36086         var v = el.dom.value;
36087         var ts = this.textSizeEl;
36088
36089         ts.innerHTML = '';
36090         ts.appendChild(document.createTextNode(v));
36091         v = ts.innerHTML;
36092
36093         Roo.fly(ts).setWidth(this.el.getWidth());
36094         if(v.length < 1){
36095             v = "&#160;&#160;";
36096         }else{
36097             if(Roo.isIE){
36098                 v = v.replace(/\n/g, '<p>&#160;</p>');
36099             }
36100             v += "&#160;\n&#160;";
36101         }
36102         ts.innerHTML = v;
36103         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36104         if(h != this.lastHeight){
36105             this.lastHeight = h;
36106             this.el.setHeight(h);
36107             this.fireEvent("autosize", this, h);
36108         }
36109     }
36110 });/*
36111  * Based on:
36112  * Ext JS Library 1.1.1
36113  * Copyright(c) 2006-2007, Ext JS, LLC.
36114  *
36115  * Originally Released Under LGPL - original licence link has changed is not relivant.
36116  *
36117  * Fork - LGPL
36118  * <script type="text/javascript">
36119  */
36120  
36121
36122 /**
36123  * @class Roo.form.NumberField
36124  * @extends Roo.form.TextField
36125  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36126  * @constructor
36127  * Creates a new NumberField
36128  * @param {Object} config Configuration options
36129  */
36130 Roo.form.NumberField = function(config){
36131     Roo.form.NumberField.superclass.constructor.call(this, config);
36132 };
36133
36134 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36135     /**
36136      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36137      */
36138     fieldClass: "x-form-field x-form-num-field",
36139     /**
36140      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36141      */
36142     allowDecimals : true,
36143     /**
36144      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36145      */
36146     decimalSeparator : ".",
36147     /**
36148      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36149      */
36150     decimalPrecision : 2,
36151     /**
36152      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36153      */
36154     allowNegative : true,
36155     /**
36156      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36157      */
36158     minValue : Number.NEGATIVE_INFINITY,
36159     /**
36160      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36161      */
36162     maxValue : Number.MAX_VALUE,
36163     /**
36164      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36165      */
36166     minText : "The minimum value for this field is {0}",
36167     /**
36168      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36169      */
36170     maxText : "The maximum value for this field is {0}",
36171     /**
36172      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36173      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36174      */
36175     nanText : "{0} is not a valid number",
36176
36177     // private
36178     initEvents : function(){
36179         Roo.form.NumberField.superclass.initEvents.call(this);
36180         var allowed = "0123456789";
36181         if(this.allowDecimals){
36182             allowed += this.decimalSeparator;
36183         }
36184         if(this.allowNegative){
36185             allowed += "-";
36186         }
36187         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36188         var keyPress = function(e){
36189             var k = e.getKey();
36190             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36191                 return;
36192             }
36193             var c = e.getCharCode();
36194             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36195                 e.stopEvent();
36196             }
36197         };
36198         this.el.on("keypress", keyPress, this);
36199     },
36200
36201     // private
36202     validateValue : function(value){
36203         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36204             return false;
36205         }
36206         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36207              return true;
36208         }
36209         var num = this.parseValue(value);
36210         if(isNaN(num)){
36211             this.markInvalid(String.format(this.nanText, value));
36212             return false;
36213         }
36214         if(num < this.minValue){
36215             this.markInvalid(String.format(this.minText, this.minValue));
36216             return false;
36217         }
36218         if(num > this.maxValue){
36219             this.markInvalid(String.format(this.maxText, this.maxValue));
36220             return false;
36221         }
36222         return true;
36223     },
36224
36225     getValue : function(){
36226         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36227     },
36228
36229     // private
36230     parseValue : function(value){
36231         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36232         return isNaN(value) ? '' : value;
36233     },
36234
36235     // private
36236     fixPrecision : function(value){
36237         var nan = isNaN(value);
36238         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36239             return nan ? '' : value;
36240         }
36241         return parseFloat(value).toFixed(this.decimalPrecision);
36242     },
36243
36244     setValue : function(v){
36245         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36246     },
36247
36248     // private
36249     decimalPrecisionFcn : function(v){
36250         return Math.floor(v);
36251     },
36252
36253     beforeBlur : function(){
36254         var v = this.parseValue(this.getRawValue());
36255         if(v){
36256             this.setValue(this.fixPrecision(v));
36257         }
36258     }
36259 });/*
36260  * Based on:
36261  * Ext JS Library 1.1.1
36262  * Copyright(c) 2006-2007, Ext JS, LLC.
36263  *
36264  * Originally Released Under LGPL - original licence link has changed is not relivant.
36265  *
36266  * Fork - LGPL
36267  * <script type="text/javascript">
36268  */
36269  
36270 /**
36271  * @class Roo.form.DateField
36272  * @extends Roo.form.TriggerField
36273  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36274 * @constructor
36275 * Create a new DateField
36276 * @param {Object} config
36277  */
36278 Roo.form.DateField = function(config){
36279     Roo.form.DateField.superclass.constructor.call(this, config);
36280     
36281       this.addEvents({
36282          
36283         /**
36284          * @event select
36285          * Fires when a date is selected
36286              * @param {Roo.form.DateField} combo This combo box
36287              * @param {Date} date The date selected
36288              */
36289         'select' : true
36290          
36291     });
36292     
36293     
36294     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36295     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36296     this.ddMatch = null;
36297     if(this.disabledDates){
36298         var dd = this.disabledDates;
36299         var re = "(?:";
36300         for(var i = 0; i < dd.length; i++){
36301             re += dd[i];
36302             if(i != dd.length-1) re += "|";
36303         }
36304         this.ddMatch = new RegExp(re + ")");
36305     }
36306 };
36307
36308 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36309     /**
36310      * @cfg {String} format
36311      * The default date format string which can be overriden for localization support.  The format must be
36312      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36313      */
36314     format : "m/d/y",
36315     /**
36316      * @cfg {String} altFormats
36317      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36318      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36319      */
36320     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36321     /**
36322      * @cfg {Array} disabledDays
36323      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36324      */
36325     disabledDays : null,
36326     /**
36327      * @cfg {String} disabledDaysText
36328      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36329      */
36330     disabledDaysText : "Disabled",
36331     /**
36332      * @cfg {Array} disabledDates
36333      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36334      * expression so they are very powerful. Some examples:
36335      * <ul>
36336      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36337      * <li>["03/08", "09/16"] would disable those days for every year</li>
36338      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36339      * <li>["03/../2006"] would disable every day in March 2006</li>
36340      * <li>["^03"] would disable every day in every March</li>
36341      * </ul>
36342      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36343      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36344      */
36345     disabledDates : null,
36346     /**
36347      * @cfg {String} disabledDatesText
36348      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36349      */
36350     disabledDatesText : "Disabled",
36351     /**
36352      * @cfg {Date/String} minValue
36353      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36354      * valid format (defaults to null).
36355      */
36356     minValue : null,
36357     /**
36358      * @cfg {Date/String} maxValue
36359      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36360      * valid format (defaults to null).
36361      */
36362     maxValue : null,
36363     /**
36364      * @cfg {String} minText
36365      * The error text to display when the date in the cell is before minValue (defaults to
36366      * 'The date in this field must be after {minValue}').
36367      */
36368     minText : "The date in this field must be equal to or after {0}",
36369     /**
36370      * @cfg {String} maxText
36371      * The error text to display when the date in the cell is after maxValue (defaults to
36372      * 'The date in this field must be before {maxValue}').
36373      */
36374     maxText : "The date in this field must be equal to or before {0}",
36375     /**
36376      * @cfg {String} invalidText
36377      * The error text to display when the date in the field is invalid (defaults to
36378      * '{value} is not a valid date - it must be in the format {format}').
36379      */
36380     invalidText : "{0} is not a valid date - it must be in the format {1}",
36381     /**
36382      * @cfg {String} triggerClass
36383      * An additional CSS class used to style the trigger button.  The trigger will always get the
36384      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36385      * which displays a calendar icon).
36386      */
36387     triggerClass : 'x-form-date-trigger',
36388     
36389
36390     /**
36391      * @cfg {bool} useIso
36392      * if enabled, then the date field will use a hidden field to store the 
36393      * real value as iso formated date. default (false)
36394      */ 
36395     useIso : false,
36396     /**
36397      * @cfg {String/Object} autoCreate
36398      * A DomHelper element spec, or true for a default element spec (defaults to
36399      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36400      */ 
36401     // private
36402     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36403     
36404     // private
36405     hiddenField: false,
36406     
36407     onRender : function(ct, position)
36408     {
36409         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36410         if (this.useIso) {
36411             this.el.dom.removeAttribute('name'); 
36412             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36413                     'before', true);
36414             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36415             // prevent input submission
36416             this.hiddenName = this.name;
36417         }
36418             
36419             
36420     },
36421     
36422     // private
36423     validateValue : function(value)
36424     {
36425         value = this.formatDate(value);
36426         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36427             return false;
36428         }
36429         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36430              return true;
36431         }
36432         var svalue = value;
36433         value = this.parseDate(value);
36434         if(!value){
36435             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36436             return false;
36437         }
36438         var time = value.getTime();
36439         if(this.minValue && time < this.minValue.getTime()){
36440             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36441             return false;
36442         }
36443         if(this.maxValue && time > this.maxValue.getTime()){
36444             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36445             return false;
36446         }
36447         if(this.disabledDays){
36448             var day = value.getDay();
36449             for(var i = 0; i < this.disabledDays.length; i++) {
36450                 if(day === this.disabledDays[i]){
36451                     this.markInvalid(this.disabledDaysText);
36452                     return false;
36453                 }
36454             }
36455         }
36456         var fvalue = this.formatDate(value);
36457         if(this.ddMatch && this.ddMatch.test(fvalue)){
36458             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36459             return false;
36460         }
36461         return true;
36462     },
36463
36464     // private
36465     // Provides logic to override the default TriggerField.validateBlur which just returns true
36466     validateBlur : function(){
36467         return !this.menu || !this.menu.isVisible();
36468     },
36469
36470     /**
36471      * Returns the current date value of the date field.
36472      * @return {Date} The date value
36473      */
36474     getValue : function(){
36475         
36476         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36477     },
36478
36479     /**
36480      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36481      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36482      * (the default format used is "m/d/y").
36483      * <br />Usage:
36484      * <pre><code>
36485 //All of these calls set the same date value (May 4, 2006)
36486
36487 //Pass a date object:
36488 var dt = new Date('5/4/06');
36489 dateField.setValue(dt);
36490
36491 //Pass a date string (default format):
36492 dateField.setValue('5/4/06');
36493
36494 //Pass a date string (custom format):
36495 dateField.format = 'Y-m-d';
36496 dateField.setValue('2006-5-4');
36497 </code></pre>
36498      * @param {String/Date} date The date or valid date string
36499      */
36500     setValue : function(date){
36501         if (this.hiddenField) {
36502             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36503         }
36504         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36505     },
36506
36507     // private
36508     parseDate : function(value){
36509         if(!value || value instanceof Date){
36510             return value;
36511         }
36512         var v = Date.parseDate(value, this.format);
36513         if(!v && this.altFormats){
36514             if(!this.altFormatsArray){
36515                 this.altFormatsArray = this.altFormats.split("|");
36516             }
36517             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36518                 v = Date.parseDate(value, this.altFormatsArray[i]);
36519             }
36520         }
36521         return v;
36522     },
36523
36524     // private
36525     formatDate : function(date, fmt){
36526         return (!date || !(date instanceof Date)) ?
36527                date : date.dateFormat(fmt || this.format);
36528     },
36529
36530     // private
36531     menuListeners : {
36532         select: function(m, d){
36533             this.setValue(d);
36534             this.fireEvent('select', this, d);
36535         },
36536         show : function(){ // retain focus styling
36537             this.onFocus();
36538         },
36539         hide : function(){
36540             this.focus.defer(10, this);
36541             var ml = this.menuListeners;
36542             this.menu.un("select", ml.select,  this);
36543             this.menu.un("show", ml.show,  this);
36544             this.menu.un("hide", ml.hide,  this);
36545         }
36546     },
36547
36548     // private
36549     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36550     onTriggerClick : function(){
36551         if(this.disabled){
36552             return;
36553         }
36554         if(this.menu == null){
36555             this.menu = new Roo.menu.DateMenu();
36556         }
36557         Roo.apply(this.menu.picker,  {
36558             showClear: this.allowBlank,
36559             minDate : this.minValue,
36560             maxDate : this.maxValue,
36561             disabledDatesRE : this.ddMatch,
36562             disabledDatesText : this.disabledDatesText,
36563             disabledDays : this.disabledDays,
36564             disabledDaysText : this.disabledDaysText,
36565             format : this.format,
36566             minText : String.format(this.minText, this.formatDate(this.minValue)),
36567             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36568         });
36569         this.menu.on(Roo.apply({}, this.menuListeners, {
36570             scope:this
36571         }));
36572         this.menu.picker.setValue(this.getValue() || new Date());
36573         this.menu.show(this.el, "tl-bl?");
36574     },
36575
36576     beforeBlur : function(){
36577         var v = this.parseDate(this.getRawValue());
36578         if(v){
36579             this.setValue(v);
36580         }
36581     }
36582
36583     /** @cfg {Boolean} grow @hide */
36584     /** @cfg {Number} growMin @hide */
36585     /** @cfg {Number} growMax @hide */
36586     /**
36587      * @hide
36588      * @method autoSize
36589      */
36590 });/*
36591  * Based on:
36592  * Ext JS Library 1.1.1
36593  * Copyright(c) 2006-2007, Ext JS, LLC.
36594  *
36595  * Originally Released Under LGPL - original licence link has changed is not relivant.
36596  *
36597  * Fork - LGPL
36598  * <script type="text/javascript">
36599  */
36600  
36601
36602 /**
36603  * @class Roo.form.ComboBox
36604  * @extends Roo.form.TriggerField
36605  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36606  * @constructor
36607  * Create a new ComboBox.
36608  * @param {Object} config Configuration options
36609  */
36610 Roo.form.ComboBox = function(config){
36611     Roo.form.ComboBox.superclass.constructor.call(this, config);
36612     this.addEvents({
36613         /**
36614          * @event expand
36615          * Fires when the dropdown list is expanded
36616              * @param {Roo.form.ComboBox} combo This combo box
36617              */
36618         'expand' : true,
36619         /**
36620          * @event collapse
36621          * Fires when the dropdown list is collapsed
36622              * @param {Roo.form.ComboBox} combo This combo box
36623              */
36624         'collapse' : true,
36625         /**
36626          * @event beforeselect
36627          * Fires before a list item is selected. Return false to cancel the selection.
36628              * @param {Roo.form.ComboBox} combo This combo box
36629              * @param {Roo.data.Record} record The data record returned from the underlying store
36630              * @param {Number} index The index of the selected item in the dropdown list
36631              */
36632         'beforeselect' : true,
36633         /**
36634          * @event select
36635          * Fires when a list item is selected
36636              * @param {Roo.form.ComboBox} combo This combo box
36637              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36638              * @param {Number} index The index of the selected item in the dropdown list
36639              */
36640         'select' : true,
36641         /**
36642          * @event beforequery
36643          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36644          * The event object passed has these properties:
36645              * @param {Roo.form.ComboBox} combo This combo box
36646              * @param {String} query The query
36647              * @param {Boolean} forceAll true to force "all" query
36648              * @param {Boolean} cancel true to cancel the query
36649              * @param {Object} e The query event object
36650              */
36651         'beforequery': true
36652     });
36653     if(this.transform){
36654         this.allowDomMove = false;
36655         var s = Roo.getDom(this.transform);
36656         if(!this.hiddenName){
36657             this.hiddenName = s.name;
36658         }
36659         if(!this.store){
36660             this.mode = 'local';
36661             var d = [], opts = s.options;
36662             for(var i = 0, len = opts.length;i < len; i++){
36663                 var o = opts[i];
36664                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36665                 if(o.selected) {
36666                     this.value = value;
36667                 }
36668                 d.push([value, o.text]);
36669             }
36670             this.store = new Roo.data.SimpleStore({
36671                 'id': 0,
36672                 fields: ['value', 'text'],
36673                 data : d
36674             });
36675             this.valueField = 'value';
36676             this.displayField = 'text';
36677         }
36678         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36679         if(!this.lazyRender){
36680             this.target = true;
36681             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36682             s.parentNode.removeChild(s); // remove it
36683             this.render(this.el.parentNode);
36684         }else{
36685             s.parentNode.removeChild(s); // remove it
36686         }
36687
36688     }
36689     if (this.store) {
36690         this.store = Roo.factory(this.store, Roo.data);
36691     }
36692     
36693     this.selectedIndex = -1;
36694     if(this.mode == 'local'){
36695         if(config.queryDelay === undefined){
36696             this.queryDelay = 10;
36697         }
36698         if(config.minChars === undefined){
36699             this.minChars = 0;
36700         }
36701     }
36702 };
36703
36704 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36705     /**
36706      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36707      */
36708     /**
36709      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36710      * rendering into an Roo.Editor, defaults to false)
36711      */
36712     /**
36713      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36714      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36715      */
36716     /**
36717      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36718      */
36719     /**
36720      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36721      * the dropdown list (defaults to undefined, with no header element)
36722      */
36723
36724      /**
36725      * @cfg {String/Roo.Template} tpl The template to use to render the output
36726      */
36727      
36728     // private
36729     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36730     /**
36731      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36732      */
36733     listWidth: undefined,
36734     /**
36735      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36736      * mode = 'remote' or 'text' if mode = 'local')
36737      */
36738     displayField: undefined,
36739     /**
36740      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36741      * mode = 'remote' or 'value' if mode = 'local'). 
36742      * Note: use of a valueField requires the user make a selection
36743      * in order for a value to be mapped.
36744      */
36745     valueField: undefined,
36746     /**
36747      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36748      * field's data value (defaults to the underlying DOM element's name)
36749      */
36750     hiddenName: undefined,
36751     /**
36752      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36753      */
36754     listClass: '',
36755     /**
36756      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36757      */
36758     selectedClass: 'x-combo-selected',
36759     /**
36760      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36761      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36762      * which displays a downward arrow icon).
36763      */
36764     triggerClass : 'x-form-arrow-trigger',
36765     /**
36766      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36767      */
36768     shadow:'sides',
36769     /**
36770      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36771      * anchor positions (defaults to 'tl-bl')
36772      */
36773     listAlign: 'tl-bl?',
36774     /**
36775      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36776      */
36777     maxHeight: 300,
36778     /**
36779      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36780      * query specified by the allQuery config option (defaults to 'query')
36781      */
36782     triggerAction: 'query',
36783     /**
36784      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36785      * (defaults to 4, does not apply if editable = false)
36786      */
36787     minChars : 4,
36788     /**
36789      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36790      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36791      */
36792     typeAhead: false,
36793     /**
36794      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36795      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36796      */
36797     queryDelay: 500,
36798     /**
36799      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36800      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36801      */
36802     pageSize: 0,
36803     /**
36804      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36805      * when editable = true (defaults to false)
36806      */
36807     selectOnFocus:false,
36808     /**
36809      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36810      */
36811     queryParam: 'query',
36812     /**
36813      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36814      * when mode = 'remote' (defaults to 'Loading...')
36815      */
36816     loadingText: 'Loading...',
36817     /**
36818      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36819      */
36820     resizable: false,
36821     /**
36822      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36823      */
36824     handleHeight : 8,
36825     /**
36826      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36827      * traditional select (defaults to true)
36828      */
36829     editable: true,
36830     /**
36831      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36832      */
36833     allQuery: '',
36834     /**
36835      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36836      */
36837     mode: 'remote',
36838     /**
36839      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36840      * listWidth has a higher value)
36841      */
36842     minListWidth : 70,
36843     /**
36844      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36845      * allow the user to set arbitrary text into the field (defaults to false)
36846      */
36847     forceSelection:false,
36848     /**
36849      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36850      * if typeAhead = true (defaults to 250)
36851      */
36852     typeAheadDelay : 250,
36853     /**
36854      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36855      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36856      */
36857     valueNotFoundText : undefined,
36858     /**
36859      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36860      */
36861     blockFocus : false,
36862     
36863     /**
36864      * @cfg {bool} disableClear Disable showing of clear button.
36865      */
36866     disableClear : false,
36867     
36868     // private
36869     onRender : function(ct, position){
36870         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36871         if(this.hiddenName){
36872             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36873                     'before', true);
36874             this.hiddenField.value =
36875                 this.hiddenValue !== undefined ? this.hiddenValue :
36876                 this.value !== undefined ? this.value : '';
36877
36878             // prevent input submission
36879             this.el.dom.removeAttribute('name');
36880         }
36881         if(Roo.isGecko){
36882             this.el.dom.setAttribute('autocomplete', 'off');
36883         }
36884
36885         var cls = 'x-combo-list';
36886
36887         this.list = new Roo.Layer({
36888             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36889         });
36890
36891         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36892         this.list.setWidth(lw);
36893         this.list.swallowEvent('mousewheel');
36894         this.assetHeight = 0;
36895
36896         if(this.title){
36897             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36898             this.assetHeight += this.header.getHeight();
36899         }
36900
36901         this.innerList = this.list.createChild({cls:cls+'-inner'});
36902         this.innerList.on('mouseover', this.onViewOver, this);
36903         this.innerList.on('mousemove', this.onViewMove, this);
36904         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36905         
36906         if(this.allowBlank && !this.pageSize && !this.disableClear){
36907             this.footer = this.list.createChild({cls:cls+'-ft'});
36908             this.pageTb = new Roo.Toolbar(this.footer);
36909            
36910         }
36911         if(this.pageSize){
36912             this.footer = this.list.createChild({cls:cls+'-ft'});
36913             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36914                     {pageSize: this.pageSize});
36915             
36916         }
36917         
36918         if (this.pageTb && this.allowBlank && !this.disableClear) {
36919             var _this = this;
36920             this.pageTb.add(new Roo.Toolbar.Fill(), {
36921                 cls: 'x-btn-icon x-btn-clear',
36922                 text: '&#160;',
36923                 handler: function()
36924                 {
36925                     _this.collapse();
36926                     _this.clearValue();
36927                     _this.onSelect(false, -1);
36928                 }
36929             });
36930         }
36931         if (this.footer) {
36932             this.assetHeight += this.footer.getHeight();
36933         }
36934         
36935
36936         if(!this.tpl){
36937             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36938         }
36939
36940         this.view = new Roo.View(this.innerList, this.tpl, {
36941             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36942         });
36943
36944         this.view.on('click', this.onViewClick, this);
36945
36946         this.store.on('beforeload', this.onBeforeLoad, this);
36947         this.store.on('load', this.onLoad, this);
36948         this.store.on('loadexception', this.collapse, this);
36949
36950         if(this.resizable){
36951             this.resizer = new Roo.Resizable(this.list,  {
36952                pinned:true, handles:'se'
36953             });
36954             this.resizer.on('resize', function(r, w, h){
36955                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36956                 this.listWidth = w;
36957                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36958                 this.restrictHeight();
36959             }, this);
36960             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36961         }
36962         if(!this.editable){
36963             this.editable = true;
36964             this.setEditable(false);
36965         }
36966     },
36967
36968     // private
36969     initEvents : function(){
36970         Roo.form.ComboBox.superclass.initEvents.call(this);
36971
36972         this.keyNav = new Roo.KeyNav(this.el, {
36973             "up" : function(e){
36974                 this.inKeyMode = true;
36975                 this.selectPrev();
36976             },
36977
36978             "down" : function(e){
36979                 if(!this.isExpanded()){
36980                     this.onTriggerClick();
36981                 }else{
36982                     this.inKeyMode = true;
36983                     this.selectNext();
36984                 }
36985             },
36986
36987             "enter" : function(e){
36988                 this.onViewClick();
36989                 //return true;
36990             },
36991
36992             "esc" : function(e){
36993                 this.collapse();
36994             },
36995
36996             "tab" : function(e){
36997                 this.onViewClick(false);
36998                 return true;
36999             },
37000
37001             scope : this,
37002
37003             doRelay : function(foo, bar, hname){
37004                 if(hname == 'down' || this.scope.isExpanded()){
37005                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37006                 }
37007                 return true;
37008             },
37009
37010             forceKeyDown: true
37011         });
37012         this.queryDelay = Math.max(this.queryDelay || 10,
37013                 this.mode == 'local' ? 10 : 250);
37014         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37015         if(this.typeAhead){
37016             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37017         }
37018         if(this.editable !== false){
37019             this.el.on("keyup", this.onKeyUp, this);
37020         }
37021         if(this.forceSelection){
37022             this.on('blur', this.doForce, this);
37023         }
37024     },
37025
37026     onDestroy : function(){
37027         if(this.view){
37028             this.view.setStore(null);
37029             this.view.el.removeAllListeners();
37030             this.view.el.remove();
37031             this.view.purgeListeners();
37032         }
37033         if(this.list){
37034             this.list.destroy();
37035         }
37036         if(this.store){
37037             this.store.un('beforeload', this.onBeforeLoad, this);
37038             this.store.un('load', this.onLoad, this);
37039             this.store.un('loadexception', this.collapse, this);
37040         }
37041         Roo.form.ComboBox.superclass.onDestroy.call(this);
37042     },
37043
37044     // private
37045     fireKey : function(e){
37046         if(e.isNavKeyPress() && !this.list.isVisible()){
37047             this.fireEvent("specialkey", this, e);
37048         }
37049     },
37050
37051     // private
37052     onResize: function(w, h){
37053         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37054         if(this.list && this.listWidth === undefined){
37055             var lw = Math.max(w, this.minListWidth);
37056             this.list.setWidth(lw);
37057             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37058         }
37059     },
37060
37061     /**
37062      * Allow or prevent the user from directly editing the field text.  If false is passed,
37063      * the user will only be able to select from the items defined in the dropdown list.  This method
37064      * is the runtime equivalent of setting the 'editable' config option at config time.
37065      * @param {Boolean} value True to allow the user to directly edit the field text
37066      */
37067     setEditable : function(value){
37068         if(value == this.editable){
37069             return;
37070         }
37071         this.editable = value;
37072         if(!value){
37073             this.el.dom.setAttribute('readOnly', true);
37074             this.el.on('mousedown', this.onTriggerClick,  this);
37075             this.el.addClass('x-combo-noedit');
37076         }else{
37077             this.el.dom.setAttribute('readOnly', false);
37078             this.el.un('mousedown', this.onTriggerClick,  this);
37079             this.el.removeClass('x-combo-noedit');
37080         }
37081     },
37082
37083     // private
37084     onBeforeLoad : function(){
37085         if(!this.hasFocus){
37086             return;
37087         }
37088         this.innerList.update(this.loadingText ?
37089                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37090         this.restrictHeight();
37091         this.selectedIndex = -1;
37092     },
37093
37094     // private
37095     onLoad : function(){
37096         if(!this.hasFocus){
37097             return;
37098         }
37099         if(this.store.getCount() > 0){
37100             this.expand();
37101             this.restrictHeight();
37102             if(this.lastQuery == this.allQuery){
37103                 if(this.editable){
37104                     this.el.dom.select();
37105                 }
37106                 if(!this.selectByValue(this.value, true)){
37107                     this.select(0, true);
37108                 }
37109             }else{
37110                 this.selectNext();
37111                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37112                     this.taTask.delay(this.typeAheadDelay);
37113                 }
37114             }
37115         }else{
37116             this.onEmptyResults();
37117         }
37118         //this.el.focus();
37119     },
37120
37121     // private
37122     onTypeAhead : function(){
37123         if(this.store.getCount() > 0){
37124             var r = this.store.getAt(0);
37125             var newValue = r.data[this.displayField];
37126             var len = newValue.length;
37127             var selStart = this.getRawValue().length;
37128             if(selStart != len){
37129                 this.setRawValue(newValue);
37130                 this.selectText(selStart, newValue.length);
37131             }
37132         }
37133     },
37134
37135     // private
37136     onSelect : function(record, index){
37137         if(this.fireEvent('beforeselect', this, record, index) !== false){
37138             this.setFromData(index > -1 ? record.data : false);
37139             this.collapse();
37140             this.fireEvent('select', this, record, index);
37141         }
37142     },
37143
37144     /**
37145      * Returns the currently selected field value or empty string if no value is set.
37146      * @return {String} value The selected value
37147      */
37148     getValue : function(){
37149         if(this.valueField){
37150             return typeof this.value != 'undefined' ? this.value : '';
37151         }else{
37152             return Roo.form.ComboBox.superclass.getValue.call(this);
37153         }
37154     },
37155
37156     /**
37157      * Clears any text/value currently set in the field
37158      */
37159     clearValue : function(){
37160         if(this.hiddenField){
37161             this.hiddenField.value = '';
37162         }
37163         this.value = '';
37164         this.setRawValue('');
37165         this.lastSelectionText = '';
37166         this.applyEmptyText();
37167     },
37168
37169     /**
37170      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37171      * will be displayed in the field.  If the value does not match the data value of an existing item,
37172      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37173      * Otherwise the field will be blank (although the value will still be set).
37174      * @param {String} value The value to match
37175      */
37176     setValue : function(v){
37177         var text = v;
37178         if(this.valueField){
37179             var r = this.findRecord(this.valueField, v);
37180             if(r){
37181                 text = r.data[this.displayField];
37182             }else if(this.valueNotFoundText !== undefined){
37183                 text = this.valueNotFoundText;
37184             }
37185         }
37186         this.lastSelectionText = text;
37187         if(this.hiddenField){
37188             this.hiddenField.value = v;
37189         }
37190         Roo.form.ComboBox.superclass.setValue.call(this, text);
37191         this.value = v;
37192     },
37193     /**
37194      * @property {Object} the last set data for the element
37195      */
37196     
37197     lastData : false,
37198     /**
37199      * Sets the value of the field based on a object which is related to the record format for the store.
37200      * @param {Object} value the value to set as. or false on reset?
37201      */
37202     setFromData : function(o){
37203         var dv = ''; // display value
37204         var vv = ''; // value value..
37205         this.lastData = o;
37206         if (this.displayField) {
37207             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37208         } else {
37209             // this is an error condition!!!
37210             console.log('no value field set for '+ this.name);
37211         }
37212         
37213         if(this.valueField){
37214             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37215         }
37216         if(this.hiddenField){
37217             this.hiddenField.value = vv;
37218             
37219             this.lastSelectionText = dv;
37220             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37221             this.value = vv;
37222             return;
37223         }
37224         // no hidden field.. - we store the value in 'value', but still display
37225         // display field!!!!
37226         this.lastSelectionText = dv;
37227         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37228         this.value = vv;
37229         
37230         
37231     },
37232     // private
37233     reset : function(){
37234         // overridden so that last data is reset..
37235         this.setValue(this.originalValue);
37236         this.clearInvalid();
37237         this.lastData = false;
37238     },
37239     // private
37240     findRecord : function(prop, value){
37241         var record;
37242         if(this.store.getCount() > 0){
37243             this.store.each(function(r){
37244                 if(r.data[prop] == value){
37245                     record = r;
37246                     return false;
37247                 }
37248             });
37249         }
37250         return record;
37251     },
37252
37253     // private
37254     onViewMove : function(e, t){
37255         this.inKeyMode = false;
37256     },
37257
37258     // private
37259     onViewOver : function(e, t){
37260         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37261             return;
37262         }
37263         var item = this.view.findItemFromChild(t);
37264         if(item){
37265             var index = this.view.indexOf(item);
37266             this.select(index, false);
37267         }
37268     },
37269
37270     // private
37271     onViewClick : function(doFocus){
37272         var index = this.view.getSelectedIndexes()[0];
37273         var r = this.store.getAt(index);
37274         if(r){
37275             this.onSelect(r, index);
37276         }
37277         if(doFocus !== false && !this.blockFocus){
37278             this.el.focus();
37279         }
37280     },
37281
37282     // private
37283     restrictHeight : function(){
37284         this.innerList.dom.style.height = '';
37285         var inner = this.innerList.dom;
37286         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37287         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37288         this.list.beginUpdate();
37289         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37290         this.list.alignTo(this.el, this.listAlign);
37291         this.list.endUpdate();
37292     },
37293
37294     // private
37295     onEmptyResults : function(){
37296         this.collapse();
37297     },
37298
37299     /**
37300      * Returns true if the dropdown list is expanded, else false.
37301      */
37302     isExpanded : function(){
37303         return this.list.isVisible();
37304     },
37305
37306     /**
37307      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37308      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37309      * @param {String} value The data value of the item to select
37310      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37311      * selected item if it is not currently in view (defaults to true)
37312      * @return {Boolean} True if the value matched an item in the list, else false
37313      */
37314     selectByValue : function(v, scrollIntoView){
37315         if(v !== undefined && v !== null){
37316             var r = this.findRecord(this.valueField || this.displayField, v);
37317             if(r){
37318                 this.select(this.store.indexOf(r), scrollIntoView);
37319                 return true;
37320             }
37321         }
37322         return false;
37323     },
37324
37325     /**
37326      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37327      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37328      * @param {Number} index The zero-based index of the list item to select
37329      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37330      * selected item if it is not currently in view (defaults to true)
37331      */
37332     select : function(index, scrollIntoView){
37333         this.selectedIndex = index;
37334         this.view.select(index);
37335         if(scrollIntoView !== false){
37336             var el = this.view.getNode(index);
37337             if(el){
37338                 this.innerList.scrollChildIntoView(el, false);
37339             }
37340         }
37341     },
37342
37343     // private
37344     selectNext : function(){
37345         var ct = this.store.getCount();
37346         if(ct > 0){
37347             if(this.selectedIndex == -1){
37348                 this.select(0);
37349             }else if(this.selectedIndex < ct-1){
37350                 this.select(this.selectedIndex+1);
37351             }
37352         }
37353     },
37354
37355     // private
37356     selectPrev : function(){
37357         var ct = this.store.getCount();
37358         if(ct > 0){
37359             if(this.selectedIndex == -1){
37360                 this.select(0);
37361             }else if(this.selectedIndex != 0){
37362                 this.select(this.selectedIndex-1);
37363             }
37364         }
37365     },
37366
37367     // private
37368     onKeyUp : function(e){
37369         if(this.editable !== false && !e.isSpecialKey()){
37370             this.lastKey = e.getKey();
37371             this.dqTask.delay(this.queryDelay);
37372         }
37373     },
37374
37375     // private
37376     validateBlur : function(){
37377         return !this.list || !this.list.isVisible();   
37378     },
37379
37380     // private
37381     initQuery : function(){
37382         this.doQuery(this.getRawValue());
37383     },
37384
37385     // private
37386     doForce : function(){
37387         if(this.el.dom.value.length > 0){
37388             this.el.dom.value =
37389                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37390             this.applyEmptyText();
37391         }
37392     },
37393
37394     /**
37395      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37396      * query allowing the query action to be canceled if needed.
37397      * @param {String} query The SQL query to execute
37398      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37399      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37400      * saved in the current store (defaults to false)
37401      */
37402     doQuery : function(q, forceAll){
37403         if(q === undefined || q === null){
37404             q = '';
37405         }
37406         var qe = {
37407             query: q,
37408             forceAll: forceAll,
37409             combo: this,
37410             cancel:false
37411         };
37412         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37413             return false;
37414         }
37415         q = qe.query;
37416         forceAll = qe.forceAll;
37417         if(forceAll === true || (q.length >= this.minChars)){
37418             if(this.lastQuery != q){
37419                 this.lastQuery = q;
37420                 if(this.mode == 'local'){
37421                     this.selectedIndex = -1;
37422                     if(forceAll){
37423                         this.store.clearFilter();
37424                     }else{
37425                         this.store.filter(this.displayField, q);
37426                     }
37427                     this.onLoad();
37428                 }else{
37429                     this.store.baseParams[this.queryParam] = q;
37430                     this.store.load({
37431                         params: this.getParams(q)
37432                     });
37433                     this.expand();
37434                 }
37435             }else{
37436                 this.selectedIndex = -1;
37437                 this.onLoad();   
37438             }
37439         }
37440     },
37441
37442     // private
37443     getParams : function(q){
37444         var p = {};
37445         //p[this.queryParam] = q;
37446         if(this.pageSize){
37447             p.start = 0;
37448             p.limit = this.pageSize;
37449         }
37450         return p;
37451     },
37452
37453     /**
37454      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37455      */
37456     collapse : function(){
37457         if(!this.isExpanded()){
37458             return;
37459         }
37460         this.list.hide();
37461         Roo.get(document).un('mousedown', this.collapseIf, this);
37462         Roo.get(document).un('mousewheel', this.collapseIf, this);
37463         this.fireEvent('collapse', this);
37464     },
37465
37466     // private
37467     collapseIf : function(e){
37468         if(!e.within(this.wrap) && !e.within(this.list)){
37469             this.collapse();
37470         }
37471     },
37472
37473     /**
37474      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37475      */
37476     expand : function(){
37477         if(this.isExpanded() || !this.hasFocus){
37478             return;
37479         }
37480         this.list.alignTo(this.el, this.listAlign);
37481         this.list.show();
37482         Roo.get(document).on('mousedown', this.collapseIf, this);
37483         Roo.get(document).on('mousewheel', this.collapseIf, this);
37484         this.fireEvent('expand', this);
37485     },
37486
37487     // private
37488     // Implements the default empty TriggerField.onTriggerClick function
37489     onTriggerClick : function(){
37490         if(this.disabled){
37491             return;
37492         }
37493         if(this.isExpanded()){
37494             this.collapse();
37495             if (!this.blockFocus) {
37496                 this.el.focus();
37497             }
37498             
37499         }else {
37500             this.hasFocus = true;
37501             if(this.triggerAction == 'all') {
37502                 this.doQuery(this.allQuery, true);
37503             } else {
37504                 this.doQuery(this.getRawValue());
37505             }
37506             if (!this.blockFocus) {
37507                 this.el.focus();
37508             }
37509         }
37510     }
37511
37512     /** 
37513     * @cfg {Boolean} grow 
37514     * @hide 
37515     */
37516     /** 
37517     * @cfg {Number} growMin 
37518     * @hide 
37519     */
37520     /** 
37521     * @cfg {Number} growMax 
37522     * @hide 
37523     */
37524     /**
37525      * @hide
37526      * @method autoSize
37527      */
37528 });/*
37529  * Based on:
37530  * Ext JS Library 1.1.1
37531  * Copyright(c) 2006-2007, Ext JS, LLC.
37532  *
37533  * Originally Released Under LGPL - original licence link has changed is not relivant.
37534  *
37535  * Fork - LGPL
37536  * <script type="text/javascript">
37537  */
37538 /**
37539  * @class Roo.form.Checkbox
37540  * @extends Roo.form.Field
37541  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37542  * @constructor
37543  * Creates a new Checkbox
37544  * @param {Object} config Configuration options
37545  */
37546 Roo.form.Checkbox = function(config){
37547     Roo.form.Checkbox.superclass.constructor.call(this, config);
37548     this.addEvents({
37549         /**
37550          * @event check
37551          * Fires when the checkbox is checked or unchecked.
37552              * @param {Roo.form.Checkbox} this This checkbox
37553              * @param {Boolean} checked The new checked value
37554              */
37555         check : true
37556     });
37557 };
37558
37559 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37560     /**
37561      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37562      */
37563     focusClass : undefined,
37564     /**
37565      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37566      */
37567     fieldClass: "x-form-field",
37568     /**
37569      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37570      */
37571     checked: false,
37572     /**
37573      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37574      * {tag: "input", type: "checkbox", autocomplete: "off"})
37575      */
37576     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37577     /**
37578      * @cfg {String} boxLabel The text that appears beside the checkbox
37579      */
37580     boxLabel : "",
37581     /**
37582      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37583      */  
37584     inputValue : '1',
37585     /**
37586      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37587      */
37588      valueOff: '0', // value when not checked..
37589
37590     actionMode : 'viewEl', 
37591     //
37592     // private
37593     itemCls : 'x-menu-check-item x-form-item',
37594     groupClass : 'x-menu-group-item',
37595     inputType : 'hidden',
37596     
37597     
37598     inSetChecked: false, // check that we are not calling self...
37599     
37600     inputElement: false, // real input element?
37601     basedOn: false, // ????
37602     
37603     isFormField: true, // not sure where this is needed!!!!
37604
37605     onResize : function(){
37606         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37607         if(!this.boxLabel){
37608             this.el.alignTo(this.wrap, 'c-c');
37609         }
37610     },
37611
37612     initEvents : function(){
37613         Roo.form.Checkbox.superclass.initEvents.call(this);
37614         this.el.on("click", this.onClick,  this);
37615         this.el.on("change", this.onClick,  this);
37616     },
37617
37618
37619     getResizeEl : function(){
37620         return this.wrap;
37621     },
37622
37623     getPositionEl : function(){
37624         return this.wrap;
37625     },
37626
37627     // private
37628     onRender : function(ct, position){
37629         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37630         /*
37631         if(this.inputValue !== undefined){
37632             this.el.dom.value = this.inputValue;
37633         }
37634         */
37635         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37636         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37637         var viewEl = this.wrap.createChild({ 
37638             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37639         this.viewEl = viewEl;   
37640         this.wrap.on('click', this.onClick,  this); 
37641         
37642         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37643         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37644         
37645         
37646         
37647         if(this.boxLabel){
37648             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37649         //    viewEl.on('click', this.onClick,  this); 
37650         }
37651         //if(this.checked){
37652             this.setChecked(this.checked);
37653         //}else{
37654             //this.checked = this.el.dom;
37655         //}
37656
37657     },
37658
37659     // private
37660     initValue : Roo.emptyFn,
37661
37662     /**
37663      * Returns the checked state of the checkbox.
37664      * @return {Boolean} True if checked, else false
37665      */
37666     getValue : function(){
37667         if(this.el){
37668             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37669         }
37670         return this.valueOff;
37671         
37672     },
37673
37674         // private
37675     onClick : function(){ 
37676         this.setChecked(!this.checked);
37677
37678         //if(this.el.dom.checked != this.checked){
37679         //    this.setValue(this.el.dom.checked);
37680        // }
37681     },
37682
37683     /**
37684      * Sets the checked state of the checkbox.
37685      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37686      */
37687     setValue : function(v,suppressEvent){
37688         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37689         //if(this.el && this.el.dom){
37690         //    this.el.dom.checked = this.checked;
37691         //    this.el.dom.defaultChecked = this.checked;
37692         //}
37693         this.setChecked(v === this.inputValue);
37694         //this.fireEvent("check", this, this.checked);
37695     },
37696     // private..
37697     setChecked : function(state,suppressEvent)
37698     {
37699         if (this.inSetChecked) {
37700             this.checked = state;
37701             return;
37702         }
37703         
37704     
37705         if(this.wrap){
37706             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37707         }
37708         this.checked = state;
37709         if(suppressEvent !== true){
37710             this.fireEvent('checkchange', this, state);
37711         }
37712         this.inSetChecked = true;
37713         this.el.dom.value = state ? this.inputValue : this.valueOff;
37714         this.inSetChecked = false;
37715         
37716     },
37717     // handle setting of hidden value by some other method!!?!?
37718     setFromHidden: function()
37719     {
37720         if(!this.el){
37721             return;
37722         }
37723         //console.log("SET FROM HIDDEN");
37724         //alert('setFrom hidden');
37725         this.setValue(this.el.dom.value);
37726     },
37727     
37728     onDestroy : function()
37729     {
37730         if(this.viewEl){
37731             Roo.get(this.viewEl).remove();
37732         }
37733          
37734         Roo.form.Checkbox.superclass.onDestroy.call(this);
37735     }
37736
37737 });/*
37738  * Based on:
37739  * Ext JS Library 1.1.1
37740  * Copyright(c) 2006-2007, Ext JS, LLC.
37741  *
37742  * Originally Released Under LGPL - original licence link has changed is not relivant.
37743  *
37744  * Fork - LGPL
37745  * <script type="text/javascript">
37746  */
37747  
37748 /**
37749  * @class Roo.form.Radio
37750  * @extends Roo.form.Checkbox
37751  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37752  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37753  * @constructor
37754  * Creates a new Radio
37755  * @param {Object} config Configuration options
37756  */
37757 Roo.form.Radio = function(){
37758     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37759 };
37760 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37761     inputType: 'radio',
37762
37763     /**
37764      * If this radio is part of a group, it will return the selected value
37765      * @return {String}
37766      */
37767     getGroupValue : function(){
37768         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37769     }
37770 });//<script type="text/javascript">
37771
37772 /*
37773  * Ext JS Library 1.1.1
37774  * Copyright(c) 2006-2007, Ext JS, LLC.
37775  * licensing@extjs.com
37776  * 
37777  * http://www.extjs.com/license
37778  */
37779  
37780  /*
37781   * 
37782   * Known bugs:
37783   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37784   * - IE ? - no idea how much works there.
37785   * 
37786   * 
37787   * 
37788   */
37789  
37790
37791 /**
37792  * @class Ext.form.HtmlEditor
37793  * @extends Ext.form.Field
37794  * Provides a lightweight HTML Editor component.
37795  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37796  * 
37797  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37798  * supported by this editor.</b><br/><br/>
37799  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37800  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37801  */
37802 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37803       /**
37804      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37805      */
37806     toolbars : false,
37807     /**
37808      * @cfg {String} createLinkText The default text for the create link prompt
37809      */
37810     createLinkText : 'Please enter the URL for the link:',
37811     /**
37812      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37813      */
37814     defaultLinkValue : 'http:/'+'/',
37815    
37816     
37817     // id of frame..
37818     frameId: false,
37819     
37820     // private properties
37821     validationEvent : false,
37822     deferHeight: true,
37823     initialized : false,
37824     activated : false,
37825     sourceEditMode : false,
37826     onFocus : Roo.emptyFn,
37827     iframePad:3,
37828     hideMode:'offsets',
37829     defaultAutoCreate : {
37830         tag: "textarea",
37831         style:"width:500px;height:300px;",
37832         autocomplete: "off"
37833     },
37834
37835     // private
37836     initComponent : function(){
37837         this.addEvents({
37838             /**
37839              * @event initialize
37840              * Fires when the editor is fully initialized (including the iframe)
37841              * @param {HtmlEditor} this
37842              */
37843             initialize: true,
37844             /**
37845              * @event activate
37846              * Fires when the editor is first receives the focus. Any insertion must wait
37847              * until after this event.
37848              * @param {HtmlEditor} this
37849              */
37850             activate: true,
37851              /**
37852              * @event beforesync
37853              * Fires before the textarea is updated with content from the editor iframe. Return false
37854              * to cancel the sync.
37855              * @param {HtmlEditor} this
37856              * @param {String} html
37857              */
37858             beforesync: true,
37859              /**
37860              * @event beforepush
37861              * Fires before the iframe editor is updated with content from the textarea. Return false
37862              * to cancel the push.
37863              * @param {HtmlEditor} this
37864              * @param {String} html
37865              */
37866             beforepush: true,
37867              /**
37868              * @event sync
37869              * Fires when the textarea is updated with content from the editor iframe.
37870              * @param {HtmlEditor} this
37871              * @param {String} html
37872              */
37873             sync: true,
37874              /**
37875              * @event push
37876              * Fires when the iframe editor is updated with content from the textarea.
37877              * @param {HtmlEditor} this
37878              * @param {String} html
37879              */
37880             push: true,
37881              /**
37882              * @event editmodechange
37883              * Fires when the editor switches edit modes
37884              * @param {HtmlEditor} this
37885              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37886              */
37887             editmodechange: true,
37888             /**
37889              * @event editorevent
37890              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37891              * @param {HtmlEditor} this
37892              */
37893             editorevent: true
37894         })
37895     },
37896
37897     /**
37898      * Protected method that will not generally be called directly. It
37899      * is called when the editor creates its toolbar. Override this method if you need to
37900      * add custom toolbar buttons.
37901      * @param {HtmlEditor} editor
37902      */
37903     createToolbar : function(editor){
37904         if (!editor.toolbars || !editor.toolbars.length) {
37905             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37906         }
37907         
37908         for (var i =0 ; i < editor.toolbars.length;i++) {
37909             editor.toolbars[i].init(editor);
37910         }
37911          
37912         
37913     },
37914
37915     /**
37916      * Protected method that will not generally be called directly. It
37917      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37918      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37919      */
37920     getDocMarkup : function(){
37921         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37922     },
37923
37924     // private
37925     onRender : function(ct, position){
37926         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37927         this.el.dom.style.border = '0 none';
37928         this.el.dom.setAttribute('tabIndex', -1);
37929         this.el.addClass('x-hidden');
37930         if(Roo.isIE){ // fix IE 1px bogus margin
37931             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37932         }
37933         this.wrap = this.el.wrap({
37934             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37935         });
37936
37937         this.frameId = Roo.id();
37938         this.createToolbar(this);
37939         
37940         
37941         
37942         
37943       
37944         
37945         var iframe = this.wrap.createChild({
37946             tag: 'iframe',
37947             id: this.frameId,
37948             name: this.frameId,
37949             frameBorder : 'no',
37950             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37951         });
37952         
37953        // console.log(iframe);
37954         //this.wrap.dom.appendChild(iframe);
37955
37956         this.iframe = iframe.dom;
37957
37958          this.assignDocWin();
37959         
37960         this.doc.designMode = 'on';
37961        
37962         this.doc.open();
37963         this.doc.write(this.getDocMarkup());
37964         this.doc.close();
37965
37966         
37967         var task = { // must defer to wait for browser to be ready
37968             run : function(){
37969                 //console.log("run task?" + this.doc.readyState);
37970                 this.assignDocWin();
37971                 if(this.doc.body || this.doc.readyState == 'complete'){
37972                     try {
37973                         
37974                        
37975                         this.doc.designMode="on";
37976                     } catch (e) {
37977                         return;
37978                     }
37979                     Roo.TaskMgr.stop(task);
37980                     this.initEditor.defer(10, this);
37981                 }
37982             },
37983             interval : 10,
37984             duration:10000,
37985             scope: this
37986         };
37987         Roo.TaskMgr.start(task);
37988
37989         if(!this.width){
37990             this.setSize(this.el.getSize());
37991         }
37992     },
37993
37994     // private
37995     onResize : function(w, h){
37996         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
37997         if(this.el && this.iframe){
37998             if(typeof w == 'number'){
37999                 var aw = w - this.wrap.getFrameWidth('lr');
38000                 this.el.setWidth(this.adjustWidth('textarea', aw));
38001                 this.iframe.style.width = aw + 'px';
38002             }
38003             if(typeof h == 'number'){
38004                 var tbh = 0;
38005                 for (var i =0; i < this.toolbars.length;i++) {
38006                     // fixme - ask toolbars for heights?
38007                     tbh += this.toolbars[i].tb.el.getHeight();
38008                 }
38009                 
38010                 
38011                 
38012                 
38013                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38014                 this.el.setHeight(this.adjustWidth('textarea', ah));
38015                 this.iframe.style.height = ah + 'px';
38016                 if(this.doc){
38017                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38018                 }
38019             }
38020         }
38021     },
38022
38023     /**
38024      * Toggles the editor between standard and source edit mode.
38025      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38026      */
38027     toggleSourceEdit : function(sourceEditMode){
38028         
38029         this.sourceEditMode = sourceEditMode === true;
38030         
38031         if(this.sourceEditMode){
38032           
38033             this.syncValue();
38034             this.iframe.className = 'x-hidden';
38035             this.el.removeClass('x-hidden');
38036             this.el.dom.removeAttribute('tabIndex');
38037             this.el.focus();
38038         }else{
38039              
38040             this.pushValue();
38041             this.iframe.className = '';
38042             this.el.addClass('x-hidden');
38043             this.el.dom.setAttribute('tabIndex', -1);
38044             this.deferFocus();
38045         }
38046         this.setSize(this.wrap.getSize());
38047         this.fireEvent('editmodechange', this, this.sourceEditMode);
38048     },
38049
38050     // private used internally
38051     createLink : function(){
38052         var url = prompt(this.createLinkText, this.defaultLinkValue);
38053         if(url && url != 'http:/'+'/'){
38054             this.relayCmd('createlink', url);
38055         }
38056     },
38057
38058     // private (for BoxComponent)
38059     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38060
38061     // private (for BoxComponent)
38062     getResizeEl : function(){
38063         return this.wrap;
38064     },
38065
38066     // private (for BoxComponent)
38067     getPositionEl : function(){
38068         return this.wrap;
38069     },
38070
38071     // private
38072     initEvents : function(){
38073         this.originalValue = this.getValue();
38074     },
38075
38076     /**
38077      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38078      * @method
38079      */
38080     markInvalid : Roo.emptyFn,
38081     /**
38082      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38083      * @method
38084      */
38085     clearInvalid : Roo.emptyFn,
38086
38087     setValue : function(v){
38088         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38089         this.pushValue();
38090     },
38091
38092     /**
38093      * Protected method that will not generally be called directly. If you need/want
38094      * custom HTML cleanup, this is the method you should override.
38095      * @param {String} html The HTML to be cleaned
38096      * return {String} The cleaned HTML
38097      */
38098     cleanHtml : function(html){
38099         html = String(html);
38100         if(html.length > 5){
38101             if(Roo.isSafari){ // strip safari nonsense
38102                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38103             }
38104         }
38105         if(html == '&nbsp;'){
38106             html = '';
38107         }
38108         return html;
38109     },
38110
38111     /**
38112      * Protected method that will not generally be called directly. Syncs the contents
38113      * of the editor iframe with the textarea.
38114      */
38115     syncValue : function(){
38116         if(this.initialized){
38117             var bd = (this.doc.body || this.doc.documentElement);
38118             var html = bd.innerHTML;
38119             if(Roo.isSafari){
38120                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38121                 var m = bs.match(/text-align:(.*?);/i);
38122                 if(m && m[1]){
38123                     html = '<div style="'+m[0]+'">' + html + '</div>';
38124                 }
38125             }
38126             html = this.cleanHtml(html);
38127             if(this.fireEvent('beforesync', this, html) !== false){
38128                 this.el.dom.value = html;
38129                 this.fireEvent('sync', this, html);
38130             }
38131         }
38132     },
38133
38134     /**
38135      * Protected method that will not generally be called directly. Pushes the value of the textarea
38136      * into the iframe editor.
38137      */
38138     pushValue : function(){
38139         if(this.initialized){
38140             var v = this.el.dom.value;
38141             if(v.length < 1){
38142                 v = '&#160;';
38143             }
38144             if(this.fireEvent('beforepush', this, v) !== false){
38145                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38146                 this.fireEvent('push', this, v);
38147             }
38148         }
38149     },
38150
38151     // private
38152     deferFocus : function(){
38153         this.focus.defer(10, this);
38154     },
38155
38156     // doc'ed in Field
38157     focus : function(){
38158         if(this.win && !this.sourceEditMode){
38159             this.win.focus();
38160         }else{
38161             this.el.focus();
38162         }
38163     },
38164     
38165     assignDocWin: function()
38166     {
38167         var iframe = this.iframe;
38168         
38169          if(Roo.isIE){
38170             this.doc = iframe.contentWindow.document;
38171             this.win = iframe.contentWindow;
38172         } else {
38173             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38174             this.win = Roo.get(this.frameId).dom.contentWindow;
38175         }
38176     },
38177     
38178     // private
38179     initEditor : function(){
38180         //console.log("INIT EDITOR");
38181         this.assignDocWin();
38182         
38183         
38184         
38185         this.doc.designMode="on";
38186         this.doc.open();
38187         this.doc.write(this.getDocMarkup());
38188         this.doc.close();
38189         
38190         var dbody = (this.doc.body || this.doc.documentElement);
38191         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38192         // this copies styles from the containing element into thsi one..
38193         // not sure why we need all of this..
38194         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38195         ss['background-attachment'] = 'fixed'; // w3c
38196         dbody.bgProperties = 'fixed'; // ie
38197         Roo.DomHelper.applyStyles(dbody, ss);
38198         Roo.EventManager.on(this.doc, {
38199             'mousedown': this.onEditorEvent,
38200             'dblclick': this.onEditorEvent,
38201             'click': this.onEditorEvent,
38202             'keyup': this.onEditorEvent,
38203             buffer:100,
38204             scope: this
38205         });
38206         if(Roo.isGecko){
38207             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38208         }
38209         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38210             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38211         }
38212         this.initialized = true;
38213
38214         this.fireEvent('initialize', this);
38215         this.pushValue();
38216     },
38217
38218     // private
38219     onDestroy : function(){
38220         
38221         
38222         
38223         if(this.rendered){
38224             
38225             for (var i =0; i < this.toolbars.length;i++) {
38226                 // fixme - ask toolbars for heights?
38227                 this.toolbars[i].onDestroy();
38228             }
38229             
38230             this.wrap.dom.innerHTML = '';
38231             this.wrap.remove();
38232         }
38233     },
38234
38235     // private
38236     onFirstFocus : function(){
38237         
38238         this.assignDocWin();
38239         
38240         
38241         this.activated = true;
38242         for (var i =0; i < this.toolbars.length;i++) {
38243             this.toolbars[i].onFirstFocus();
38244         }
38245        
38246         if(Roo.isGecko){ // prevent silly gecko errors
38247             this.win.focus();
38248             var s = this.win.getSelection();
38249             if(!s.focusNode || s.focusNode.nodeType != 3){
38250                 var r = s.getRangeAt(0);
38251                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38252                 r.collapse(true);
38253                 this.deferFocus();
38254             }
38255             try{
38256                 this.execCmd('useCSS', true);
38257                 this.execCmd('styleWithCSS', false);
38258             }catch(e){}
38259         }
38260         this.fireEvent('activate', this);
38261     },
38262
38263     // private
38264     adjustFont: function(btn){
38265         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38266         //if(Roo.isSafari){ // safari
38267         //    adjust *= 2;
38268        // }
38269         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38270         if(Roo.isSafari){ // safari
38271             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38272             v =  (v < 10) ? 10 : v;
38273             v =  (v > 48) ? 48 : v;
38274             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38275             
38276         }
38277         
38278         
38279         v = Math.max(1, v+adjust);
38280         
38281         this.execCmd('FontSize', v  );
38282     },
38283
38284     onEditorEvent : function(e){
38285         this.fireEvent('editorevent', this, e);
38286       //  this.updateToolbar();
38287         this.syncValue();
38288     },
38289
38290     insertTag : function(tg)
38291     {
38292         // could be a bit smarter... -> wrap the current selected tRoo..
38293         
38294         this.execCmd("formatblock",   tg);
38295         
38296     },
38297     
38298     insertText : function(txt)
38299     {
38300         
38301         
38302         range = this.createRange();
38303         range.deleteContents();
38304                //alert(Sender.getAttribute('label'));
38305                
38306         range.insertNode(this.doc.createTextNode(txt));
38307     } ,
38308     
38309     // private
38310     relayBtnCmd : function(btn){
38311         this.relayCmd(btn.cmd);
38312     },
38313
38314     /**
38315      * Executes a Midas editor command on the editor document and performs necessary focus and
38316      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38317      * @param {String} cmd The Midas command
38318      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38319      */
38320     relayCmd : function(cmd, value){
38321         this.win.focus();
38322         this.execCmd(cmd, value);
38323         this.fireEvent('editorevent', this);
38324         //this.updateToolbar();
38325         this.deferFocus();
38326     },
38327
38328     /**
38329      * Executes a Midas editor command directly on the editor document.
38330      * For visual commands, you should use {@link #relayCmd} instead.
38331      * <b>This should only be called after the editor is initialized.</b>
38332      * @param {String} cmd The Midas command
38333      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38334      */
38335     execCmd : function(cmd, value){
38336         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38337         this.syncValue();
38338     },
38339
38340     // private
38341     applyCommand : function(e){
38342         if(e.ctrlKey){
38343             var c = e.getCharCode(), cmd;
38344             if(c > 0){
38345                 c = String.fromCharCode(c);
38346                 switch(c){
38347                     case 'b':
38348                         cmd = 'bold';
38349                     break;
38350                     case 'i':
38351                         cmd = 'italic';
38352                     break;
38353                     case 'u':
38354                         cmd = 'underline';
38355                     break;
38356                 }
38357                 if(cmd){
38358                     this.win.focus();
38359                     this.execCmd(cmd);
38360                     this.deferFocus();
38361                     e.preventDefault();
38362                 }
38363             }
38364         }
38365     },
38366
38367     /**
38368      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38369      * to insert tRoo.
38370      * @param {String} text
38371      */
38372     insertAtCursor : function(text){
38373         if(!this.activated){
38374             return;
38375         }
38376         if(Roo.isIE){
38377             this.win.focus();
38378             var r = this.doc.selection.createRange();
38379             if(r){
38380                 r.collapse(true);
38381                 r.pasteHTML(text);
38382                 this.syncValue();
38383                 this.deferFocus();
38384             }
38385         }else if(Roo.isGecko || Roo.isOpera){
38386             this.win.focus();
38387             this.execCmd('InsertHTML', text);
38388             this.deferFocus();
38389         }else if(Roo.isSafari){
38390             this.execCmd('InsertText', text);
38391             this.deferFocus();
38392         }
38393     },
38394
38395     // private
38396     fixKeys : function(){ // load time branching for fastest keydown performance
38397         if(Roo.isIE){
38398             return function(e){
38399                 var k = e.getKey(), r;
38400                 if(k == e.TAB){
38401                     e.stopEvent();
38402                     r = this.doc.selection.createRange();
38403                     if(r){
38404                         r.collapse(true);
38405                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38406                         this.deferFocus();
38407                     }
38408                 }else if(k == e.ENTER){
38409                     r = this.doc.selection.createRange();
38410                     if(r){
38411                         var target = r.parentElement();
38412                         if(!target || target.tagName.toLowerCase() != 'li'){
38413                             e.stopEvent();
38414                             r.pasteHTML('<br />');
38415                             r.collapse(false);
38416                             r.select();
38417                         }
38418                     }
38419                 }
38420             };
38421         }else if(Roo.isOpera){
38422             return function(e){
38423                 var k = e.getKey();
38424                 if(k == e.TAB){
38425                     e.stopEvent();
38426                     this.win.focus();
38427                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38428                     this.deferFocus();
38429                 }
38430             };
38431         }else if(Roo.isSafari){
38432             return function(e){
38433                 var k = e.getKey();
38434                 if(k == e.TAB){
38435                     e.stopEvent();
38436                     this.execCmd('InsertText','\t');
38437                     this.deferFocus();
38438                 }
38439              };
38440         }
38441     }(),
38442     
38443     getAllAncestors: function()
38444     {
38445         var p = this.getSelectedNode();
38446         var a = [];
38447         if (!p) {
38448             a.push(p); // push blank onto stack..
38449             p = this.getParentElement();
38450         }
38451         
38452         
38453         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38454             a.push(p);
38455             p = p.parentNode;
38456         }
38457         a.push(this.doc.body);
38458         return a;
38459     },
38460     lastSel : false,
38461     lastSelNode : false,
38462     
38463     
38464     getSelection : function() 
38465     {
38466         this.assignDocWin();
38467         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38468     },
38469     
38470     getSelectedNode: function() 
38471     {
38472         // this may only work on Gecko!!!
38473         
38474         // should we cache this!!!!
38475         
38476         
38477         
38478          
38479         var range = this.createRange(this.getSelection());
38480         
38481         if (Roo.isIE) {
38482             var parent = range.parentElement();
38483             while (true) {
38484                 var testRange = range.duplicate();
38485                 testRange.moveToElementText(parent);
38486                 if (testRange.inRange(range)) {
38487                     break;
38488                 }
38489                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38490                     break;
38491                 }
38492                 parent = parent.parentElement;
38493             }
38494             return parent;
38495         }
38496         
38497         
38498         var ar = range.endContainer.childNodes;
38499         if (!ar.length) {
38500             ar = range.commonAncestorContainer.childNodes;
38501             //alert(ar.length);
38502         }
38503         var nodes = [];
38504         var other_nodes = [];
38505         var has_other_nodes = false;
38506         for (var i=0;i<ar.length;i++) {
38507             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38508                 continue;
38509             }
38510             // fullly contained node.
38511             
38512             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38513                 nodes.push(ar[i]);
38514                 continue;
38515             }
38516             
38517             // probably selected..
38518             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38519                 other_nodes.push(ar[i]);
38520                 continue;
38521             }
38522             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38523                 continue;
38524             }
38525             
38526             
38527             has_other_nodes = true;
38528         }
38529         if (!nodes.length && other_nodes.length) {
38530             nodes= other_nodes;
38531         }
38532         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38533             return false;
38534         }
38535         
38536         return nodes[0];
38537     },
38538     createRange: function(sel)
38539     {
38540         // this has strange effects when using with 
38541         // top toolbar - not sure if it's a great idea.
38542         //this.editor.contentWindow.focus();
38543         if (typeof sel != "undefined") {
38544             try {
38545                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38546             } catch(e) {
38547                 return this.doc.createRange();
38548             }
38549         } else {
38550             return this.doc.createRange();
38551         }
38552     },
38553     getParentElement: function()
38554     {
38555         
38556         this.assignDocWin();
38557         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38558         
38559         var range = this.createRange(sel);
38560          
38561         try {
38562             var p = range.commonAncestorContainer;
38563             while (p.nodeType == 3) { // text node
38564                 p = p.parentNode;
38565             }
38566             return p;
38567         } catch (e) {
38568             return null;
38569         }
38570     
38571     },
38572     
38573     
38574     
38575     // BC Hacks - cause I cant work out what i was trying to do..
38576     rangeIntersectsNode : function(range, node)
38577     {
38578         var nodeRange = node.ownerDocument.createRange();
38579         try {
38580             nodeRange.selectNode(node);
38581         }
38582         catch (e) {
38583             nodeRange.selectNodeContents(node);
38584         }
38585
38586         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38587                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38588     },
38589     rangeCompareNode : function(range, node) {
38590         var nodeRange = node.ownerDocument.createRange();
38591         try {
38592             nodeRange.selectNode(node);
38593         } catch (e) {
38594             nodeRange.selectNodeContents(node);
38595         }
38596         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38597         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38598
38599         if (nodeIsBefore && !nodeIsAfter)
38600             return 0;
38601         if (!nodeIsBefore && nodeIsAfter)
38602             return 1;
38603         if (nodeIsBefore && nodeIsAfter)
38604             return 2;
38605
38606         return 3;
38607     }
38608
38609     
38610     
38611     // hide stuff that is not compatible
38612     /**
38613      * @event blur
38614      * @hide
38615      */
38616     /**
38617      * @event change
38618      * @hide
38619      */
38620     /**
38621      * @event focus
38622      * @hide
38623      */
38624     /**
38625      * @event specialkey
38626      * @hide
38627      */
38628     /**
38629      * @cfg {String} fieldClass @hide
38630      */
38631     /**
38632      * @cfg {String} focusClass @hide
38633      */
38634     /**
38635      * @cfg {String} autoCreate @hide
38636      */
38637     /**
38638      * @cfg {String} inputType @hide
38639      */
38640     /**
38641      * @cfg {String} invalidClass @hide
38642      */
38643     /**
38644      * @cfg {String} invalidText @hide
38645      */
38646     /**
38647      * @cfg {String} msgFx @hide
38648      */
38649     /**
38650      * @cfg {String} validateOnBlur @hide
38651      */
38652 });// <script type="text/javascript">
38653 /*
38654  * Based on
38655  * Ext JS Library 1.1.1
38656  * Copyright(c) 2006-2007, Ext JS, LLC.
38657  *  
38658  
38659  */
38660
38661 /**
38662  * @class Roo.form.HtmlEditorToolbar1
38663  * Basic Toolbar
38664  * 
38665  * Usage:
38666  *
38667  new Roo.form.HtmlEditor({
38668     ....
38669     toolbars : [
38670         new Roo.form.HtmlEditorToolbar1({
38671             disable : { fonts: 1 , format: 1, ..., ... , ...],
38672             btns : [ .... ]
38673         })
38674     }
38675      
38676  * 
38677  * @cfg {Object} disable List of elements to disable..
38678  * @cfg {Array} btns List of additional buttons.
38679  * 
38680  * 
38681  * NEEDS Extra CSS? 
38682  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38683  */
38684  
38685 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38686 {
38687     
38688     Roo.apply(this, config);
38689     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38690     // dont call parent... till later.
38691 }
38692
38693 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38694     
38695     tb: false,
38696     
38697     rendered: false,
38698     
38699     editor : false,
38700     /**
38701      * @cfg {Object} disable  List of toolbar elements to disable
38702          
38703      */
38704     disable : false,
38705       /**
38706      * @cfg {Array} fontFamilies An array of available font families
38707      */
38708     fontFamilies : [
38709         'Arial',
38710         'Courier New',
38711         'Tahoma',
38712         'Times New Roman',
38713         'Verdana'
38714     ],
38715     
38716     specialChars : [
38717            "&#169;",
38718           "&#174;",     
38719           "&#8482;",    
38720           "&#163;" ,    
38721          // "&#8212;",    
38722           "&#8230;",    
38723           "&#247;" ,    
38724         //  "&#225;" ,     ?? a acute?
38725            "&#8364;"    , //Euro
38726        //   "&#8220;"    ,
38727         //  "&#8221;"    ,
38728         //  "&#8226;"    ,
38729           "&#176;"  //   , // degrees
38730
38731          // "&#233;"     , // e ecute
38732          // "&#250;"     , // u ecute?
38733     ],
38734     inputElements : [ 
38735             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38736             "input:submit", "input:button", "select", "textarea", "label" ],
38737     formats : [
38738         ["p"] ,  
38739         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38740         ["pre"],[ "code"], 
38741         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38742     ],
38743      /**
38744      * @cfg {String} defaultFont default font to use.
38745      */
38746     defaultFont: 'tahoma',
38747    
38748     fontSelect : false,
38749     
38750     
38751     formatCombo : false,
38752     
38753     init : function(editor)
38754     {
38755         this.editor = editor;
38756         
38757         
38758         var fid = editor.frameId;
38759         var etb = this;
38760         function btn(id, toggle, handler){
38761             var xid = fid + '-'+ id ;
38762             return {
38763                 id : xid,
38764                 cmd : id,
38765                 cls : 'x-btn-icon x-edit-'+id,
38766                 enableToggle:toggle !== false,
38767                 scope: editor, // was editor...
38768                 handler:handler||editor.relayBtnCmd,
38769                 clickEvent:'mousedown',
38770                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38771                 tabIndex:-1
38772             };
38773         }
38774         
38775         
38776         
38777         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38778         this.tb = tb;
38779          // stop form submits
38780         tb.el.on('click', function(e){
38781             e.preventDefault(); // what does this do?
38782         });
38783
38784         if(!this.disable.font && !Roo.isSafari){
38785             /* why no safari for fonts
38786             editor.fontSelect = tb.el.createChild({
38787                 tag:'select',
38788                 tabIndex: -1,
38789                 cls:'x-font-select',
38790                 html: editor.createFontOptions()
38791             });
38792             editor.fontSelect.on('change', function(){
38793                 var font = editor.fontSelect.dom.value;
38794                 editor.relayCmd('fontname', font);
38795                 editor.deferFocus();
38796             }, editor);
38797             tb.add(
38798                 editor.fontSelect.dom,
38799                 '-'
38800             );
38801             */
38802         };
38803         if(!this.disable.formats){
38804             this.formatCombo = new Roo.form.ComboBox({
38805                 store: new Roo.data.SimpleStore({
38806                     id : 'tag',
38807                     fields: ['tag'],
38808                     data : this.formats // from states.js
38809                 }),
38810                 blockFocus : true,
38811                 //autoCreate : {tag: "div",  size: "20"},
38812                 displayField:'tag',
38813                 typeAhead: false,
38814                 mode: 'local',
38815                 editable : false,
38816                 triggerAction: 'all',
38817                 emptyText:'Add tag',
38818                 selectOnFocus:true,
38819                 width:135,
38820                 listeners : {
38821                     'select': function(c, r, i) {
38822                         editor.insertTag(r.get('tag'));
38823                         editor.focus();
38824                     }
38825                 }
38826
38827             });
38828             tb.addField(this.formatCombo);
38829             
38830         }
38831         
38832         if(!this.disable.format){
38833             tb.add(
38834                 btn('bold'),
38835                 btn('italic'),
38836                 btn('underline')
38837             );
38838         };
38839         if(!this.disable.fontSize){
38840             tb.add(
38841                 '-',
38842                 
38843                 
38844                 btn('increasefontsize', false, editor.adjustFont),
38845                 btn('decreasefontsize', false, editor.adjustFont)
38846             );
38847         };
38848         
38849         
38850         if(this.disable.colors){
38851             tb.add(
38852                 '-', {
38853                     id:editor.frameId +'-forecolor',
38854                     cls:'x-btn-icon x-edit-forecolor',
38855                     clickEvent:'mousedown',
38856                     tooltip: this.buttonTips['forecolor'] || undefined,
38857                     tabIndex:-1,
38858                     menu : new Roo.menu.ColorMenu({
38859                         allowReselect: true,
38860                         focus: Roo.emptyFn,
38861                         value:'000000',
38862                         plain:true,
38863                         selectHandler: function(cp, color){
38864                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38865                             editor.deferFocus();
38866                         },
38867                         scope: editor,
38868                         clickEvent:'mousedown'
38869                     })
38870                 }, {
38871                     id:editor.frameId +'backcolor',
38872                     cls:'x-btn-icon x-edit-backcolor',
38873                     clickEvent:'mousedown',
38874                     tooltip: this.buttonTips['backcolor'] || undefined,
38875                     tabIndex:-1,
38876                     menu : new Roo.menu.ColorMenu({
38877                         focus: Roo.emptyFn,
38878                         value:'FFFFFF',
38879                         plain:true,
38880                         allowReselect: true,
38881                         selectHandler: function(cp, color){
38882                             if(Roo.isGecko){
38883                                 editor.execCmd('useCSS', false);
38884                                 editor.execCmd('hilitecolor', color);
38885                                 editor.execCmd('useCSS', true);
38886                                 editor.deferFocus();
38887                             }else{
38888                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38889                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38890                                 editor.deferFocus();
38891                             }
38892                         },
38893                         scope:editor,
38894                         clickEvent:'mousedown'
38895                     })
38896                 }
38897             );
38898         };
38899         // now add all the items...
38900         
38901
38902         if(!this.disable.alignments){
38903             tb.add(
38904                 '-',
38905                 btn('justifyleft'),
38906                 btn('justifycenter'),
38907                 btn('justifyright')
38908             );
38909         };
38910
38911         //if(!Roo.isSafari){
38912             if(!this.disable.links){
38913                 tb.add(
38914                     '-',
38915                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38916                 );
38917             };
38918
38919             if(!this.disable.lists){
38920                 tb.add(
38921                     '-',
38922                     btn('insertorderedlist'),
38923                     btn('insertunorderedlist')
38924                 );
38925             }
38926             if(!this.disable.sourceEdit){
38927                 tb.add(
38928                     '-',
38929                     btn('sourceedit', true, function(btn){
38930                         this.toggleSourceEdit(btn.pressed);
38931                     })
38932                 );
38933             }
38934         //}
38935         
38936         var smenu = { };
38937         // special menu.. - needs to be tidied up..
38938         if (!this.disable.special) {
38939             smenu = {
38940                 text: "&#169;",
38941                 cls: 'x-edit-none',
38942                 menu : {
38943                     items : []
38944                    }
38945             };
38946             for (var i =0; i < this.specialChars.length; i++) {
38947                 smenu.menu.items.push({
38948                     
38949                     text: this.specialChars[i],
38950                     handler: function(a,b) {
38951                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38952                     },
38953                     tabIndex:-1
38954                 });
38955             }
38956             
38957             
38958             tb.add(smenu);
38959             
38960             
38961         }
38962         if (this.btns) {
38963             for(var i =0; i< this.btns.length;i++) {
38964                 var b = this.btns[i];
38965                 b.cls =  'x-edit-none';
38966                 b.scope = editor;
38967                 tb.add(b);
38968             }
38969         
38970         }
38971         
38972         
38973         
38974         // disable everything...
38975         
38976         this.tb.items.each(function(item){
38977            if(item.id != editor.frameId+ '-sourceedit'){
38978                 item.disable();
38979             }
38980         });
38981         this.rendered = true;
38982         
38983         // the all the btns;
38984         editor.on('editorevent', this.updateToolbar, this);
38985         // other toolbars need to implement this..
38986         //editor.on('editmodechange', this.updateToolbar, this);
38987     },
38988     
38989     
38990     
38991     /**
38992      * Protected method that will not generally be called directly. It triggers
38993      * a toolbar update by reading the markup state of the current selection in the editor.
38994      */
38995     updateToolbar: function(){
38996
38997         if(!this.editor.activated){
38998             this.editor.onFirstFocus();
38999             return;
39000         }
39001
39002         var btns = this.tb.items.map, 
39003             doc = this.editor.doc,
39004             frameId = this.editor.frameId;
39005
39006         if(!this.disable.font && !Roo.isSafari){
39007             /*
39008             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39009             if(name != this.fontSelect.dom.value){
39010                 this.fontSelect.dom.value = name;
39011             }
39012             */
39013         }
39014         if(!this.disable.format){
39015             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39016             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39017             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39018         }
39019         if(!this.disable.alignments){
39020             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39021             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39022             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39023         }
39024         if(!Roo.isSafari && !this.disable.lists){
39025             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39026             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39027         }
39028         
39029         var ans = this.editor.getAllAncestors();
39030         if (this.formatCombo) {
39031             
39032             
39033             var store = this.formatCombo.store;
39034             this.formatCombo.setValue("");
39035             for (var i =0; i < ans.length;i++) {
39036                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39037                     // select it..
39038                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39039                     break;
39040                 }
39041             }
39042         }
39043         
39044         
39045         
39046         // hides menus... - so this cant be on a menu...
39047         Roo.menu.MenuMgr.hideAll();
39048
39049         //this.editorsyncValue();
39050     },
39051    
39052     
39053     createFontOptions : function(){
39054         var buf = [], fs = this.fontFamilies, ff, lc;
39055         for(var i = 0, len = fs.length; i< len; i++){
39056             ff = fs[i];
39057             lc = ff.toLowerCase();
39058             buf.push(
39059                 '<option value="',lc,'" style="font-family:',ff,';"',
39060                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39061                     ff,
39062                 '</option>'
39063             );
39064         }
39065         return buf.join('');
39066     },
39067     
39068     toggleSourceEdit : function(sourceEditMode){
39069         if(sourceEditMode === undefined){
39070             sourceEditMode = !this.sourceEditMode;
39071         }
39072         this.sourceEditMode = sourceEditMode === true;
39073         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39074         // just toggle the button?
39075         if(btn.pressed !== this.editor.sourceEditMode){
39076             btn.toggle(this.editor.sourceEditMode);
39077             return;
39078         }
39079         
39080         if(this.sourceEditMode){
39081             this.tb.items.each(function(item){
39082                 if(item.cmd != 'sourceedit'){
39083                     item.disable();
39084                 }
39085             });
39086           
39087         }else{
39088             if(this.initialized){
39089                 this.tb.items.each(function(item){
39090                     item.enable();
39091                 });
39092             }
39093             
39094         }
39095         // tell the editor that it's been pressed..
39096         this.editor.toggleSourceEdit(sourceEditMode);
39097        
39098     },
39099      /**
39100      * Object collection of toolbar tooltips for the buttons in the editor. The key
39101      * is the command id associated with that button and the value is a valid QuickTips object.
39102      * For example:
39103 <pre><code>
39104 {
39105     bold : {
39106         title: 'Bold (Ctrl+B)',
39107         text: 'Make the selected text bold.',
39108         cls: 'x-html-editor-tip'
39109     },
39110     italic : {
39111         title: 'Italic (Ctrl+I)',
39112         text: 'Make the selected text italic.',
39113         cls: 'x-html-editor-tip'
39114     },
39115     ...
39116 </code></pre>
39117     * @type Object
39118      */
39119     buttonTips : {
39120         bold : {
39121             title: 'Bold (Ctrl+B)',
39122             text: 'Make the selected text bold.',
39123             cls: 'x-html-editor-tip'
39124         },
39125         italic : {
39126             title: 'Italic (Ctrl+I)',
39127             text: 'Make the selected text italic.',
39128             cls: 'x-html-editor-tip'
39129         },
39130         underline : {
39131             title: 'Underline (Ctrl+U)',
39132             text: 'Underline the selected text.',
39133             cls: 'x-html-editor-tip'
39134         },
39135         increasefontsize : {
39136             title: 'Grow Text',
39137             text: 'Increase the font size.',
39138             cls: 'x-html-editor-tip'
39139         },
39140         decreasefontsize : {
39141             title: 'Shrink Text',
39142             text: 'Decrease the font size.',
39143             cls: 'x-html-editor-tip'
39144         },
39145         backcolor : {
39146             title: 'Text Highlight Color',
39147             text: 'Change the background color of the selected text.',
39148             cls: 'x-html-editor-tip'
39149         },
39150         forecolor : {
39151             title: 'Font Color',
39152             text: 'Change the color of the selected text.',
39153             cls: 'x-html-editor-tip'
39154         },
39155         justifyleft : {
39156             title: 'Align Text Left',
39157             text: 'Align text to the left.',
39158             cls: 'x-html-editor-tip'
39159         },
39160         justifycenter : {
39161             title: 'Center Text',
39162             text: 'Center text in the editor.',
39163             cls: 'x-html-editor-tip'
39164         },
39165         justifyright : {
39166             title: 'Align Text Right',
39167             text: 'Align text to the right.',
39168             cls: 'x-html-editor-tip'
39169         },
39170         insertunorderedlist : {
39171             title: 'Bullet List',
39172             text: 'Start a bulleted list.',
39173             cls: 'x-html-editor-tip'
39174         },
39175         insertorderedlist : {
39176             title: 'Numbered List',
39177             text: 'Start a numbered list.',
39178             cls: 'x-html-editor-tip'
39179         },
39180         createlink : {
39181             title: 'Hyperlink',
39182             text: 'Make the selected text a hyperlink.',
39183             cls: 'x-html-editor-tip'
39184         },
39185         sourceedit : {
39186             title: 'Source Edit',
39187             text: 'Switch to source editing mode.',
39188             cls: 'x-html-editor-tip'
39189         }
39190     },
39191     // private
39192     onDestroy : function(){
39193         if(this.rendered){
39194             
39195             this.tb.items.each(function(item){
39196                 if(item.menu){
39197                     item.menu.removeAll();
39198                     if(item.menu.el){
39199                         item.menu.el.destroy();
39200                     }
39201                 }
39202                 item.destroy();
39203             });
39204              
39205         }
39206     },
39207     onFirstFocus: function() {
39208         this.tb.items.each(function(item){
39209            item.enable();
39210         });
39211     }
39212 });
39213
39214
39215
39216
39217 // <script type="text/javascript">
39218 /*
39219  * Based on
39220  * Ext JS Library 1.1.1
39221  * Copyright(c) 2006-2007, Ext JS, LLC.
39222  *  
39223  
39224  */
39225
39226  
39227 /**
39228  * @class Roo.form.HtmlEditor.ToolbarContext
39229  * Context Toolbar
39230  * 
39231  * Usage:
39232  *
39233  new Roo.form.HtmlEditor({
39234     ....
39235     toolbars : [
39236         new Roo.form.HtmlEditor.ToolbarStandard(),
39237         new Roo.form.HtmlEditor.ToolbarContext()
39238         })
39239     }
39240      
39241  * 
39242  * @config : {Object} disable List of elements to disable.. (not done yet.)
39243  * 
39244  * 
39245  */
39246
39247 Roo.form.HtmlEditor.ToolbarContext = function(config)
39248 {
39249     
39250     Roo.apply(this, config);
39251     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39252     // dont call parent... till later.
39253 }
39254 Roo.form.HtmlEditor.ToolbarContext.types = {
39255     'IMG' : {
39256         width : {
39257             title: "Width",
39258             width: 40
39259         },
39260         height:  {
39261             title: "Height",
39262             width: 40
39263         },
39264         align: {
39265             title: "Align",
39266             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39267             width : 80
39268             
39269         },
39270         border: {
39271             title: "Border",
39272             width: 40
39273         },
39274         alt: {
39275             title: "Alt",
39276             width: 120
39277         },
39278         src : {
39279             title: "Src",
39280             width: 220
39281         }
39282         
39283     },
39284     'A' : {
39285         name : {
39286             title: "Name",
39287             width: 50
39288         },
39289         href:  {
39290             title: "Href",
39291             width: 220
39292         } // border?
39293         
39294     },
39295     'TABLE' : {
39296         rows : {
39297             title: "Rows",
39298             width: 20
39299         },
39300         cols : {
39301             title: "Cols",
39302             width: 20
39303         },
39304         width : {
39305             title: "Width",
39306             width: 40
39307         },
39308         height : {
39309             title: "Height",
39310             width: 40
39311         },
39312         border : {
39313             title: "Border",
39314             width: 20
39315         }
39316     },
39317     'TD' : {
39318         width : {
39319             title: "Width",
39320             width: 40
39321         },
39322         height : {
39323             title: "Height",
39324             width: 40
39325         },   
39326         align: {
39327             title: "Align",
39328             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39329             width: 40
39330         },
39331         valign: {
39332             title: "Valign",
39333             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39334             width: 40
39335         },
39336         colspan: {
39337             title: "Colspan",
39338             width: 20
39339             
39340         }
39341     },
39342     'INPUT' : {
39343         name : {
39344             title: "name",
39345             width: 120
39346         },
39347         value : {
39348             title: "Value",
39349             width: 120
39350         },
39351         width : {
39352             title: "Width",
39353             width: 40
39354         }
39355     },
39356     'LABEL' : {
39357         'for' : {
39358             title: "For",
39359             width: 120
39360         }
39361     },
39362     'TEXTAREA' : {
39363           name : {
39364             title: "name",
39365             width: 120
39366         },
39367         rows : {
39368             title: "Rows",
39369             width: 20
39370         },
39371         cols : {
39372             title: "Cols",
39373             width: 20
39374         }
39375     },
39376     'SELECT' : {
39377         name : {
39378             title: "name",
39379             width: 120
39380         },
39381         selectoptions : {
39382             title: "Options",
39383             width: 200
39384         }
39385     },
39386     'BODY' : {
39387         title : {
39388             title: "title",
39389             width: 120,
39390             disabled : true
39391         }
39392     }
39393 };
39394
39395
39396
39397 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39398     
39399     tb: false,
39400     
39401     rendered: false,
39402     
39403     editor : false,
39404     /**
39405      * @cfg {Object} disable  List of toolbar elements to disable
39406          
39407      */
39408     disable : false,
39409     
39410     
39411     
39412     toolbars : false,
39413     
39414     init : function(editor)
39415     {
39416         this.editor = editor;
39417         
39418         
39419         var fid = editor.frameId;
39420         var etb = this;
39421         function btn(id, toggle, handler){
39422             var xid = fid + '-'+ id ;
39423             return {
39424                 id : xid,
39425                 cmd : id,
39426                 cls : 'x-btn-icon x-edit-'+id,
39427                 enableToggle:toggle !== false,
39428                 scope: editor, // was editor...
39429                 handler:handler||editor.relayBtnCmd,
39430                 clickEvent:'mousedown',
39431                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39432                 tabIndex:-1
39433             };
39434         }
39435         // create a new element.
39436         var wdiv = editor.wrap.createChild({
39437                 tag: 'div'
39438             }, editor.wrap.dom.firstChild.nextSibling, true);
39439         
39440         // can we do this more than once??
39441         
39442          // stop form submits
39443       
39444  
39445         // disable everything...
39446         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39447         this.toolbars = {};
39448            
39449         for (var i in  ty) {
39450             this.toolbars[i] = this.buildToolbar(ty[i],i);
39451         }
39452         this.tb = this.toolbars.BODY;
39453         this.tb.el.show();
39454         
39455          
39456         this.rendered = true;
39457         
39458         // the all the btns;
39459         editor.on('editorevent', this.updateToolbar, this);
39460         // other toolbars need to implement this..
39461         //editor.on('editmodechange', this.updateToolbar, this);
39462     },
39463     
39464     
39465     
39466     /**
39467      * Protected method that will not generally be called directly. It triggers
39468      * a toolbar update by reading the markup state of the current selection in the editor.
39469      */
39470     updateToolbar: function(){
39471
39472         if(!this.editor.activated){
39473             this.editor.onFirstFocus();
39474             return;
39475         }
39476
39477         
39478         var ans = this.editor.getAllAncestors();
39479         
39480         // pick
39481         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39482         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39483         sel = sel ? sel : this.editor.doc.body;
39484         sel = sel.tagName.length ? sel : this.editor.doc.body;
39485         var tn = sel.tagName.toUpperCase();
39486         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39487         tn = sel.tagName.toUpperCase();
39488         if (this.tb.name  == tn) {
39489             return; // no change
39490         }
39491         this.tb.el.hide();
39492         ///console.log("show: " + tn);
39493         this.tb =  this.toolbars[tn];
39494         this.tb.el.show();
39495         this.tb.fields.each(function(e) {
39496             e.setValue(sel.getAttribute(e.name));
39497         });
39498         this.tb.selectedNode = sel;
39499         
39500         
39501         Roo.menu.MenuMgr.hideAll();
39502
39503         //this.editorsyncValue();
39504     },
39505    
39506        
39507     // private
39508     onDestroy : function(){
39509         if(this.rendered){
39510             
39511             this.tb.items.each(function(item){
39512                 if(item.menu){
39513                     item.menu.removeAll();
39514                     if(item.menu.el){
39515                         item.menu.el.destroy();
39516                     }
39517                 }
39518                 item.destroy();
39519             });
39520              
39521         }
39522     },
39523     onFirstFocus: function() {
39524         // need to do this for all the toolbars..
39525         this.tb.items.each(function(item){
39526            item.enable();
39527         });
39528     },
39529     buildToolbar: function(tlist, nm)
39530     {
39531         var editor = this.editor;
39532          // create a new element.
39533         var wdiv = editor.wrap.createChild({
39534                 tag: 'div'
39535             }, editor.wrap.dom.firstChild.nextSibling, true);
39536         
39537        
39538         var tb = new Roo.Toolbar(wdiv);
39539         tb.add(nm+ ":&nbsp;");
39540         for (var i in tlist) {
39541             var item = tlist[i];
39542             tb.add(item.title + ":&nbsp;");
39543             if (item.opts) {
39544                 // fixme
39545                 
39546               
39547                 tb.addField( new Roo.form.ComboBox({
39548                     store: new Roo.data.SimpleStore({
39549                         id : 'val',
39550                         fields: ['val'],
39551                         data : item.opts // from states.js
39552                     }),
39553                     name : i,
39554                     displayField:'val',
39555                     typeAhead: false,
39556                     mode: 'local',
39557                     editable : false,
39558                     triggerAction: 'all',
39559                     emptyText:'Select',
39560                     selectOnFocus:true,
39561                     width: item.width ? item.width  : 130,
39562                     listeners : {
39563                         'select': function(c, r, i) {
39564                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39565                         }
39566                     }
39567
39568                 }));
39569                 continue;
39570                     
39571                 
39572                 
39573                 
39574                 
39575                 tb.addField( new Roo.form.TextField({
39576                     name: i,
39577                     width: 100,
39578                     //allowBlank:false,
39579                     value: ''
39580                 }));
39581                 continue;
39582             }
39583             tb.addField( new Roo.form.TextField({
39584                 name: i,
39585                 width: item.width,
39586                 //allowBlank:true,
39587                 value: '',
39588                 listeners: {
39589                     'change' : function(f, nv, ov) {
39590                         tb.selectedNode.setAttribute(f.name, nv);
39591                     }
39592                 }
39593             }));
39594              
39595         }
39596         tb.el.on('click', function(e){
39597             e.preventDefault(); // what does this do?
39598         });
39599         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39600         tb.el.hide();
39601         tb.name = nm;
39602         // dont need to disable them... as they will get hidden
39603         return tb;
39604          
39605         
39606     }
39607     
39608     
39609     
39610     
39611 });
39612
39613
39614
39615
39616
39617 /*
39618  * Based on:
39619  * Ext JS Library 1.1.1
39620  * Copyright(c) 2006-2007, Ext JS, LLC.
39621  *
39622  * Originally Released Under LGPL - original licence link has changed is not relivant.
39623  *
39624  * Fork - LGPL
39625  * <script type="text/javascript">
39626  */
39627  
39628 /**
39629  * @class Roo.form.BasicForm
39630  * @extends Roo.util.Observable
39631  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39632  * @constructor
39633  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39634  * @param {Object} config Configuration options
39635  */
39636 Roo.form.BasicForm = function(el, config){
39637     Roo.apply(this, config);
39638     /*
39639      * The Roo.form.Field items in this form.
39640      * @type MixedCollection
39641      */
39642     this.items = new Roo.util.MixedCollection(false, function(o){
39643         return o.id || (o.id = Roo.id());
39644     });
39645     this.addEvents({
39646         /**
39647          * @event beforeaction
39648          * Fires before any action is performed. Return false to cancel the action.
39649          * @param {Form} this
39650          * @param {Action} action The action to be performed
39651          */
39652         beforeaction: true,
39653         /**
39654          * @event actionfailed
39655          * Fires when an action fails.
39656          * @param {Form} this
39657          * @param {Action} action The action that failed
39658          */
39659         actionfailed : true,
39660         /**
39661          * @event actioncomplete
39662          * Fires when an action is completed.
39663          * @param {Form} this
39664          * @param {Action} action The action that completed
39665          */
39666         actioncomplete : true
39667     });
39668     if(el){
39669         this.initEl(el);
39670     }
39671     Roo.form.BasicForm.superclass.constructor.call(this);
39672 };
39673
39674 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39675     /**
39676      * @cfg {String} method
39677      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39678      */
39679     /**
39680      * @cfg {DataReader} reader
39681      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39682      * This is optional as there is built-in support for processing JSON.
39683      */
39684     /**
39685      * @cfg {DataReader} errorReader
39686      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39687      * This is completely optional as there is built-in support for processing JSON.
39688      */
39689     /**
39690      * @cfg {String} url
39691      * The URL to use for form actions if one isn't supplied in the action options.
39692      */
39693     /**
39694      * @cfg {Boolean} fileUpload
39695      * Set to true if this form is a file upload.
39696      */
39697     /**
39698      * @cfg {Object} baseParams
39699      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39700      */
39701     /**
39702      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39703      */
39704     timeout: 30,
39705
39706     // private
39707     activeAction : null,
39708
39709     /**
39710      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39711      * or setValues() data instead of when the form was first created.
39712      */
39713     trackResetOnLoad : false,
39714     
39715     
39716     /**
39717      * childForms - used for multi-tab forms
39718      * @type {Array}
39719      */
39720     childForms : false,
39721     
39722     /**
39723      * allFields - full list of fields.
39724      * @type {Array}
39725      */
39726     allFields : false,
39727     
39728     /**
39729      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39730      * element by passing it or its id or mask the form itself by passing in true.
39731      * @type Mixed
39732      */
39733     waitMsgTarget : undefined,
39734
39735     // private
39736     initEl : function(el){
39737         this.el = Roo.get(el);
39738         this.id = this.el.id || Roo.id();
39739         this.el.on('submit', this.onSubmit, this);
39740         this.el.addClass('x-form');
39741     },
39742
39743     // private
39744     onSubmit : function(e){
39745         e.stopEvent();
39746     },
39747
39748     /**
39749      * Returns true if client-side validation on the form is successful.
39750      * @return Boolean
39751      */
39752     isValid : function(){
39753         var valid = true;
39754         this.items.each(function(f){
39755            if(!f.validate()){
39756                valid = false;
39757            }
39758         });
39759         return valid;
39760     },
39761
39762     /**
39763      * Returns true if any fields in this form have changed since their original load.
39764      * @return Boolean
39765      */
39766     isDirty : function(){
39767         var dirty = false;
39768         this.items.each(function(f){
39769            if(f.isDirty()){
39770                dirty = true;
39771                return false;
39772            }
39773         });
39774         return dirty;
39775     },
39776
39777     /**
39778      * Performs a predefined action (submit or load) or custom actions you define on this form.
39779      * @param {String} actionName The name of the action type
39780      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39781      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39782      * accept other config options):
39783      * <pre>
39784 Property          Type             Description
39785 ----------------  ---------------  ----------------------------------------------------------------------------------
39786 url               String           The url for the action (defaults to the form's url)
39787 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39788 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39789 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39790                                    validate the form on the client (defaults to false)
39791      * </pre>
39792      * @return {BasicForm} this
39793      */
39794     doAction : function(action, options){
39795         if(typeof action == 'string'){
39796             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39797         }
39798         if(this.fireEvent('beforeaction', this, action) !== false){
39799             this.beforeAction(action);
39800             action.run.defer(100, action);
39801         }
39802         return this;
39803     },
39804
39805     /**
39806      * Shortcut to do a submit action.
39807      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39808      * @return {BasicForm} this
39809      */
39810     submit : function(options){
39811         this.doAction('submit', options);
39812         return this;
39813     },
39814
39815     /**
39816      * Shortcut to do a load action.
39817      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39818      * @return {BasicForm} this
39819      */
39820     load : function(options){
39821         this.doAction('load', options);
39822         return this;
39823     },
39824
39825     /**
39826      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39827      * @param {Record} record The record to edit
39828      * @return {BasicForm} this
39829      */
39830     updateRecord : function(record){
39831         record.beginEdit();
39832         var fs = record.fields;
39833         fs.each(function(f){
39834             var field = this.findField(f.name);
39835             if(field){
39836                 record.set(f.name, field.getValue());
39837             }
39838         }, this);
39839         record.endEdit();
39840         return this;
39841     },
39842
39843     /**
39844      * Loads an Roo.data.Record into this form.
39845      * @param {Record} record The record to load
39846      * @return {BasicForm} this
39847      */
39848     loadRecord : function(record){
39849         this.setValues(record.data);
39850         return this;
39851     },
39852
39853     // private
39854     beforeAction : function(action){
39855         var o = action.options;
39856         if(o.waitMsg){
39857             if(this.waitMsgTarget === true){
39858                 this.el.mask(o.waitMsg, 'x-mask-loading');
39859             }else if(this.waitMsgTarget){
39860                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39861                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39862             }else{
39863                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39864             }
39865         }
39866     },
39867
39868     // private
39869     afterAction : function(action, success){
39870         this.activeAction = null;
39871         var o = action.options;
39872         if(o.waitMsg){
39873             if(this.waitMsgTarget === true){
39874                 this.el.unmask();
39875             }else if(this.waitMsgTarget){
39876                 this.waitMsgTarget.unmask();
39877             }else{
39878                 Roo.MessageBox.updateProgress(1);
39879                 Roo.MessageBox.hide();
39880             }
39881         }
39882         if(success){
39883             if(o.reset){
39884                 this.reset();
39885             }
39886             Roo.callback(o.success, o.scope, [this, action]);
39887             this.fireEvent('actioncomplete', this, action);
39888         }else{
39889             Roo.callback(o.failure, o.scope, [this, action]);
39890             this.fireEvent('actionfailed', this, action);
39891         }
39892     },
39893
39894     /**
39895      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
39896      * @param {String} id The value to search for
39897      * @return Field
39898      */
39899     findField : function(id){
39900         var field = this.items.get(id);
39901         if(!field){
39902             this.items.each(function(f){
39903                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
39904                     field = f;
39905                     return false;
39906                 }
39907             });
39908         }
39909         return field || null;
39910     },
39911
39912     /**
39913      * Add a secondary form to this one, 
39914      * Used to provide tabbed forms. One form is primary, with hidden values 
39915      * which mirror the elements from the other forms.
39916      * 
39917      * @param {Roo.form.Form} form to add.
39918      * 
39919      */
39920     addForm : function(form){
39921        
39922         this.childForms.push(form);
39923         form.allItems.each(function (fe) {
39924             
39925             if (this.findField(fe.name)) { // already added..
39926                 return;
39927             }
39928             this.add( new Roo.form.Hidden({
39929                 name : fe.name
39930             }));
39931         }, this);
39932         
39933     },
39934     /**
39935      * Mark fields in this form invalid in bulk.
39936      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
39937      * @return {BasicForm} this
39938      */
39939     markInvalid : function(errors){
39940         if(errors instanceof Array){
39941             for(var i = 0, len = errors.length; i < len; i++){
39942                 var fieldError = errors[i];
39943                 var f = this.findField(fieldError.id);
39944                 if(f){
39945                     f.markInvalid(fieldError.msg);
39946                 }
39947             }
39948         }else{
39949             var field, id;
39950             for(id in errors){
39951                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
39952                     field.markInvalid(errors[id]);
39953                 }
39954             }
39955         }
39956         Roo.each(this.childForms || [], function (f) {
39957             f.markInvalid(errors);
39958         });
39959         
39960         return this;
39961     },
39962
39963     /**
39964      * Set values for fields in this form in bulk.
39965      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
39966      * @return {BasicForm} this
39967      */
39968     setValues : function(values){
39969         if(values instanceof Array){ // array of objects
39970             for(var i = 0, len = values.length; i < len; i++){
39971                 var v = values[i];
39972                 var f = this.findField(v.id);
39973                 if(f){
39974                     f.setValue(v.value);
39975                     if(this.trackResetOnLoad){
39976                         f.originalValue = f.getValue();
39977                     }
39978                 }
39979             }
39980         }else{ // object hash
39981             var field, id;
39982             for(id in values){
39983                 if(typeof values[id] != 'function' && (field = this.findField(id))){
39984                     
39985                     if (field.setFromData && 
39986                         field.valueField && 
39987                         field.displayField &&
39988                         // combos' with local stores can 
39989                         // be queried via setValue()
39990                         // to set their value..
39991                         (field.store && !field.store.isLocal)
39992                         ) {
39993                         // it's a combo
39994                         var sd = { };
39995                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
39996                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
39997                         field.setFromData(sd);
39998                         
39999                     } else {
40000                         field.setValue(values[id]);
40001                     }
40002                     
40003                     
40004                     if(this.trackResetOnLoad){
40005                         field.originalValue = field.getValue();
40006                     }
40007                 }
40008             }
40009         }
40010          
40011         Roo.each(this.childForms || [], function (f) {
40012             f.setValues(values);
40013         });
40014                 
40015         return this;
40016     },
40017
40018     /**
40019      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40020      * they are returned as an array.
40021      * @param {Boolean} asString
40022      * @return {Object}
40023      */
40024     getValues : function(asString){
40025         if (this.childForms) {
40026             // copy values from the child forms
40027             Roo.each(this.childForms, function (f) {
40028                 if (f.allFields) {
40029                     Roo.each(f.allFields, function (e) {
40030                         if (e.name && e.getValue && this.findField(e.name)) {
40031                             this.findField(e.name).setValue(e.getValue());
40032                         }
40033                     });
40034                 }
40035             }, this);
40036         }
40037         
40038         
40039         
40040         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40041         if(asString === true){
40042             return fs;
40043         }
40044         return Roo.urlDecode(fs);
40045     },
40046
40047     /**
40048      * Clears all invalid messages in this form.
40049      * @return {BasicForm} this
40050      */
40051     clearInvalid : function(){
40052         this.items.each(function(f){
40053            f.clearInvalid();
40054         });
40055         
40056         Roo.each(this.childForms || [], function (f) {
40057             f.clearInvalid();
40058         });
40059         
40060         
40061         return this;
40062     },
40063
40064     /**
40065      * Resets this form.
40066      * @return {BasicForm} this
40067      */
40068     reset : function(){
40069         this.items.each(function(f){
40070             f.reset();
40071         });
40072         
40073         Roo.each(this.childForms || [], function (f) {
40074             f.reset();
40075         });
40076        
40077         
40078         return this;
40079     },
40080
40081     /**
40082      * Add Roo.form components to this form.
40083      * @param {Field} field1
40084      * @param {Field} field2 (optional)
40085      * @param {Field} etc (optional)
40086      * @return {BasicForm} this
40087      */
40088     add : function(){
40089         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40090         return this;
40091     },
40092
40093
40094     /**
40095      * Removes a field from the items collection (does NOT remove its markup).
40096      * @param {Field} field
40097      * @return {BasicForm} this
40098      */
40099     remove : function(field){
40100         this.items.remove(field);
40101         return this;
40102     },
40103
40104     /**
40105      * Looks at the fields in this form, checks them for an id attribute,
40106      * and calls applyTo on the existing dom element with that id.
40107      * @return {BasicForm} this
40108      */
40109     render : function(){
40110         this.items.each(function(f){
40111             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40112                 f.applyTo(f.id);
40113             }
40114         });
40115         return this;
40116     },
40117
40118     /**
40119      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40120      * @param {Object} values
40121      * @return {BasicForm} this
40122      */
40123     applyToFields : function(o){
40124         this.items.each(function(f){
40125            Roo.apply(f, o);
40126         });
40127         return this;
40128     },
40129
40130     /**
40131      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40132      * @param {Object} values
40133      * @return {BasicForm} this
40134      */
40135     applyIfToFields : function(o){
40136         this.items.each(function(f){
40137            Roo.applyIf(f, o);
40138         });
40139         return this;
40140     }
40141 });
40142
40143 // back compat
40144 Roo.BasicForm = Roo.form.BasicForm;/*
40145  * Based on:
40146  * Ext JS Library 1.1.1
40147  * Copyright(c) 2006-2007, Ext JS, LLC.
40148  *
40149  * Originally Released Under LGPL - original licence link has changed is not relivant.
40150  *
40151  * Fork - LGPL
40152  * <script type="text/javascript">
40153  */
40154
40155 /**
40156  * @class Roo.form.Form
40157  * @extends Roo.form.BasicForm
40158  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40159  * @constructor
40160  * @param {Object} config Configuration options
40161  */
40162 Roo.form.Form = function(config){
40163     var xitems =  [];
40164     if (config.items) {
40165         xitems = config.items;
40166         delete config.items;
40167     }
40168     this.childForms = [];
40169     
40170     Roo.form.Form.superclass.constructor.call(this, null, config);
40171     this.url = this.url || this.action;
40172     if(!this.root){
40173         this.root = new Roo.form.Layout(Roo.applyIf({
40174             id: Roo.id()
40175         }, config));
40176     }
40177     this.active = this.root;
40178     /**
40179      * Array of all the buttons that have been added to this form via {@link addButton}
40180      * @type Array
40181      */
40182     this.buttons = [];
40183     this.allItems = [];
40184     this.addEvents({
40185         /**
40186          * @event clientvalidation
40187          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40188          * @param {Form} this
40189          * @param {Boolean} valid true if the form has passed client-side validation
40190          */
40191         clientvalidation: true,
40192         /**
40193          * @event rendered
40194          * Fires when the form is rendered
40195          * @param {Roo.form.Form} form
40196          */
40197         rendered : true
40198     });
40199     
40200     Roo.each(xitems, this.addxtype, this);
40201     
40202     
40203     
40204 };
40205
40206 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40207     /**
40208      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40209      */
40210     /**
40211      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40212      */
40213     /**
40214      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40215      */
40216     buttonAlign:'center',
40217
40218     /**
40219      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40220      */
40221     minButtonWidth:75,
40222
40223     /**
40224      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40225      * This property cascades to child containers if not set.
40226      */
40227     labelAlign:'left',
40228
40229     /**
40230      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40231      * fires a looping event with that state. This is required to bind buttons to the valid
40232      * state using the config value formBind:true on the button.
40233      */
40234     monitorValid : false,
40235
40236     /**
40237      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40238      */
40239     monitorPoll : 200,
40240
40241   
40242     /**
40243      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40244      * fields are added and the column is closed. If no fields are passed the column remains open
40245      * until end() is called.
40246      * @param {Object} config The config to pass to the column
40247      * @param {Field} field1 (optional)
40248      * @param {Field} field2 (optional)
40249      * @param {Field} etc (optional)
40250      * @return Column The column container object
40251      */
40252     column : function(c){
40253         var col = new Roo.form.Column(c);
40254         this.start(col);
40255         if(arguments.length > 1){ // duplicate code required because of Opera
40256             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40257             this.end();
40258         }
40259         return col;
40260     },
40261
40262     /**
40263      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40264      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40265      * until end() is called.
40266      * @param {Object} config The config to pass to the fieldset
40267      * @param {Field} field1 (optional)
40268      * @param {Field} field2 (optional)
40269      * @param {Field} etc (optional)
40270      * @return FieldSet The fieldset container object
40271      */
40272     fieldset : function(c){
40273         var fs = new Roo.form.FieldSet(c);
40274         this.start(fs);
40275         if(arguments.length > 1){ // duplicate code required because of Opera
40276             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40277             this.end();
40278         }
40279         return fs;
40280     },
40281
40282     /**
40283      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40284      * fields are added and the container is closed. If no fields are passed the container remains open
40285      * until end() is called.
40286      * @param {Object} config The config to pass to the Layout
40287      * @param {Field} field1 (optional)
40288      * @param {Field} field2 (optional)
40289      * @param {Field} etc (optional)
40290      * @return Layout The container object
40291      */
40292     container : function(c){
40293         var l = new Roo.form.Layout(c);
40294         this.start(l);
40295         if(arguments.length > 1){ // duplicate code required because of Opera
40296             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40297             this.end();
40298         }
40299         return l;
40300     },
40301
40302     /**
40303      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40304      * @param {Object} container A Roo.form.Layout or subclass of Layout
40305      * @return {Form} this
40306      */
40307     start : function(c){
40308         // cascade label info
40309         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40310         this.active.stack.push(c);
40311         c.ownerCt = this.active;
40312         this.active = c;
40313         return this;
40314     },
40315
40316     /**
40317      * Closes the current open container
40318      * @return {Form} this
40319      */
40320     end : function(){
40321         if(this.active == this.root){
40322             return this;
40323         }
40324         this.active = this.active.ownerCt;
40325         return this;
40326     },
40327
40328     /**
40329      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40330      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40331      * as the label of the field.
40332      * @param {Field} field1
40333      * @param {Field} field2 (optional)
40334      * @param {Field} etc. (optional)
40335      * @return {Form} this
40336      */
40337     add : function(){
40338         this.active.stack.push.apply(this.active.stack, arguments);
40339         this.allItems.push.apply(this.allItems,arguments);
40340         var r = [];
40341         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40342             if(a[i].isFormField){
40343                 r.push(a[i]);
40344             }
40345         }
40346         if(r.length > 0){
40347             Roo.form.Form.superclass.add.apply(this, r);
40348         }
40349         return this;
40350     },
40351     
40352
40353     
40354     
40355     
40356      /**
40357      * Find any element that has been added to a form, using it's ID or name
40358      * This can include framesets, columns etc. along with regular fields..
40359      * @param {String} id - id or name to find.
40360      
40361      * @return {Element} e - or false if nothing found.
40362      */
40363     findbyId : function(id)
40364     {
40365         var ret = false;
40366         if (!id) {
40367             return ret;
40368         }
40369         Ext.each(this.allItems, function(f){
40370             if (f.id == id || f.name == id ){
40371                 ret = f;
40372                 return false;
40373             }
40374         });
40375         return ret;
40376     },
40377
40378     
40379     
40380     /**
40381      * Render this form into the passed container. This should only be called once!
40382      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40383      * @return {Form} this
40384      */
40385     render : function(ct){
40386         ct = Roo.get(ct);
40387         var o = this.autoCreate || {
40388             tag: 'form',
40389             method : this.method || 'POST',
40390             id : this.id || Roo.id()
40391         };
40392         this.initEl(ct.createChild(o));
40393
40394         this.root.render(this.el);
40395
40396         this.items.each(function(f){
40397             f.render('x-form-el-'+f.id);
40398         });
40399
40400         if(this.buttons.length > 0){
40401             // tables are required to maintain order and for correct IE layout
40402             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40403                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40404                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40405             }}, null, true);
40406             var tr = tb.getElementsByTagName('tr')[0];
40407             for(var i = 0, len = this.buttons.length; i < len; i++) {
40408                 var b = this.buttons[i];
40409                 var td = document.createElement('td');
40410                 td.className = 'x-form-btn-td';
40411                 b.render(tr.appendChild(td));
40412             }
40413         }
40414         if(this.monitorValid){ // initialize after render
40415             this.startMonitoring();
40416         }
40417         this.fireEvent('rendered', this);
40418         return this;
40419     },
40420
40421     /**
40422      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40423      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40424      * object or a valid Roo.DomHelper element config
40425      * @param {Function} handler The function called when the button is clicked
40426      * @param {Object} scope (optional) The scope of the handler function
40427      * @return {Roo.Button}
40428      */
40429     addButton : function(config, handler, scope){
40430         var bc = {
40431             handler: handler,
40432             scope: scope,
40433             minWidth: this.minButtonWidth,
40434             hideParent:true
40435         };
40436         if(typeof config == "string"){
40437             bc.text = config;
40438         }else{
40439             Roo.apply(bc, config);
40440         }
40441         var btn = new Roo.Button(null, bc);
40442         this.buttons.push(btn);
40443         return btn;
40444     },
40445
40446      /**
40447      * Adds a series of form elements (using the xtype property as the factory method.
40448      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40449      * @param {Object} config 
40450      */
40451     
40452     addxtype : function()
40453     {
40454         var ar = Array.prototype.slice.call(arguments, 0);
40455         var ret = false;
40456         for(var i = 0; i < ar.length; i++) {
40457             if (!ar[i]) {
40458                 continue; // skip -- if this happends something invalid got sent, we 
40459                 // should ignore it, as basically that interface element will not show up
40460                 // and that should be pretty obvious!!
40461             }
40462             
40463             if (Roo.form[ar[i].xtype]) {
40464                 ar[i].form = this;
40465                 var fe = Roo.factory(ar[i], Roo.form);
40466                 if (!ret) {
40467                     ret = fe;
40468                 }
40469                 fe.form = this;
40470                 if (fe.store) {
40471                     fe.store.form = this;
40472                 }
40473                 if (fe.isLayout) {  
40474                          
40475                     this.start(fe);
40476                     this.allItems.push(fe);
40477                     if (fe.items && fe.addxtype) {
40478                         fe.addxtype.apply(fe, fe.items);
40479                         delete fe.items;
40480                     }
40481                      this.end();
40482                     continue;
40483                 }
40484                 
40485                 
40486                  
40487                 this.add(fe);
40488               //  console.log('adding ' + ar[i].xtype);
40489             }
40490             if (ar[i].xtype == 'Button') {  
40491                 //console.log('adding button');
40492                 //console.log(ar[i]);
40493                 this.addButton(ar[i]);
40494                 this.allItems.push(fe);
40495                 continue;
40496             }
40497             
40498             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40499                 alert('end is not supported on xtype any more, use items');
40500             //    this.end();
40501             //    //console.log('adding end');
40502             }
40503             
40504         }
40505         return ret;
40506     },
40507     
40508     /**
40509      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40510      * option "monitorValid"
40511      */
40512     startMonitoring : function(){
40513         if(!this.bound){
40514             this.bound = true;
40515             Roo.TaskMgr.start({
40516                 run : this.bindHandler,
40517                 interval : this.monitorPoll || 200,
40518                 scope: this
40519             });
40520         }
40521     },
40522
40523     /**
40524      * Stops monitoring of the valid state of this form
40525      */
40526     stopMonitoring : function(){
40527         this.bound = false;
40528     },
40529
40530     // private
40531     bindHandler : function(){
40532         if(!this.bound){
40533             return false; // stops binding
40534         }
40535         var valid = true;
40536         this.items.each(function(f){
40537             if(!f.isValid(true)){
40538                 valid = false;
40539                 return false;
40540             }
40541         });
40542         for(var i = 0, len = this.buttons.length; i < len; i++){
40543             var btn = this.buttons[i];
40544             if(btn.formBind === true && btn.disabled === valid){
40545                 btn.setDisabled(!valid);
40546             }
40547         }
40548         this.fireEvent('clientvalidation', this, valid);
40549     }
40550     
40551     
40552     
40553     
40554     
40555     
40556     
40557     
40558 });
40559
40560
40561 // back compat
40562 Roo.Form = Roo.form.Form;
40563 /*
40564  * Based on:
40565  * Ext JS Library 1.1.1
40566  * Copyright(c) 2006-2007, Ext JS, LLC.
40567  *
40568  * Originally Released Under LGPL - original licence link has changed is not relivant.
40569  *
40570  * Fork - LGPL
40571  * <script type="text/javascript">
40572  */
40573  
40574  /**
40575  * @class Roo.form.Action
40576  * Internal Class used to handle form actions
40577  * @constructor
40578  * @param {Roo.form.BasicForm} el The form element or its id
40579  * @param {Object} config Configuration options
40580  */
40581  
40582  
40583 // define the action interface
40584 Roo.form.Action = function(form, options){
40585     this.form = form;
40586     this.options = options || {};
40587 };
40588 /**
40589  * Client Validation Failed
40590  * @const 
40591  */
40592 Roo.form.Action.CLIENT_INVALID = 'client';
40593 /**
40594  * Server Validation Failed
40595  * @const 
40596  */
40597  Roo.form.Action.SERVER_INVALID = 'server';
40598  /**
40599  * Connect to Server Failed
40600  * @const 
40601  */
40602 Roo.form.Action.CONNECT_FAILURE = 'connect';
40603 /**
40604  * Reading Data from Server Failed
40605  * @const 
40606  */
40607 Roo.form.Action.LOAD_FAILURE = 'load';
40608
40609 Roo.form.Action.prototype = {
40610     type : 'default',
40611     failureType : undefined,
40612     response : undefined,
40613     result : undefined,
40614
40615     // interface method
40616     run : function(options){
40617
40618     },
40619
40620     // interface method
40621     success : function(response){
40622
40623     },
40624
40625     // interface method
40626     handleResponse : function(response){
40627
40628     },
40629
40630     // default connection failure
40631     failure : function(response){
40632         this.response = response;
40633         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40634         this.form.afterAction(this, false);
40635     },
40636
40637     processResponse : function(response){
40638         this.response = response;
40639         if(!response.responseText){
40640             return true;
40641         }
40642         this.result = this.handleResponse(response);
40643         return this.result;
40644     },
40645
40646     // utility functions used internally
40647     getUrl : function(appendParams){
40648         var url = this.options.url || this.form.url || this.form.el.dom.action;
40649         if(appendParams){
40650             var p = this.getParams();
40651             if(p){
40652                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40653             }
40654         }
40655         return url;
40656     },
40657
40658     getMethod : function(){
40659         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40660     },
40661
40662     getParams : function(){
40663         var bp = this.form.baseParams;
40664         var p = this.options.params;
40665         if(p){
40666             if(typeof p == "object"){
40667                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40668             }else if(typeof p == 'string' && bp){
40669                 p += '&' + Roo.urlEncode(bp);
40670             }
40671         }else if(bp){
40672             p = Roo.urlEncode(bp);
40673         }
40674         return p;
40675     },
40676
40677     createCallback : function(){
40678         return {
40679             success: this.success,
40680             failure: this.failure,
40681             scope: this,
40682             timeout: (this.form.timeout*1000),
40683             upload: this.form.fileUpload ? this.success : undefined
40684         };
40685     }
40686 };
40687
40688 Roo.form.Action.Submit = function(form, options){
40689     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40690 };
40691
40692 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40693     type : 'submit',
40694
40695     run : function(){
40696         var o = this.options;
40697         var method = this.getMethod();
40698         var isPost = method == 'POST';
40699         if(o.clientValidation === false || this.form.isValid()){
40700             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40701                 form:this.form.el.dom,
40702                 url:this.getUrl(!isPost),
40703                 method: method,
40704                 params:isPost ? this.getParams() : null,
40705                 isUpload: this.form.fileUpload
40706             }));
40707
40708         }else if (o.clientValidation !== false){ // client validation failed
40709             this.failureType = Roo.form.Action.CLIENT_INVALID;
40710             this.form.afterAction(this, false);
40711         }
40712     },
40713
40714     success : function(response){
40715         var result = this.processResponse(response);
40716         if(result === true || result.success){
40717             this.form.afterAction(this, true);
40718             return;
40719         }
40720         if(result.errors){
40721             this.form.markInvalid(result.errors);
40722             this.failureType = Roo.form.Action.SERVER_INVALID;
40723         }
40724         this.form.afterAction(this, false);
40725     },
40726
40727     handleResponse : function(response){
40728         if(this.form.errorReader){
40729             var rs = this.form.errorReader.read(response);
40730             var errors = [];
40731             if(rs.records){
40732                 for(var i = 0, len = rs.records.length; i < len; i++) {
40733                     var r = rs.records[i];
40734                     errors[i] = r.data;
40735                 }
40736             }
40737             if(errors.length < 1){
40738                 errors = null;
40739             }
40740             return {
40741                 success : rs.success,
40742                 errors : errors
40743             };
40744         }
40745         var ret = false;
40746         try {
40747             ret = Roo.decode(response.responseText);
40748         } catch (e) {
40749             ret = {
40750                 success: false,
40751                 errorMsg: "Failed to read server message: " + response.responseText,
40752                 errors : []
40753             };
40754         }
40755         return ret;
40756         
40757     }
40758 });
40759
40760
40761 Roo.form.Action.Load = function(form, options){
40762     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40763     this.reader = this.form.reader;
40764 };
40765
40766 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40767     type : 'load',
40768
40769     run : function(){
40770         Roo.Ajax.request(Roo.apply(
40771                 this.createCallback(), {
40772                     method:this.getMethod(),
40773                     url:this.getUrl(false),
40774                     params:this.getParams()
40775         }));
40776     },
40777
40778     success : function(response){
40779         var result = this.processResponse(response);
40780         if(result === true || !result.success || !result.data){
40781             this.failureType = Roo.form.Action.LOAD_FAILURE;
40782             this.form.afterAction(this, false);
40783             return;
40784         }
40785         this.form.clearInvalid();
40786         this.form.setValues(result.data);
40787         this.form.afterAction(this, true);
40788     },
40789
40790     handleResponse : function(response){
40791         if(this.form.reader){
40792             var rs = this.form.reader.read(response);
40793             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40794             return {
40795                 success : rs.success,
40796                 data : data
40797             };
40798         }
40799         return Roo.decode(response.responseText);
40800     }
40801 });
40802
40803 Roo.form.Action.ACTION_TYPES = {
40804     'load' : Roo.form.Action.Load,
40805     'submit' : Roo.form.Action.Submit
40806 };/*
40807  * Based on:
40808  * Ext JS Library 1.1.1
40809  * Copyright(c) 2006-2007, Ext JS, LLC.
40810  *
40811  * Originally Released Under LGPL - original licence link has changed is not relivant.
40812  *
40813  * Fork - LGPL
40814  * <script type="text/javascript">
40815  */
40816  
40817 /**
40818  * @class Roo.form.Layout
40819  * @extends Roo.Component
40820  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40821  * @constructor
40822  * @param {Object} config Configuration options
40823  */
40824 Roo.form.Layout = function(config){
40825     var xitems = [];
40826     if (config.items) {
40827         xitems = config.items;
40828         delete config.items;
40829     }
40830     Roo.form.Layout.superclass.constructor.call(this, config);
40831     this.stack = [];
40832     Roo.each(xitems, this.addxtype, this);
40833      
40834 };
40835
40836 Roo.extend(Roo.form.Layout, Roo.Component, {
40837     /**
40838      * @cfg {String/Object} autoCreate
40839      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40840      */
40841     /**
40842      * @cfg {String/Object/Function} style
40843      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40844      * a function which returns such a specification.
40845      */
40846     /**
40847      * @cfg {String} labelAlign
40848      * Valid values are "left," "top" and "right" (defaults to "left")
40849      */
40850     /**
40851      * @cfg {Number} labelWidth
40852      * Fixed width in pixels of all field labels (defaults to undefined)
40853      */
40854     /**
40855      * @cfg {Boolean} clear
40856      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40857      */
40858     clear : true,
40859     /**
40860      * @cfg {String} labelSeparator
40861      * The separator to use after field labels (defaults to ':')
40862      */
40863     labelSeparator : ':',
40864     /**
40865      * @cfg {Boolean} hideLabels
40866      * True to suppress the display of field labels in this layout (defaults to false)
40867      */
40868     hideLabels : false,
40869
40870     // private
40871     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40872     
40873     isLayout : true,
40874     
40875     // private
40876     onRender : function(ct, position){
40877         if(this.el){ // from markup
40878             this.el = Roo.get(this.el);
40879         }else {  // generate
40880             var cfg = this.getAutoCreate();
40881             this.el = ct.createChild(cfg, position);
40882         }
40883         if(this.style){
40884             this.el.applyStyles(this.style);
40885         }
40886         if(this.labelAlign){
40887             this.el.addClass('x-form-label-'+this.labelAlign);
40888         }
40889         if(this.hideLabels){
40890             this.labelStyle = "display:none";
40891             this.elementStyle = "padding-left:0;";
40892         }else{
40893             if(typeof this.labelWidth == 'number'){
40894                 this.labelStyle = "width:"+this.labelWidth+"px;";
40895                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
40896             }
40897             if(this.labelAlign == 'top'){
40898                 this.labelStyle = "width:auto;";
40899                 this.elementStyle = "padding-left:0;";
40900             }
40901         }
40902         var stack = this.stack;
40903         var slen = stack.length;
40904         if(slen > 0){
40905             if(!this.fieldTpl){
40906                 var t = new Roo.Template(
40907                     '<div class="x-form-item {5}">',
40908                         '<label for="{0}" style="{2}">{1}{4}</label>',
40909                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
40910                         '</div>',
40911                     '</div><div class="x-form-clear-left"></div>'
40912                 );
40913                 t.disableFormats = true;
40914                 t.compile();
40915                 Roo.form.Layout.prototype.fieldTpl = t;
40916             }
40917             for(var i = 0; i < slen; i++) {
40918                 if(stack[i].isFormField){
40919                     this.renderField(stack[i]);
40920                 }else{
40921                     this.renderComponent(stack[i]);
40922                 }
40923             }
40924         }
40925         if(this.clear){
40926             this.el.createChild({cls:'x-form-clear'});
40927         }
40928     },
40929
40930     // private
40931     renderField : function(f){
40932         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
40933                f.id, //0
40934                f.fieldLabel, //1
40935                f.labelStyle||this.labelStyle||'', //2
40936                this.elementStyle||'', //3
40937                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
40938                f.itemCls||this.itemCls||''  //5
40939        ], true).getPrevSibling());
40940     },
40941
40942     // private
40943     renderComponent : function(c){
40944         c.render(c.isLayout ? this.el : this.el.createChild());    
40945     },
40946     /**
40947      * Adds a object form elements (using the xtype property as the factory method.)
40948      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
40949      * @param {Object} config 
40950      */
40951     addxtype : function(o)
40952     {
40953         // create the lement.
40954         o.form = this.form;
40955         var fe = Roo.factory(o, Roo.form);
40956         this.form.allItems.push(fe);
40957         this.stack.push(fe);
40958         
40959         if (fe.isFormField) {
40960             this.form.items.add(fe);
40961         }
40962          
40963         return fe;
40964     }
40965 });
40966
40967 /**
40968  * @class Roo.form.Column
40969  * @extends Roo.form.Layout
40970  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
40971  * @constructor
40972  * @param {Object} config Configuration options
40973  */
40974 Roo.form.Column = function(config){
40975     Roo.form.Column.superclass.constructor.call(this, config);
40976 };
40977
40978 Roo.extend(Roo.form.Column, Roo.form.Layout, {
40979     /**
40980      * @cfg {Number/String} width
40981      * The fixed width of the column in pixels or CSS value (defaults to "auto")
40982      */
40983     /**
40984      * @cfg {String/Object} autoCreate
40985      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
40986      */
40987
40988     // private
40989     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
40990
40991     // private
40992     onRender : function(ct, position){
40993         Roo.form.Column.superclass.onRender.call(this, ct, position);
40994         if(this.width){
40995             this.el.setWidth(this.width);
40996         }
40997     }
40998 });
40999
41000
41001 /**
41002  * @class Roo.form.Row
41003  * @extends Roo.form.Layout
41004  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41005  * @constructor
41006  * @param {Object} config Configuration options
41007  */
41008
41009  
41010 Roo.form.Row = function(config){
41011     Roo.form.Row.superclass.constructor.call(this, config);
41012 };
41013  
41014 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41015       /**
41016      * @cfg {Number/String} width
41017      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41018      */
41019     /**
41020      * @cfg {Number/String} height
41021      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41022      */
41023     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41024     
41025     padWidth : 20,
41026     // private
41027     onRender : function(ct, position){
41028         //console.log('row render');
41029         if(!this.rowTpl){
41030             var t = new Roo.Template(
41031                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41032                     '<label for="{0}" style="{2}">{1}{4}</label>',
41033                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41034                     '</div>',
41035                 '</div>'
41036             );
41037             t.disableFormats = true;
41038             t.compile();
41039             Roo.form.Layout.prototype.rowTpl = t;
41040         }
41041         this.fieldTpl = this.rowTpl;
41042         
41043         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41044         var labelWidth = 100;
41045         
41046         if ((this.labelAlign != 'top')) {
41047             if (typeof this.labelWidth == 'number') {
41048                 labelWidth = this.labelWidth
41049             }
41050             this.padWidth =  20 + labelWidth;
41051             
41052         }
41053         
41054         Roo.form.Column.superclass.onRender.call(this, ct, position);
41055         if(this.width){
41056             this.el.setWidth(this.width);
41057         }
41058         if(this.height){
41059             this.el.setHeight(this.height);
41060         }
41061     },
41062     
41063     // private
41064     renderField : function(f){
41065         f.fieldEl = this.fieldTpl.append(this.el, [
41066                f.id, f.fieldLabel,
41067                f.labelStyle||this.labelStyle||'',
41068                this.elementStyle||'',
41069                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41070                f.itemCls||this.itemCls||'',
41071                f.width ? f.width + this.padWidth : 160 + this.padWidth
41072        ],true);
41073     }
41074 });
41075  
41076
41077 /**
41078  * @class Roo.form.FieldSet
41079  * @extends Roo.form.Layout
41080  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41081  * @constructor
41082  * @param {Object} config Configuration options
41083  */
41084 Roo.form.FieldSet = function(config){
41085     Roo.form.FieldSet.superclass.constructor.call(this, config);
41086 };
41087
41088 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41089     /**
41090      * @cfg {String} legend
41091      * The text to display as the legend for the FieldSet (defaults to '')
41092      */
41093     /**
41094      * @cfg {String/Object} autoCreate
41095      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41096      */
41097
41098     // private
41099     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41100
41101     // private
41102     onRender : function(ct, position){
41103         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41104         if(this.legend){
41105             this.setLegend(this.legend);
41106         }
41107     },
41108
41109     // private
41110     setLegend : function(text){
41111         if(this.rendered){
41112             this.el.child('legend').update(text);
41113         }
41114     }
41115 });/*
41116  * Based on:
41117  * Ext JS Library 1.1.1
41118  * Copyright(c) 2006-2007, Ext JS, LLC.
41119  *
41120  * Originally Released Under LGPL - original licence link has changed is not relivant.
41121  *
41122  * Fork - LGPL
41123  * <script type="text/javascript">
41124  */
41125 /**
41126  * @class Roo.form.VTypes
41127  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41128  * @singleton
41129  */
41130 Roo.form.VTypes = function(){
41131     // closure these in so they are only created once.
41132     var alpha = /^[a-zA-Z_]+$/;
41133     var alphanum = /^[a-zA-Z0-9_]+$/;
41134     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41135     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41136
41137     // All these messages and functions are configurable
41138     return {
41139         /**
41140          * The function used to validate email addresses
41141          * @param {String} value The email address
41142          */
41143         'email' : function(v){
41144             return email.test(v);
41145         },
41146         /**
41147          * The error text to display when the email validation function returns false
41148          * @type String
41149          */
41150         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41151         /**
41152          * The keystroke filter mask to be applied on email input
41153          * @type RegExp
41154          */
41155         'emailMask' : /[a-z0-9_\.\-@]/i,
41156
41157         /**
41158          * The function used to validate URLs
41159          * @param {String} value The URL
41160          */
41161         'url' : function(v){
41162             return url.test(v);
41163         },
41164         /**
41165          * The error text to display when the url validation function returns false
41166          * @type String
41167          */
41168         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41169         
41170         /**
41171          * The function used to validate alpha values
41172          * @param {String} value The value
41173          */
41174         'alpha' : function(v){
41175             return alpha.test(v);
41176         },
41177         /**
41178          * The error text to display when the alpha validation function returns false
41179          * @type String
41180          */
41181         'alphaText' : 'This field should only contain letters and _',
41182         /**
41183          * The keystroke filter mask to be applied on alpha input
41184          * @type RegExp
41185          */
41186         'alphaMask' : /[a-z_]/i,
41187
41188         /**
41189          * The function used to validate alphanumeric values
41190          * @param {String} value The value
41191          */
41192         'alphanum' : function(v){
41193             return alphanum.test(v);
41194         },
41195         /**
41196          * The error text to display when the alphanumeric validation function returns false
41197          * @type String
41198          */
41199         'alphanumText' : 'This field should only contain letters, numbers and _',
41200         /**
41201          * The keystroke filter mask to be applied on alphanumeric input
41202          * @type RegExp
41203          */
41204         'alphanumMask' : /[a-z0-9_]/i
41205     };
41206 }();//<script type="text/javascript">
41207
41208 /**
41209  * @class Roo.form.FCKeditor
41210  * @extends Roo.form.TextArea
41211  * Wrapper around the FCKEditor http://www.fckeditor.net
41212  * @constructor
41213  * Creates a new FCKeditor
41214  * @param {Object} config Configuration options
41215  */
41216 Roo.form.FCKeditor = function(config){
41217     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41218     this.addEvents({
41219          /**
41220          * @event editorinit
41221          * Fired when the editor is initialized - you can add extra handlers here..
41222          * @param {FCKeditor} this
41223          * @param {Object} the FCK object.
41224          */
41225         editorinit : true
41226     });
41227     
41228     
41229 };
41230 Roo.form.FCKeditor.editors = { };
41231 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41232 {
41233     //defaultAutoCreate : {
41234     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41235     //},
41236     // private
41237     /**
41238      * @cfg {Object} fck options - see fck manual for details.
41239      */
41240     fckconfig : false,
41241     
41242     /**
41243      * @cfg {Object} fck toolbar set (Basic or Default)
41244      */
41245     toolbarSet : 'Basic',
41246     /**
41247      * @cfg {Object} fck BasePath
41248      */ 
41249     basePath : '/fckeditor/',
41250     
41251     
41252     frame : false,
41253     
41254     value : '',
41255     
41256    
41257     onRender : function(ct, position)
41258     {
41259         if(!this.el){
41260             this.defaultAutoCreate = {
41261                 tag: "textarea",
41262                 style:"width:300px;height:60px;",
41263                 autocomplete: "off"
41264             };
41265         }
41266         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41267         /*
41268         if(this.grow){
41269             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41270             if(this.preventScrollbars){
41271                 this.el.setStyle("overflow", "hidden");
41272             }
41273             this.el.setHeight(this.growMin);
41274         }
41275         */
41276         //console.log('onrender' + this.getId() );
41277         Roo.form.FCKeditor.editors[this.getId()] = this;
41278          
41279
41280         this.replaceTextarea() ;
41281         
41282     },
41283     
41284     getEditor : function() {
41285         return this.fckEditor;
41286     },
41287     /**
41288      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41289      * @param {Mixed} value The value to set
41290      */
41291     
41292     
41293     setValue : function(value)
41294     {
41295         //console.log('setValue: ' + value);
41296         
41297         if(typeof(value) == 'undefined') { // not sure why this is happending...
41298             return;
41299         }
41300         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41301         
41302         //if(!this.el || !this.getEditor()) {
41303         //    this.value = value;
41304             //this.setValue.defer(100,this,[value]);    
41305         //    return;
41306         //} 
41307         
41308         if(!this.getEditor()) {
41309             return;
41310         }
41311         
41312         this.getEditor().SetData(value);
41313         
41314         //
41315
41316     },
41317
41318     /**
41319      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41320      * @return {Mixed} value The field value
41321      */
41322     getValue : function()
41323     {
41324         
41325         if (this.frame && this.frame.dom.style.display == 'none') {
41326             return Roo.form.FCKeditor.superclass.getValue.call(this);
41327         }
41328         
41329         if(!this.el || !this.getEditor()) {
41330            
41331            // this.getValue.defer(100,this); 
41332             return this.value;
41333         }
41334        
41335         
41336         var value=this.getEditor().GetData();
41337         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41338         return Roo.form.FCKeditor.superclass.getValue.call(this);
41339         
41340
41341     },
41342
41343     /**
41344      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41345      * @return {Mixed} value The field value
41346      */
41347     getRawValue : function()
41348     {
41349         if (this.frame && this.frame.dom.style.display == 'none') {
41350             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41351         }
41352         
41353         if(!this.el || !this.getEditor()) {
41354             //this.getRawValue.defer(100,this); 
41355             return this.value;
41356             return;
41357         }
41358         
41359         
41360         
41361         var value=this.getEditor().GetData();
41362         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41363         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41364          
41365     },
41366     
41367     setSize : function(w,h) {
41368         
41369         
41370         
41371         //if (this.frame && this.frame.dom.style.display == 'none') {
41372         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41373         //    return;
41374         //}
41375         //if(!this.el || !this.getEditor()) {
41376         //    this.setSize.defer(100,this, [w,h]); 
41377         //    return;
41378         //}
41379         
41380         
41381         
41382         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41383         
41384         this.frame.dom.setAttribute('width', w);
41385         this.frame.dom.setAttribute('height', h);
41386         this.frame.setSize(w,h);
41387         
41388     },
41389     
41390     toggleSourceEdit : function(value) {
41391         
41392       
41393          
41394         this.el.dom.style.display = value ? '' : 'none';
41395         this.frame.dom.style.display = value ?  'none' : '';
41396         
41397     },
41398     
41399     
41400     focus: function(tag)
41401     {
41402         if (this.frame.dom.style.display == 'none') {
41403             return Roo.form.FCKeditor.superclass.focus.call(this);
41404         }
41405         if(!this.el || !this.getEditor()) {
41406             this.focus.defer(100,this, [tag]); 
41407             return;
41408         }
41409         
41410         
41411         
41412         
41413         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41414         this.getEditor().Focus();
41415         if (tgs.length) {
41416             if (!this.getEditor().Selection.GetSelection()) {
41417                 this.focus.defer(100,this, [tag]); 
41418                 return;
41419             }
41420             
41421             
41422             var r = this.getEditor().EditorDocument.createRange();
41423             r.setStart(tgs[0],0);
41424             r.setEnd(tgs[0],0);
41425             this.getEditor().Selection.GetSelection().removeAllRanges();
41426             this.getEditor().Selection.GetSelection().addRange(r);
41427             this.getEditor().Focus();
41428         }
41429         
41430     },
41431     
41432     
41433     
41434     replaceTextarea : function()
41435     {
41436         if ( document.getElementById( this.getId() + '___Frame' ) )
41437             return ;
41438         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41439         //{
41440             // We must check the elements firstly using the Id and then the name.
41441         var oTextarea = document.getElementById( this.getId() );
41442         
41443         var colElementsByName = document.getElementsByName( this.getId() ) ;
41444          
41445         oTextarea.style.display = 'none' ;
41446
41447         if ( oTextarea.tabIndex ) {            
41448             this.TabIndex = oTextarea.tabIndex ;
41449         }
41450         
41451         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41452         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41453         this.frame = Roo.get(this.getId() + '___Frame')
41454     },
41455     
41456     _getConfigHtml : function()
41457     {
41458         var sConfig = '' ;
41459
41460         for ( var o in this.fckconfig ) {
41461             sConfig += sConfig.length > 0  ? '&amp;' : '';
41462             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41463         }
41464
41465         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41466     },
41467     
41468     
41469     _getIFrameHtml : function()
41470     {
41471         var sFile = 'fckeditor.html' ;
41472         /* no idea what this is about..
41473         try
41474         {
41475             if ( (/fcksource=true/i).test( window.top.location.search ) )
41476                 sFile = 'fckeditor.original.html' ;
41477         }
41478         catch (e) { 
41479         */
41480
41481         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41482         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41483         
41484         
41485         var html = '<iframe id="' + this.getId() +
41486             '___Frame" src="' + sLink +
41487             '" width="' + this.width +
41488             '" height="' + this.height + '"' +
41489             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41490             ' frameborder="0" scrolling="no"></iframe>' ;
41491
41492         return html ;
41493     },
41494     
41495     _insertHtmlBefore : function( html, element )
41496     {
41497         if ( element.insertAdjacentHTML )       {
41498             // IE
41499             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41500         } else { // Gecko
41501             var oRange = document.createRange() ;
41502             oRange.setStartBefore( element ) ;
41503             var oFragment = oRange.createContextualFragment( html );
41504             element.parentNode.insertBefore( oFragment, element ) ;
41505         }
41506     }
41507     
41508     
41509   
41510     
41511     
41512     
41513     
41514
41515 });
41516
41517 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41518
41519 function FCKeditor_OnComplete(editorInstance){
41520     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41521     f.fckEditor = editorInstance;
41522     //console.log("loaded");
41523     f.fireEvent('editorinit', f, editorInstance);
41524
41525   
41526
41527  
41528
41529
41530
41531
41532
41533
41534
41535
41536
41537
41538
41539
41540
41541
41542
41543 //<script type="text/javascript">
41544 /**
41545  * @class Roo.form.GridField
41546  * @extends Roo.form.Field
41547  * Embed a grid (or editable grid into a form)
41548  * STATUS ALPHA
41549  * @constructor
41550  * Creates a new GridField
41551  * @param {Object} config Configuration options
41552  */
41553 Roo.form.GridField = function(config){
41554     Roo.form.GridField.superclass.constructor.call(this, config);
41555      
41556 };
41557
41558 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41559     /**
41560      * @cfg {Number} width  - used to restrict width of grid..
41561      */
41562     width : 100,
41563     /**
41564      * @cfg {Number} height - used to restrict height of grid..
41565      */
41566     height : 50,
41567      /**
41568      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41569      */
41570     xgrid : false, 
41571     /**
41572      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41573      * {tag: "input", type: "checkbox", autocomplete: "off"})
41574      */
41575    // defaultAutoCreate : { tag: 'div' },
41576     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41577     /**
41578      * @cfg {String} addTitle Text to include for adding a title.
41579      */
41580     addTitle : false,
41581     //
41582     onResize : function(){
41583         Roo.form.Field.superclass.onResize.apply(this, arguments);
41584     },
41585
41586     initEvents : function(){
41587         // Roo.form.Checkbox.superclass.initEvents.call(this);
41588         // has no events...
41589        
41590     },
41591
41592
41593     getResizeEl : function(){
41594         return this.wrap;
41595     },
41596
41597     getPositionEl : function(){
41598         return this.wrap;
41599     },
41600
41601     // private
41602     onRender : function(ct, position){
41603         
41604         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41605         var style = this.style;
41606         delete this.style;
41607         
41608         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41609         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41610         this.viewEl = this.wrap.createChild({ tag: 'div' });
41611         if (style) {
41612             this.viewEl.applyStyles(style);
41613         }
41614         if (this.width) {
41615             this.viewEl.setWidth(this.width);
41616         }
41617         if (this.height) {
41618             this.viewEl.setHeight(this.height);
41619         }
41620         //if(this.inputValue !== undefined){
41621         //this.setValue(this.value);
41622         
41623         
41624         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41625         
41626         
41627         this.grid.render();
41628         this.grid.getDataSource().on('remove', this.refreshValue, this);
41629         this.grid.getDataSource().on('update', this.refreshValue, this);
41630         this.grid.on('afteredit', this.refreshValue, this);
41631  
41632     },
41633      
41634     
41635     /**
41636      * Sets the value of the item. 
41637      * @param {String} either an object  or a string..
41638      */
41639     setValue : function(v){
41640         //this.value = v;
41641         v = v || []; // empty set..
41642         // this does not seem smart - it really only affects memoryproxy grids..
41643         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41644             var ds = this.grid.getDataSource();
41645             // assumes a json reader..
41646             var data = {}
41647             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41648             ds.loadData( data);
41649         }
41650         Roo.form.GridField.superclass.setValue.call(this, v);
41651         this.refreshValue();
41652         // should load data in the grid really....
41653     },
41654     
41655     // private
41656     refreshValue: function() {
41657          var val = [];
41658         this.grid.getDataSource().each(function(r) {
41659             val.push(r.data);
41660         });
41661         this.el.dom.value = Roo.encode(val);
41662     }
41663     
41664      
41665     
41666     
41667 });//<script type="text/javasscript">
41668  
41669
41670 /**
41671  * @class Roo.DDView
41672  * A DnD enabled version of Roo.View.
41673  * @param {Element/String} container The Element in which to create the View.
41674  * @param {String} tpl The template string used to create the markup for each element of the View
41675  * @param {Object} config The configuration properties. These include all the config options of
41676  * {@link Roo.View} plus some specific to this class.<br>
41677  * <p>
41678  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41679  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41680  * <p>
41681  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41682 .x-view-drag-insert-above {
41683         border-top:1px dotted #3366cc;
41684 }
41685 .x-view-drag-insert-below {
41686         border-bottom:1px dotted #3366cc;
41687 }
41688 </code></pre>
41689  * 
41690  */
41691  
41692 Roo.DDView = function(container, tpl, config) {
41693     Roo.DDView.superclass.constructor.apply(this, arguments);
41694     this.getEl().setStyle("outline", "0px none");
41695     this.getEl().unselectable();
41696     if (this.dragGroup) {
41697                 this.setDraggable(this.dragGroup.split(","));
41698     }
41699     if (this.dropGroup) {
41700                 this.setDroppable(this.dropGroup.split(","));
41701     }
41702     if (this.deletable) {
41703         this.setDeletable();
41704     }
41705     this.isDirtyFlag = false;
41706         this.addEvents({
41707                 "drop" : true
41708         });
41709 };
41710
41711 Roo.extend(Roo.DDView, Roo.View, {
41712 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41713 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41714 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41715 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41716
41717         isFormField: true,
41718
41719         reset: Roo.emptyFn,
41720         
41721         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41722
41723         validate: function() {
41724                 return true;
41725         },
41726         
41727         destroy: function() {
41728                 this.purgeListeners();
41729                 this.getEl.removeAllListeners();
41730                 this.getEl().remove();
41731                 if (this.dragZone) {
41732                         if (this.dragZone.destroy) {
41733                                 this.dragZone.destroy();
41734                         }
41735                 }
41736                 if (this.dropZone) {
41737                         if (this.dropZone.destroy) {
41738                                 this.dropZone.destroy();
41739                         }
41740                 }
41741         },
41742
41743 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41744         getName: function() {
41745                 return this.name;
41746         },
41747
41748 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41749         setValue: function(v) {
41750                 if (!this.store) {
41751                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41752                 }
41753                 var data = {};
41754                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41755                 this.store.proxy = new Roo.data.MemoryProxy(data);
41756                 this.store.load();
41757         },
41758
41759 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41760         getValue: function() {
41761                 var result = '(';
41762                 this.store.each(function(rec) {
41763                         result += rec.id + ',';
41764                 });
41765                 return result.substr(0, result.length - 1) + ')';
41766         },
41767         
41768         getIds: function() {
41769                 var i = 0, result = new Array(this.store.getCount());
41770                 this.store.each(function(rec) {
41771                         result[i++] = rec.id;
41772                 });
41773                 return result;
41774         },
41775         
41776         isDirty: function() {
41777                 return this.isDirtyFlag;
41778         },
41779
41780 /**
41781  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41782  *      whole Element becomes the target, and this causes the drop gesture to append.
41783  */
41784     getTargetFromEvent : function(e) {
41785                 var target = e.getTarget();
41786                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41787                 target = target.parentNode;
41788                 }
41789                 if (!target) {
41790                         target = this.el.dom.lastChild || this.el.dom;
41791                 }
41792                 return target;
41793     },
41794
41795 /**
41796  *      Create the drag data which consists of an object which has the property "ddel" as
41797  *      the drag proxy element. 
41798  */
41799     getDragData : function(e) {
41800         var target = this.findItemFromChild(e.getTarget());
41801                 if(target) {
41802                         this.handleSelection(e);
41803                         var selNodes = this.getSelectedNodes();
41804             var dragData = {
41805                 source: this,
41806                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41807                 nodes: selNodes,
41808                 records: []
41809                         };
41810                         var selectedIndices = this.getSelectedIndexes();
41811                         for (var i = 0; i < selectedIndices.length; i++) {
41812                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41813                         }
41814                         if (selNodes.length == 1) {
41815                                 dragData.ddel = target.cloneNode(true); // the div element
41816                         } else {
41817                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41818                                 div.className = 'multi-proxy';
41819                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41820                                         div.appendChild(selNodes[i].cloneNode(true));
41821                                 }
41822                                 dragData.ddel = div;
41823                         }
41824             //console.log(dragData)
41825             //console.log(dragData.ddel.innerHTML)
41826                         return dragData;
41827                 }
41828         //console.log('nodragData')
41829                 return false;
41830     },
41831     
41832 /**     Specify to which ddGroup items in this DDView may be dragged. */
41833     setDraggable: function(ddGroup) {
41834         if (ddGroup instanceof Array) {
41835                 Roo.each(ddGroup, this.setDraggable, this);
41836                 return;
41837         }
41838         if (this.dragZone) {
41839                 this.dragZone.addToGroup(ddGroup);
41840         } else {
41841                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41842                                 containerScroll: true,
41843                                 ddGroup: ddGroup 
41844
41845                         });
41846 //                      Draggability implies selection. DragZone's mousedown selects the element.
41847                         if (!this.multiSelect) { this.singleSelect = true; }
41848
41849 //                      Wire the DragZone's handlers up to methods in *this*
41850                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41851                 }
41852     },
41853
41854 /**     Specify from which ddGroup this DDView accepts drops. */
41855     setDroppable: function(ddGroup) {
41856         if (ddGroup instanceof Array) {
41857                 Roo.each(ddGroup, this.setDroppable, this);
41858                 return;
41859         }
41860         if (this.dropZone) {
41861                 this.dropZone.addToGroup(ddGroup);
41862         } else {
41863                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41864                                 containerScroll: true,
41865                                 ddGroup: ddGroup
41866                         });
41867
41868 //                      Wire the DropZone's handlers up to methods in *this*
41869                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41870                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41871                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41872                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41873                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41874                 }
41875     },
41876
41877 /**     Decide whether to drop above or below a View node. */
41878     getDropPoint : function(e, n, dd){
41879         if (n == this.el.dom) { return "above"; }
41880                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
41881                 var c = t + (b - t) / 2;
41882                 var y = Roo.lib.Event.getPageY(e);
41883                 if(y <= c) {
41884                         return "above";
41885                 }else{
41886                         return "below";
41887                 }
41888     },
41889
41890     onNodeEnter : function(n, dd, e, data){
41891                 return false;
41892     },
41893     
41894     onNodeOver : function(n, dd, e, data){
41895                 var pt = this.getDropPoint(e, n, dd);
41896                 // set the insert point style on the target node
41897                 var dragElClass = this.dropNotAllowed;
41898                 if (pt) {
41899                         var targetElClass;
41900                         if (pt == "above"){
41901                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
41902                                 targetElClass = "x-view-drag-insert-above";
41903                         } else {
41904                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
41905                                 targetElClass = "x-view-drag-insert-below";
41906                         }
41907                         if (this.lastInsertClass != targetElClass){
41908                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
41909                                 this.lastInsertClass = targetElClass;
41910                         }
41911                 }
41912                 return dragElClass;
41913         },
41914
41915     onNodeOut : function(n, dd, e, data){
41916                 this.removeDropIndicators(n);
41917     },
41918
41919     onNodeDrop : function(n, dd, e, data){
41920         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
41921                 return false;
41922         }
41923         var pt = this.getDropPoint(e, n, dd);
41924                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
41925                 if (pt == "below") { insertAt++; }
41926                 for (var i = 0; i < data.records.length; i++) {
41927                         var r = data.records[i];
41928                         var dup = this.store.getById(r.id);
41929                         if (dup && (dd != this.dragZone)) {
41930                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
41931                         } else {
41932                                 if (data.copy) {
41933                                         this.store.insert(insertAt++, r.copy());
41934                                 } else {
41935                                         data.source.isDirtyFlag = true;
41936                                         r.store.remove(r);
41937                                         this.store.insert(insertAt++, r);
41938                                 }
41939                                 this.isDirtyFlag = true;
41940                         }
41941                 }
41942                 this.dragZone.cachedTarget = null;
41943                 return true;
41944     },
41945
41946     removeDropIndicators : function(n){
41947                 if(n){
41948                         Roo.fly(n).removeClass([
41949                                 "x-view-drag-insert-above",
41950                                 "x-view-drag-insert-below"]);
41951                         this.lastInsertClass = "_noclass";
41952                 }
41953     },
41954
41955 /**
41956  *      Utility method. Add a delete option to the DDView's context menu.
41957  *      @param {String} imageUrl The URL of the "delete" icon image.
41958  */
41959         setDeletable: function(imageUrl) {
41960                 if (!this.singleSelect && !this.multiSelect) {
41961                         this.singleSelect = true;
41962                 }
41963                 var c = this.getContextMenu();
41964                 this.contextMenu.on("itemclick", function(item) {
41965                         switch (item.id) {
41966                                 case "delete":
41967                                         this.remove(this.getSelectedIndexes());
41968                                         break;
41969                         }
41970                 }, this);
41971                 this.contextMenu.add({
41972                         icon: imageUrl,
41973                         id: "delete",
41974                         text: 'Delete'
41975                 });
41976         },
41977         
41978 /**     Return the context menu for this DDView. */
41979         getContextMenu: function() {
41980                 if (!this.contextMenu) {
41981 //                      Create the View's context menu
41982                         this.contextMenu = new Roo.menu.Menu({
41983                                 id: this.id + "-contextmenu"
41984                         });
41985                         this.el.on("contextmenu", this.showContextMenu, this);
41986                 }
41987                 return this.contextMenu;
41988         },
41989         
41990         disableContextMenu: function() {
41991                 if (this.contextMenu) {
41992                         this.el.un("contextmenu", this.showContextMenu, this);
41993                 }
41994         },
41995
41996         showContextMenu: function(e, item) {
41997         item = this.findItemFromChild(e.getTarget());
41998                 if (item) {
41999                         e.stopEvent();
42000                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42001                         this.contextMenu.showAt(e.getXY());
42002             }
42003     },
42004
42005 /**
42006  *      Remove {@link Roo.data.Record}s at the specified indices.
42007  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42008  */
42009     remove: function(selectedIndices) {
42010                 selectedIndices = [].concat(selectedIndices);
42011                 for (var i = 0; i < selectedIndices.length; i++) {
42012                         var rec = this.store.getAt(selectedIndices[i]);
42013                         this.store.remove(rec);
42014                 }
42015     },
42016
42017 /**
42018  *      Double click fires the event, but also, if this is draggable, and there is only one other
42019  *      related DropZone, it transfers the selected node.
42020  */
42021     onDblClick : function(e){
42022         var item = this.findItemFromChild(e.getTarget());
42023         if(item){
42024             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42025                 return false;
42026             }
42027             if (this.dragGroup) {
42028                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42029                     while (targets.indexOf(this.dropZone) > -1) {
42030                             targets.remove(this.dropZone);
42031                                 }
42032                     if (targets.length == 1) {
42033                                         this.dragZone.cachedTarget = null;
42034                         var el = Roo.get(targets[0].getEl());
42035                         var box = el.getBox(true);
42036                         targets[0].onNodeDrop(el.dom, {
42037                                 target: el.dom,
42038                                 xy: [box.x, box.y + box.height - 1]
42039                         }, null, this.getDragData(e));
42040                     }
42041                 }
42042         }
42043     },
42044     
42045     handleSelection: function(e) {
42046                 this.dragZone.cachedTarget = null;
42047         var item = this.findItemFromChild(e.getTarget());
42048         if (!item) {
42049                 this.clearSelections(true);
42050                 return;
42051         }
42052                 if (item && (this.multiSelect || this.singleSelect)){
42053                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42054                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42055                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42056                                 this.unselect(item);
42057                         } else {
42058                                 this.select(item, this.multiSelect && e.ctrlKey);
42059                                 this.lastSelection = item;
42060                         }
42061                 }
42062     },
42063
42064     onItemClick : function(item, index, e){
42065                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42066                         return false;
42067                 }
42068                 return true;
42069     },
42070
42071     unselect : function(nodeInfo, suppressEvent){
42072                 var node = this.getNode(nodeInfo);
42073                 if(node && this.isSelected(node)){
42074                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42075                                 Roo.fly(node).removeClass(this.selectedClass);
42076                                 this.selections.remove(node);
42077                                 if(!suppressEvent){
42078                                         this.fireEvent("selectionchange", this, this.selections);
42079                                 }
42080                         }
42081                 }
42082     }
42083 });
42084 /*
42085  * Based on:
42086  * Ext JS Library 1.1.1
42087  * Copyright(c) 2006-2007, Ext JS, LLC.
42088  *
42089  * Originally Released Under LGPL - original licence link has changed is not relivant.
42090  *
42091  * Fork - LGPL
42092  * <script type="text/javascript">
42093  */
42094  
42095 /**
42096  * @class Roo.LayoutManager
42097  * @extends Roo.util.Observable
42098  * Base class for layout managers.
42099  */
42100 Roo.LayoutManager = function(container, config){
42101     Roo.LayoutManager.superclass.constructor.call(this);
42102     this.el = Roo.get(container);
42103     // ie scrollbar fix
42104     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42105         document.body.scroll = "no";
42106     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42107         this.el.position('relative');
42108     }
42109     this.id = this.el.id;
42110     this.el.addClass("x-layout-container");
42111     /** false to disable window resize monitoring @type Boolean */
42112     this.monitorWindowResize = true;
42113     this.regions = {};
42114     this.addEvents({
42115         /**
42116          * @event layout
42117          * Fires when a layout is performed. 
42118          * @param {Roo.LayoutManager} this
42119          */
42120         "layout" : true,
42121         /**
42122          * @event regionresized
42123          * Fires when the user resizes a region. 
42124          * @param {Roo.LayoutRegion} region The resized region
42125          * @param {Number} newSize The new size (width for east/west, height for north/south)
42126          */
42127         "regionresized" : true,
42128         /**
42129          * @event regioncollapsed
42130          * Fires when a region is collapsed. 
42131          * @param {Roo.LayoutRegion} region The collapsed region
42132          */
42133         "regioncollapsed" : true,
42134         /**
42135          * @event regionexpanded
42136          * Fires when a region is expanded.  
42137          * @param {Roo.LayoutRegion} region The expanded region
42138          */
42139         "regionexpanded" : true
42140     });
42141     this.updating = false;
42142     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42143 };
42144
42145 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42146     /**
42147      * Returns true if this layout is currently being updated
42148      * @return {Boolean}
42149      */
42150     isUpdating : function(){
42151         return this.updating; 
42152     },
42153     
42154     /**
42155      * Suspend the LayoutManager from doing auto-layouts while
42156      * making multiple add or remove calls
42157      */
42158     beginUpdate : function(){
42159         this.updating = true;    
42160     },
42161     
42162     /**
42163      * Restore auto-layouts and optionally disable the manager from performing a layout
42164      * @param {Boolean} noLayout true to disable a layout update 
42165      */
42166     endUpdate : function(noLayout){
42167         this.updating = false;
42168         if(!noLayout){
42169             this.layout();
42170         }    
42171     },
42172     
42173     layout: function(){
42174         
42175     },
42176     
42177     onRegionResized : function(region, newSize){
42178         this.fireEvent("regionresized", region, newSize);
42179         this.layout();
42180     },
42181     
42182     onRegionCollapsed : function(region){
42183         this.fireEvent("regioncollapsed", region);
42184     },
42185     
42186     onRegionExpanded : function(region){
42187         this.fireEvent("regionexpanded", region);
42188     },
42189         
42190     /**
42191      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42192      * performs box-model adjustments.
42193      * @return {Object} The size as an object {width: (the width), height: (the height)}
42194      */
42195     getViewSize : function(){
42196         var size;
42197         if(this.el.dom != document.body){
42198             size = this.el.getSize();
42199         }else{
42200             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42201         }
42202         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42203         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42204         return size;
42205     },
42206     
42207     /**
42208      * Returns the Element this layout is bound to.
42209      * @return {Roo.Element}
42210      */
42211     getEl : function(){
42212         return this.el;
42213     },
42214     
42215     /**
42216      * Returns the specified region.
42217      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42218      * @return {Roo.LayoutRegion}
42219      */
42220     getRegion : function(target){
42221         return this.regions[target.toLowerCase()];
42222     },
42223     
42224     onWindowResize : function(){
42225         if(this.monitorWindowResize){
42226             this.layout();
42227         }
42228     }
42229 });/*
42230  * Based on:
42231  * Ext JS Library 1.1.1
42232  * Copyright(c) 2006-2007, Ext JS, LLC.
42233  *
42234  * Originally Released Under LGPL - original licence link has changed is not relivant.
42235  *
42236  * Fork - LGPL
42237  * <script type="text/javascript">
42238  */
42239 /**
42240  * @class Roo.BorderLayout
42241  * @extends Roo.LayoutManager
42242  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42243  * please see: <br><br>
42244  * <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>
42245  * <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>
42246  * Example:
42247  <pre><code>
42248  var layout = new Roo.BorderLayout(document.body, {
42249     north: {
42250         initialSize: 25,
42251         titlebar: false
42252     },
42253     west: {
42254         split:true,
42255         initialSize: 200,
42256         minSize: 175,
42257         maxSize: 400,
42258         titlebar: true,
42259         collapsible: true
42260     },
42261     east: {
42262         split:true,
42263         initialSize: 202,
42264         minSize: 175,
42265         maxSize: 400,
42266         titlebar: true,
42267         collapsible: true
42268     },
42269     south: {
42270         split:true,
42271         initialSize: 100,
42272         minSize: 100,
42273         maxSize: 200,
42274         titlebar: true,
42275         collapsible: true
42276     },
42277     center: {
42278         titlebar: true,
42279         autoScroll:true,
42280         resizeTabs: true,
42281         minTabWidth: 50,
42282         preferredTabWidth: 150
42283     }
42284 });
42285
42286 // shorthand
42287 var CP = Roo.ContentPanel;
42288
42289 layout.beginUpdate();
42290 layout.add("north", new CP("north", "North"));
42291 layout.add("south", new CP("south", {title: "South", closable: true}));
42292 layout.add("west", new CP("west", {title: "West"}));
42293 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42294 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42295 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42296 layout.getRegion("center").showPanel("center1");
42297 layout.endUpdate();
42298 </code></pre>
42299
42300 <b>The container the layout is rendered into can be either the body element or any other element.
42301 If it is not the body element, the container needs to either be an absolute positioned element,
42302 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42303 the container size if it is not the body element.</b>
42304
42305 * @constructor
42306 * Create a new BorderLayout
42307 * @param {String/HTMLElement/Element} container The container this layout is bound to
42308 * @param {Object} config Configuration options
42309  */
42310 Roo.BorderLayout = function(container, config){
42311     config = config || {};
42312     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42313     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42314     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42315         var target = this.factory.validRegions[i];
42316         if(config[target]){
42317             this.addRegion(target, config[target]);
42318         }
42319     }
42320 };
42321
42322 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42323     /**
42324      * Creates and adds a new region if it doesn't already exist.
42325      * @param {String} target The target region key (north, south, east, west or center).
42326      * @param {Object} config The regions config object
42327      * @return {BorderLayoutRegion} The new region
42328      */
42329     addRegion : function(target, config){
42330         if(!this.regions[target]){
42331             var r = this.factory.create(target, this, config);
42332             this.bindRegion(target, r);
42333         }
42334         return this.regions[target];
42335     },
42336
42337     // private (kinda)
42338     bindRegion : function(name, r){
42339         this.regions[name] = r;
42340         r.on("visibilitychange", this.layout, this);
42341         r.on("paneladded", this.layout, this);
42342         r.on("panelremoved", this.layout, this);
42343         r.on("invalidated", this.layout, this);
42344         r.on("resized", this.onRegionResized, this);
42345         r.on("collapsed", this.onRegionCollapsed, this);
42346         r.on("expanded", this.onRegionExpanded, this);
42347     },
42348
42349     /**
42350      * Performs a layout update.
42351      */
42352     layout : function(){
42353         if(this.updating) return;
42354         var size = this.getViewSize();
42355         var w = size.width;
42356         var h = size.height;
42357         var centerW = w;
42358         var centerH = h;
42359         var centerY = 0;
42360         var centerX = 0;
42361         //var x = 0, y = 0;
42362
42363         var rs = this.regions;
42364         var north = rs["north"];
42365         var south = rs["south"]; 
42366         var west = rs["west"];
42367         var east = rs["east"];
42368         var center = rs["center"];
42369         //if(this.hideOnLayout){ // not supported anymore
42370             //c.el.setStyle("display", "none");
42371         //}
42372         if(north && north.isVisible()){
42373             var b = north.getBox();
42374             var m = north.getMargins();
42375             b.width = w - (m.left+m.right);
42376             b.x = m.left;
42377             b.y = m.top;
42378             centerY = b.height + b.y + m.bottom;
42379             centerH -= centerY;
42380             north.updateBox(this.safeBox(b));
42381         }
42382         if(south && south.isVisible()){
42383             var b = south.getBox();
42384             var m = south.getMargins();
42385             b.width = w - (m.left+m.right);
42386             b.x = m.left;
42387             var totalHeight = (b.height + m.top + m.bottom);
42388             b.y = h - totalHeight + m.top;
42389             centerH -= totalHeight;
42390             south.updateBox(this.safeBox(b));
42391         }
42392         if(west && west.isVisible()){
42393             var b = west.getBox();
42394             var m = west.getMargins();
42395             b.height = centerH - (m.top+m.bottom);
42396             b.x = m.left;
42397             b.y = centerY + m.top;
42398             var totalWidth = (b.width + m.left + m.right);
42399             centerX += totalWidth;
42400             centerW -= totalWidth;
42401             west.updateBox(this.safeBox(b));
42402         }
42403         if(east && east.isVisible()){
42404             var b = east.getBox();
42405             var m = east.getMargins();
42406             b.height = centerH - (m.top+m.bottom);
42407             var totalWidth = (b.width + m.left + m.right);
42408             b.x = w - totalWidth + m.left;
42409             b.y = centerY + m.top;
42410             centerW -= totalWidth;
42411             east.updateBox(this.safeBox(b));
42412         }
42413         if(center){
42414             var m = center.getMargins();
42415             var centerBox = {
42416                 x: centerX + m.left,
42417                 y: centerY + m.top,
42418                 width: centerW - (m.left+m.right),
42419                 height: centerH - (m.top+m.bottom)
42420             };
42421             //if(this.hideOnLayout){
42422                 //center.el.setStyle("display", "block");
42423             //}
42424             center.updateBox(this.safeBox(centerBox));
42425         }
42426         this.el.repaint();
42427         this.fireEvent("layout", this);
42428     },
42429
42430     // private
42431     safeBox : function(box){
42432         box.width = Math.max(0, box.width);
42433         box.height = Math.max(0, box.height);
42434         return box;
42435     },
42436
42437     /**
42438      * Adds a ContentPanel (or subclass) to this layout.
42439      * @param {String} target The target region key (north, south, east, west or center).
42440      * @param {Roo.ContentPanel} panel The panel to add
42441      * @return {Roo.ContentPanel} The added panel
42442      */
42443     add : function(target, panel){
42444          
42445         target = target.toLowerCase();
42446         return this.regions[target].add(panel);
42447     },
42448
42449     /**
42450      * Remove a ContentPanel (or subclass) to this layout.
42451      * @param {String} target The target region key (north, south, east, west or center).
42452      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42453      * @return {Roo.ContentPanel} The removed panel
42454      */
42455     remove : function(target, panel){
42456         target = target.toLowerCase();
42457         return this.regions[target].remove(panel);
42458     },
42459
42460     /**
42461      * Searches all regions for a panel with the specified id
42462      * @param {String} panelId
42463      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42464      */
42465     findPanel : function(panelId){
42466         var rs = this.regions;
42467         for(var target in rs){
42468             if(typeof rs[target] != "function"){
42469                 var p = rs[target].getPanel(panelId);
42470                 if(p){
42471                     return p;
42472                 }
42473             }
42474         }
42475         return null;
42476     },
42477
42478     /**
42479      * Searches all regions for a panel with the specified id and activates (shows) it.
42480      * @param {String/ContentPanel} panelId The panels id or the panel itself
42481      * @return {Roo.ContentPanel} The shown panel or null
42482      */
42483     showPanel : function(panelId) {
42484       var rs = this.regions;
42485       for(var target in rs){
42486          var r = rs[target];
42487          if(typeof r != "function"){
42488             if(r.hasPanel(panelId)){
42489                return r.showPanel(panelId);
42490             }
42491          }
42492       }
42493       return null;
42494    },
42495
42496    /**
42497      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42498      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42499      */
42500     restoreState : function(provider){
42501         if(!provider){
42502             provider = Roo.state.Manager;
42503         }
42504         var sm = new Roo.LayoutStateManager();
42505         sm.init(this, provider);
42506     },
42507
42508     /**
42509      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42510      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42511      * a valid ContentPanel config object.  Example:
42512      * <pre><code>
42513 // Create the main layout
42514 var layout = new Roo.BorderLayout('main-ct', {
42515     west: {
42516         split:true,
42517         minSize: 175,
42518         titlebar: true
42519     },
42520     center: {
42521         title:'Components'
42522     }
42523 }, 'main-ct');
42524
42525 // Create and add multiple ContentPanels at once via configs
42526 layout.batchAdd({
42527    west: {
42528        id: 'source-files',
42529        autoCreate:true,
42530        title:'Ext Source Files',
42531        autoScroll:true,
42532        fitToFrame:true
42533    },
42534    center : {
42535        el: cview,
42536        autoScroll:true,
42537        fitToFrame:true,
42538        toolbar: tb,
42539        resizeEl:'cbody'
42540    }
42541 });
42542 </code></pre>
42543      * @param {Object} regions An object containing ContentPanel configs by region name
42544      */
42545     batchAdd : function(regions){
42546         this.beginUpdate();
42547         for(var rname in regions){
42548             var lr = this.regions[rname];
42549             if(lr){
42550                 this.addTypedPanels(lr, regions[rname]);
42551             }
42552         }
42553         this.endUpdate();
42554     },
42555
42556     // private
42557     addTypedPanels : function(lr, ps){
42558         if(typeof ps == 'string'){
42559             lr.add(new Roo.ContentPanel(ps));
42560         }
42561         else if(ps instanceof Array){
42562             for(var i =0, len = ps.length; i < len; i++){
42563                 this.addTypedPanels(lr, ps[i]);
42564             }
42565         }
42566         else if(!ps.events){ // raw config?
42567             var el = ps.el;
42568             delete ps.el; // prevent conflict
42569             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42570         }
42571         else {  // panel object assumed!
42572             lr.add(ps);
42573         }
42574     },
42575     /**
42576      * Adds a xtype elements to the layout.
42577      * <pre><code>
42578
42579 layout.addxtype({
42580        xtype : 'ContentPanel',
42581        region: 'west',
42582        items: [ .... ]
42583    }
42584 );
42585
42586 layout.addxtype({
42587         xtype : 'NestedLayoutPanel',
42588         region: 'west',
42589         layout: {
42590            center: { },
42591            west: { }   
42592         },
42593         items : [ ... list of content panels or nested layout panels.. ]
42594    }
42595 );
42596 </code></pre>
42597      * @param {Object} cfg Xtype definition of item to add.
42598      */
42599     addxtype : function(cfg)
42600     {
42601         // basically accepts a pannel...
42602         // can accept a layout region..!?!?
42603        // console.log('BorderLayout add ' + cfg.xtype)
42604         
42605         if (!cfg.xtype.match(/Panel$/)) {
42606             return false;
42607         }
42608         var ret = false;
42609         var region = cfg.region;
42610         delete cfg.region;
42611         
42612           
42613         var xitems = [];
42614         if (cfg.items) {
42615             xitems = cfg.items;
42616             delete cfg.items;
42617         }
42618         
42619         
42620         switch(cfg.xtype) 
42621         {
42622             case 'ContentPanel':  // ContentPanel (el, cfg)
42623                 if(cfg.autoCreate) {
42624                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42625                 } else {
42626                     var el = this.el.createChild();
42627                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42628                 }
42629                 
42630                 this.add(region, ret);
42631                 break;
42632             
42633             
42634             case 'TreePanel': // our new panel!
42635                 cfg.el = this.el.createChild();
42636                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42637                 this.add(region, ret);
42638                 break;
42639             
42640             case 'NestedLayoutPanel': 
42641                 // create a new Layout (which is  a Border Layout...
42642                 var el = this.el.createChild();
42643                 var clayout = cfg.layout;
42644                 delete cfg.layout;
42645                 clayout.items   = clayout.items  || [];
42646                 // replace this exitems with the clayout ones..
42647                 xitems = clayout.items;
42648                  
42649                 
42650                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42651                     cfg.background = false;
42652                 }
42653                 var layout = new Roo.BorderLayout(el, clayout);
42654                 
42655                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42656                 //console.log('adding nested layout panel '  + cfg.toSource());
42657                 this.add(region, ret);
42658                 
42659                 break;
42660                 
42661             case 'GridPanel': 
42662             
42663                 // needs grid and region
42664                 
42665                 //var el = this.getRegion(region).el.createChild();
42666                 var el = this.el.createChild();
42667                 // create the grid first...
42668                 
42669                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42670                 delete cfg.grid;
42671                 if (region == 'center' && this.active ) {
42672                     cfg.background = false;
42673                 }
42674                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42675                 
42676                 this.add(region, ret);
42677                 if (cfg.background) {
42678                     ret.on('activate', function(gp) {
42679                         if (!gp.grid.rendered) {
42680                             gp.grid.render();
42681                         }
42682                     });
42683                 } else {
42684                     grid.render();
42685                 }
42686                 break;
42687            
42688                
42689                 
42690                 
42691             default: 
42692                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42693                 return;
42694              // GridPanel (grid, cfg)
42695             
42696         }
42697         this.beginUpdate();
42698         // add children..
42699         Roo.each(xitems, function(i)  {
42700             ret.addxtype(i);
42701         });
42702         this.endUpdate();
42703         return ret;
42704         
42705     }
42706 });
42707
42708 /**
42709  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42710  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42711  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42712  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42713  * <pre><code>
42714 // shorthand
42715 var CP = Roo.ContentPanel;
42716
42717 var layout = Roo.BorderLayout.create({
42718     north: {
42719         initialSize: 25,
42720         titlebar: false,
42721         panels: [new CP("north", "North")]
42722     },
42723     west: {
42724         split:true,
42725         initialSize: 200,
42726         minSize: 175,
42727         maxSize: 400,
42728         titlebar: true,
42729         collapsible: true,
42730         panels: [new CP("west", {title: "West"})]
42731     },
42732     east: {
42733         split:true,
42734         initialSize: 202,
42735         minSize: 175,
42736         maxSize: 400,
42737         titlebar: true,
42738         collapsible: true,
42739         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42740     },
42741     south: {
42742         split:true,
42743         initialSize: 100,
42744         minSize: 100,
42745         maxSize: 200,
42746         titlebar: true,
42747         collapsible: true,
42748         panels: [new CP("south", {title: "South", closable: true})]
42749     },
42750     center: {
42751         titlebar: true,
42752         autoScroll:true,
42753         resizeTabs: true,
42754         minTabWidth: 50,
42755         preferredTabWidth: 150,
42756         panels: [
42757             new CP("center1", {title: "Close Me", closable: true}),
42758             new CP("center2", {title: "Center Panel", closable: false})
42759         ]
42760     }
42761 }, document.body);
42762
42763 layout.getRegion("center").showPanel("center1");
42764 </code></pre>
42765  * @param config
42766  * @param targetEl
42767  */
42768 Roo.BorderLayout.create = function(config, targetEl){
42769     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42770     layout.beginUpdate();
42771     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42772     for(var j = 0, jlen = regions.length; j < jlen; j++){
42773         var lr = regions[j];
42774         if(layout.regions[lr] && config[lr].panels){
42775             var r = layout.regions[lr];
42776             var ps = config[lr].panels;
42777             layout.addTypedPanels(r, ps);
42778         }
42779     }
42780     layout.endUpdate();
42781     return layout;
42782 };
42783
42784 // private
42785 Roo.BorderLayout.RegionFactory = {
42786     // private
42787     validRegions : ["north","south","east","west","center"],
42788
42789     // private
42790     create : function(target, mgr, config){
42791         target = target.toLowerCase();
42792         if(config.lightweight || config.basic){
42793             return new Roo.BasicLayoutRegion(mgr, config, target);
42794         }
42795         switch(target){
42796             case "north":
42797                 return new Roo.NorthLayoutRegion(mgr, config);
42798             case "south":
42799                 return new Roo.SouthLayoutRegion(mgr, config);
42800             case "east":
42801                 return new Roo.EastLayoutRegion(mgr, config);
42802             case "west":
42803                 return new Roo.WestLayoutRegion(mgr, config);
42804             case "center":
42805                 return new Roo.CenterLayoutRegion(mgr, config);
42806         }
42807         throw 'Layout region "'+target+'" not supported.';
42808     }
42809 };/*
42810  * Based on:
42811  * Ext JS Library 1.1.1
42812  * Copyright(c) 2006-2007, Ext JS, LLC.
42813  *
42814  * Originally Released Under LGPL - original licence link has changed is not relivant.
42815  *
42816  * Fork - LGPL
42817  * <script type="text/javascript">
42818  */
42819  
42820 /**
42821  * @class Roo.BasicLayoutRegion
42822  * @extends Roo.util.Observable
42823  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42824  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42825  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42826  */
42827 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42828     this.mgr = mgr;
42829     this.position  = pos;
42830     this.events = {
42831         /**
42832          * @scope Roo.BasicLayoutRegion
42833          */
42834         
42835         /**
42836          * @event beforeremove
42837          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42838          * @param {Roo.LayoutRegion} this
42839          * @param {Roo.ContentPanel} panel The panel
42840          * @param {Object} e The cancel event object
42841          */
42842         "beforeremove" : true,
42843         /**
42844          * @event invalidated
42845          * Fires when the layout for this region is changed.
42846          * @param {Roo.LayoutRegion} this
42847          */
42848         "invalidated" : true,
42849         /**
42850          * @event visibilitychange
42851          * Fires when this region is shown or hidden 
42852          * @param {Roo.LayoutRegion} this
42853          * @param {Boolean} visibility true or false
42854          */
42855         "visibilitychange" : true,
42856         /**
42857          * @event paneladded
42858          * Fires when a panel is added. 
42859          * @param {Roo.LayoutRegion} this
42860          * @param {Roo.ContentPanel} panel The panel
42861          */
42862         "paneladded" : true,
42863         /**
42864          * @event panelremoved
42865          * Fires when a panel is removed. 
42866          * @param {Roo.LayoutRegion} this
42867          * @param {Roo.ContentPanel} panel The panel
42868          */
42869         "panelremoved" : true,
42870         /**
42871          * @event collapsed
42872          * Fires when this region is collapsed.
42873          * @param {Roo.LayoutRegion} this
42874          */
42875         "collapsed" : true,
42876         /**
42877          * @event expanded
42878          * Fires when this region is expanded.
42879          * @param {Roo.LayoutRegion} this
42880          */
42881         "expanded" : true,
42882         /**
42883          * @event slideshow
42884          * Fires when this region is slid into view.
42885          * @param {Roo.LayoutRegion} this
42886          */
42887         "slideshow" : true,
42888         /**
42889          * @event slidehide
42890          * Fires when this region slides out of view. 
42891          * @param {Roo.LayoutRegion} this
42892          */
42893         "slidehide" : true,
42894         /**
42895          * @event panelactivated
42896          * Fires when a panel is activated. 
42897          * @param {Roo.LayoutRegion} this
42898          * @param {Roo.ContentPanel} panel The activated panel
42899          */
42900         "panelactivated" : true,
42901         /**
42902          * @event resized
42903          * Fires when the user resizes this region. 
42904          * @param {Roo.LayoutRegion} this
42905          * @param {Number} newSize The new size (width for east/west, height for north/south)
42906          */
42907         "resized" : true
42908     };
42909     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42910     this.panels = new Roo.util.MixedCollection();
42911     this.panels.getKey = this.getPanelId.createDelegate(this);
42912     this.box = null;
42913     this.activePanel = null;
42914     // ensure listeners are added...
42915     
42916     if (config.listeners || config.events) {
42917         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
42918             listeners : config.listeners || {},
42919             events : config.events || {}
42920         });
42921     }
42922     
42923     if(skipConfig !== true){
42924         this.applyConfig(config);
42925     }
42926 };
42927
42928 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
42929     getPanelId : function(p){
42930         return p.getId();
42931     },
42932     
42933     applyConfig : function(config){
42934         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42935         this.config = config;
42936         
42937     },
42938     
42939     /**
42940      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42941      * the width, for horizontal (north, south) the height.
42942      * @param {Number} newSize The new width or height
42943      */
42944     resizeTo : function(newSize){
42945         var el = this.el ? this.el :
42946                  (this.activePanel ? this.activePanel.getEl() : null);
42947         if(el){
42948             switch(this.position){
42949                 case "east":
42950                 case "west":
42951                     el.setWidth(newSize);
42952                     this.fireEvent("resized", this, newSize);
42953                 break;
42954                 case "north":
42955                 case "south":
42956                     el.setHeight(newSize);
42957                     this.fireEvent("resized", this, newSize);
42958                 break;                
42959             }
42960         }
42961     },
42962     
42963     getBox : function(){
42964         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42965     },
42966     
42967     getMargins : function(){
42968         return this.margins;
42969     },
42970     
42971     updateBox : function(box){
42972         this.box = box;
42973         var el = this.activePanel.getEl();
42974         el.dom.style.left = box.x + "px";
42975         el.dom.style.top = box.y + "px";
42976         this.activePanel.setSize(box.width, box.height);
42977     },
42978     
42979     /**
42980      * Returns the container element for this region.
42981      * @return {Roo.Element}
42982      */
42983     getEl : function(){
42984         return this.activePanel;
42985     },
42986     
42987     /**
42988      * Returns true if this region is currently visible.
42989      * @return {Boolean}
42990      */
42991     isVisible : function(){
42992         return this.activePanel ? true : false;
42993     },
42994     
42995     setActivePanel : function(panel){
42996         panel = this.getPanel(panel);
42997         if(this.activePanel && this.activePanel != panel){
42998             this.activePanel.setActiveState(false);
42999             this.activePanel.getEl().setLeftTop(-10000,-10000);
43000         }
43001         this.activePanel = panel;
43002         panel.setActiveState(true);
43003         if(this.box){
43004             panel.setSize(this.box.width, this.box.height);
43005         }
43006         this.fireEvent("panelactivated", this, panel);
43007         this.fireEvent("invalidated");
43008     },
43009     
43010     /**
43011      * Show the specified panel.
43012      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43013      * @return {Roo.ContentPanel} The shown panel or null
43014      */
43015     showPanel : function(panel){
43016         if(panel = this.getPanel(panel)){
43017             this.setActivePanel(panel);
43018         }
43019         return panel;
43020     },
43021     
43022     /**
43023      * Get the active panel for this region.
43024      * @return {Roo.ContentPanel} The active panel or null
43025      */
43026     getActivePanel : function(){
43027         return this.activePanel;
43028     },
43029     
43030     /**
43031      * Add the passed ContentPanel(s)
43032      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43033      * @return {Roo.ContentPanel} The panel added (if only one was added)
43034      */
43035     add : function(panel){
43036         if(arguments.length > 1){
43037             for(var i = 0, len = arguments.length; i < len; i++) {
43038                 this.add(arguments[i]);
43039             }
43040             return null;
43041         }
43042         if(this.hasPanel(panel)){
43043             this.showPanel(panel);
43044             return panel;
43045         }
43046         var el = panel.getEl();
43047         if(el.dom.parentNode != this.mgr.el.dom){
43048             this.mgr.el.dom.appendChild(el.dom);
43049         }
43050         if(panel.setRegion){
43051             panel.setRegion(this);
43052         }
43053         this.panels.add(panel);
43054         el.setStyle("position", "absolute");
43055         if(!panel.background){
43056             this.setActivePanel(panel);
43057             if(this.config.initialSize && this.panels.getCount()==1){
43058                 this.resizeTo(this.config.initialSize);
43059             }
43060         }
43061         this.fireEvent("paneladded", this, panel);
43062         return panel;
43063     },
43064     
43065     /**
43066      * Returns true if the panel is in this region.
43067      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43068      * @return {Boolean}
43069      */
43070     hasPanel : function(panel){
43071         if(typeof panel == "object"){ // must be panel obj
43072             panel = panel.getId();
43073         }
43074         return this.getPanel(panel) ? true : false;
43075     },
43076     
43077     /**
43078      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43079      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43080      * @param {Boolean} preservePanel Overrides the config preservePanel option
43081      * @return {Roo.ContentPanel} The panel that was removed
43082      */
43083     remove : function(panel, preservePanel){
43084         panel = this.getPanel(panel);
43085         if(!panel){
43086             return null;
43087         }
43088         var e = {};
43089         this.fireEvent("beforeremove", this, panel, e);
43090         if(e.cancel === true){
43091             return null;
43092         }
43093         var panelId = panel.getId();
43094         this.panels.removeKey(panelId);
43095         return panel;
43096     },
43097     
43098     /**
43099      * Returns the panel specified or null if it's not in this region.
43100      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43101      * @return {Roo.ContentPanel}
43102      */
43103     getPanel : function(id){
43104         if(typeof id == "object"){ // must be panel obj
43105             return id;
43106         }
43107         return this.panels.get(id);
43108     },
43109     
43110     /**
43111      * Returns this regions position (north/south/east/west/center).
43112      * @return {String} 
43113      */
43114     getPosition: function(){
43115         return this.position;    
43116     }
43117 });/*
43118  * Based on:
43119  * Ext JS Library 1.1.1
43120  * Copyright(c) 2006-2007, Ext JS, LLC.
43121  *
43122  * Originally Released Under LGPL - original licence link has changed is not relivant.
43123  *
43124  * Fork - LGPL
43125  * <script type="text/javascript">
43126  */
43127  
43128 /**
43129  * @class Roo.LayoutRegion
43130  * @extends Roo.BasicLayoutRegion
43131  * This class represents a region in a layout manager.
43132  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43133  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43134  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43135  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43136  * @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})
43137  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43138  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43139  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43140  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43141  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43142  * @cfg {String} title The title for the region (overrides panel titles)
43143  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43144  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43145  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43146  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43147  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43148  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43149  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43150  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43151  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43152  * @cfg {Boolean} showPin True to show a pin button
43153 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43154 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43155 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43156 * @cfg {Number} width  For East/West panels
43157 * @cfg {Number} height For North/South panels
43158 * @cfg {Boolean} split To show the splitter
43159  */
43160 Roo.LayoutRegion = function(mgr, config, pos){
43161     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43162     var dh = Roo.DomHelper;
43163     /** This region's container element 
43164     * @type Roo.Element */
43165     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43166     /** This region's title element 
43167     * @type Roo.Element */
43168
43169     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43170         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43171         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43172     ]}, true);
43173     this.titleEl.enableDisplayMode();
43174     /** This region's title text element 
43175     * @type HTMLElement */
43176     this.titleTextEl = this.titleEl.dom.firstChild;
43177     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43178     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43179     this.closeBtn.enableDisplayMode();
43180     this.closeBtn.on("click", this.closeClicked, this);
43181     this.closeBtn.hide();
43182
43183     this.createBody(config);
43184     this.visible = true;
43185     this.collapsed = false;
43186
43187     if(config.hideWhenEmpty){
43188         this.hide();
43189         this.on("paneladded", this.validateVisibility, this);
43190         this.on("panelremoved", this.validateVisibility, this);
43191     }
43192     this.applyConfig(config);
43193 };
43194
43195 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43196
43197     createBody : function(){
43198         /** This region's body element 
43199         * @type Roo.Element */
43200         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43201     },
43202
43203     applyConfig : function(c){
43204         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43205             var dh = Roo.DomHelper;
43206             if(c.titlebar !== false){
43207                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43208                 this.collapseBtn.on("click", this.collapse, this);
43209                 this.collapseBtn.enableDisplayMode();
43210
43211                 if(c.showPin === true || this.showPin){
43212                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43213                     this.stickBtn.enableDisplayMode();
43214                     this.stickBtn.on("click", this.expand, this);
43215                     this.stickBtn.hide();
43216                 }
43217             }
43218             /** This region's collapsed element
43219             * @type Roo.Element */
43220             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43221                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43222             ]}, true);
43223             if(c.floatable !== false){
43224                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43225                this.collapsedEl.on("click", this.collapseClick, this);
43226             }
43227
43228             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43229                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43230                    id: "message", unselectable: "on", style:{"float":"left"}});
43231                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43232              }
43233             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43234             this.expandBtn.on("click", this.expand, this);
43235         }
43236         if(this.collapseBtn){
43237             this.collapseBtn.setVisible(c.collapsible == true);
43238         }
43239         this.cmargins = c.cmargins || this.cmargins ||
43240                          (this.position == "west" || this.position == "east" ?
43241                              {top: 0, left: 2, right:2, bottom: 0} :
43242                              {top: 2, left: 0, right:0, bottom: 2});
43243         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43244         this.bottomTabs = c.tabPosition != "top";
43245         this.autoScroll = c.autoScroll || false;
43246         if(this.autoScroll){
43247             this.bodyEl.setStyle("overflow", "auto");
43248         }else{
43249             this.bodyEl.setStyle("overflow", "hidden");
43250         }
43251         //if(c.titlebar !== false){
43252             if((!c.titlebar && !c.title) || c.titlebar === false){
43253                 this.titleEl.hide();
43254             }else{
43255                 this.titleEl.show();
43256                 if(c.title){
43257                     this.titleTextEl.innerHTML = c.title;
43258                 }
43259             }
43260         //}
43261         this.duration = c.duration || .30;
43262         this.slideDuration = c.slideDuration || .45;
43263         this.config = c;
43264         if(c.collapsed){
43265             this.collapse(true);
43266         }
43267         if(c.hidden){
43268             this.hide();
43269         }
43270     },
43271     /**
43272      * Returns true if this region is currently visible.
43273      * @return {Boolean}
43274      */
43275     isVisible : function(){
43276         return this.visible;
43277     },
43278
43279     /**
43280      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43281      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43282      */
43283     setCollapsedTitle : function(title){
43284         title = title || "&#160;";
43285         if(this.collapsedTitleTextEl){
43286             this.collapsedTitleTextEl.innerHTML = title;
43287         }
43288     },
43289
43290     getBox : function(){
43291         var b;
43292         if(!this.collapsed){
43293             b = this.el.getBox(false, true);
43294         }else{
43295             b = this.collapsedEl.getBox(false, true);
43296         }
43297         return b;
43298     },
43299
43300     getMargins : function(){
43301         return this.collapsed ? this.cmargins : this.margins;
43302     },
43303
43304     highlight : function(){
43305         this.el.addClass("x-layout-panel-dragover");
43306     },
43307
43308     unhighlight : function(){
43309         this.el.removeClass("x-layout-panel-dragover");
43310     },
43311
43312     updateBox : function(box){
43313         this.box = box;
43314         if(!this.collapsed){
43315             this.el.dom.style.left = box.x + "px";
43316             this.el.dom.style.top = box.y + "px";
43317             this.updateBody(box.width, box.height);
43318         }else{
43319             this.collapsedEl.dom.style.left = box.x + "px";
43320             this.collapsedEl.dom.style.top = box.y + "px";
43321             this.collapsedEl.setSize(box.width, box.height);
43322         }
43323         if(this.tabs){
43324             this.tabs.autoSizeTabs();
43325         }
43326     },
43327
43328     updateBody : function(w, h){
43329         if(w !== null){
43330             this.el.setWidth(w);
43331             w -= this.el.getBorderWidth("rl");
43332             if(this.config.adjustments){
43333                 w += this.config.adjustments[0];
43334             }
43335         }
43336         if(h !== null){
43337             this.el.setHeight(h);
43338             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43339             h -= this.el.getBorderWidth("tb");
43340             if(this.config.adjustments){
43341                 h += this.config.adjustments[1];
43342             }
43343             this.bodyEl.setHeight(h);
43344             if(this.tabs){
43345                 h = this.tabs.syncHeight(h);
43346             }
43347         }
43348         if(this.panelSize){
43349             w = w !== null ? w : this.panelSize.width;
43350             h = h !== null ? h : this.panelSize.height;
43351         }
43352         if(this.activePanel){
43353             var el = this.activePanel.getEl();
43354             w = w !== null ? w : el.getWidth();
43355             h = h !== null ? h : el.getHeight();
43356             this.panelSize = {width: w, height: h};
43357             this.activePanel.setSize(w, h);
43358         }
43359         if(Roo.isIE && this.tabs){
43360             this.tabs.el.repaint();
43361         }
43362     },
43363
43364     /**
43365      * Returns the container element for this region.
43366      * @return {Roo.Element}
43367      */
43368     getEl : function(){
43369         return this.el;
43370     },
43371
43372     /**
43373      * Hides this region.
43374      */
43375     hide : function(){
43376         if(!this.collapsed){
43377             this.el.dom.style.left = "-2000px";
43378             this.el.hide();
43379         }else{
43380             this.collapsedEl.dom.style.left = "-2000px";
43381             this.collapsedEl.hide();
43382         }
43383         this.visible = false;
43384         this.fireEvent("visibilitychange", this, false);
43385     },
43386
43387     /**
43388      * Shows this region if it was previously hidden.
43389      */
43390     show : function(){
43391         if(!this.collapsed){
43392             this.el.show();
43393         }else{
43394             this.collapsedEl.show();
43395         }
43396         this.visible = true;
43397         this.fireEvent("visibilitychange", this, true);
43398     },
43399
43400     closeClicked : function(){
43401         if(this.activePanel){
43402             this.remove(this.activePanel);
43403         }
43404     },
43405
43406     collapseClick : function(e){
43407         if(this.isSlid){
43408            e.stopPropagation();
43409            this.slideIn();
43410         }else{
43411            e.stopPropagation();
43412            this.slideOut();
43413         }
43414     },
43415
43416     /**
43417      * Collapses this region.
43418      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43419      */
43420     collapse : function(skipAnim){
43421         if(this.collapsed) return;
43422         this.collapsed = true;
43423         if(this.split){
43424             this.split.el.hide();
43425         }
43426         if(this.config.animate && skipAnim !== true){
43427             this.fireEvent("invalidated", this);
43428             this.animateCollapse();
43429         }else{
43430             this.el.setLocation(-20000,-20000);
43431             this.el.hide();
43432             this.collapsedEl.show();
43433             this.fireEvent("collapsed", this);
43434             this.fireEvent("invalidated", this);
43435         }
43436     },
43437
43438     animateCollapse : function(){
43439         // overridden
43440     },
43441
43442     /**
43443      * Expands this region if it was previously collapsed.
43444      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43445      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43446      */
43447     expand : function(e, skipAnim){
43448         if(e) e.stopPropagation();
43449         if(!this.collapsed || this.el.hasActiveFx()) return;
43450         if(this.isSlid){
43451             this.afterSlideIn();
43452             skipAnim = true;
43453         }
43454         this.collapsed = false;
43455         if(this.config.animate && skipAnim !== true){
43456             this.animateExpand();
43457         }else{
43458             this.el.show();
43459             if(this.split){
43460                 this.split.el.show();
43461             }
43462             this.collapsedEl.setLocation(-2000,-2000);
43463             this.collapsedEl.hide();
43464             this.fireEvent("invalidated", this);
43465             this.fireEvent("expanded", this);
43466         }
43467     },
43468
43469     animateExpand : function(){
43470         // overridden
43471     },
43472
43473     initTabs : function(){
43474         this.bodyEl.setStyle("overflow", "hidden");
43475         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43476             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43477             disableTooltips: this.config.disableTabTips
43478         });
43479         if(this.config.hideTabs){
43480             ts.stripWrap.setDisplayed(false);
43481         }
43482         this.tabs = ts;
43483         ts.resizeTabs = this.config.resizeTabs === true;
43484         ts.minTabWidth = this.config.minTabWidth || 40;
43485         ts.maxTabWidth = this.config.maxTabWidth || 250;
43486         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43487         ts.monitorResize = false;
43488         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43489         ts.bodyEl.addClass('x-layout-tabs-body');
43490         this.panels.each(this.initPanelAsTab, this);
43491     },
43492
43493     initPanelAsTab : function(panel){
43494         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43495                     this.config.closeOnTab && panel.isClosable());
43496         if(panel.tabTip !== undefined){
43497             ti.setTooltip(panel.tabTip);
43498         }
43499         ti.on("activate", function(){
43500               this.setActivePanel(panel);
43501         }, this);
43502         if(this.config.closeOnTab){
43503             ti.on("beforeclose", function(t, e){
43504                 e.cancel = true;
43505                 this.remove(panel);
43506             }, this);
43507         }
43508         return ti;
43509     },
43510
43511     updatePanelTitle : function(panel, title){
43512         if(this.activePanel == panel){
43513             this.updateTitle(title);
43514         }
43515         if(this.tabs){
43516             var ti = this.tabs.getTab(panel.getEl().id);
43517             ti.setText(title);
43518             if(panel.tabTip !== undefined){
43519                 ti.setTooltip(panel.tabTip);
43520             }
43521         }
43522     },
43523
43524     updateTitle : function(title){
43525         if(this.titleTextEl && !this.config.title){
43526             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43527         }
43528     },
43529
43530     setActivePanel : function(panel){
43531         panel = this.getPanel(panel);
43532         if(this.activePanel && this.activePanel != panel){
43533             this.activePanel.setActiveState(false);
43534         }
43535         this.activePanel = panel;
43536         panel.setActiveState(true);
43537         if(this.panelSize){
43538             panel.setSize(this.panelSize.width, this.panelSize.height);
43539         }
43540         if(this.closeBtn){
43541             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43542         }
43543         this.updateTitle(panel.getTitle());
43544         if(this.tabs){
43545             this.fireEvent("invalidated", this);
43546         }
43547         this.fireEvent("panelactivated", this, panel);
43548     },
43549
43550     /**
43551      * Shows the specified panel.
43552      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43553      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43554      */
43555     showPanel : function(panel){
43556         if(panel = this.getPanel(panel)){
43557             if(this.tabs){
43558                 var tab = this.tabs.getTab(panel.getEl().id);
43559                 if(tab.isHidden()){
43560                     this.tabs.unhideTab(tab.id);
43561                 }
43562                 tab.activate();
43563             }else{
43564                 this.setActivePanel(panel);
43565             }
43566         }
43567         return panel;
43568     },
43569
43570     /**
43571      * Get the active panel for this region.
43572      * @return {Roo.ContentPanel} The active panel or null
43573      */
43574     getActivePanel : function(){
43575         return this.activePanel;
43576     },
43577
43578     validateVisibility : function(){
43579         if(this.panels.getCount() < 1){
43580             this.updateTitle("&#160;");
43581             this.closeBtn.hide();
43582             this.hide();
43583         }else{
43584             if(!this.isVisible()){
43585                 this.show();
43586             }
43587         }
43588     },
43589
43590     /**
43591      * Adds the passed ContentPanel(s) to this region.
43592      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43593      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43594      */
43595     add : function(panel){
43596         if(arguments.length > 1){
43597             for(var i = 0, len = arguments.length; i < len; i++) {
43598                 this.add(arguments[i]);
43599             }
43600             return null;
43601         }
43602         if(this.hasPanel(panel)){
43603             this.showPanel(panel);
43604             return panel;
43605         }
43606         panel.setRegion(this);
43607         this.panels.add(panel);
43608         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43609             this.bodyEl.dom.appendChild(panel.getEl().dom);
43610             if(panel.background !== true){
43611                 this.setActivePanel(panel);
43612             }
43613             this.fireEvent("paneladded", this, panel);
43614             return panel;
43615         }
43616         if(!this.tabs){
43617             this.initTabs();
43618         }else{
43619             this.initPanelAsTab(panel);
43620         }
43621         if(panel.background !== true){
43622             this.tabs.activate(panel.getEl().id);
43623         }
43624         this.fireEvent("paneladded", this, panel);
43625         return panel;
43626     },
43627
43628     /**
43629      * Hides the tab for the specified panel.
43630      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43631      */
43632     hidePanel : function(panel){
43633         if(this.tabs && (panel = this.getPanel(panel))){
43634             this.tabs.hideTab(panel.getEl().id);
43635         }
43636     },
43637
43638     /**
43639      * Unhides the tab for a previously hidden panel.
43640      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43641      */
43642     unhidePanel : function(panel){
43643         if(this.tabs && (panel = this.getPanel(panel))){
43644             this.tabs.unhideTab(panel.getEl().id);
43645         }
43646     },
43647
43648     clearPanels : function(){
43649         while(this.panels.getCount() > 0){
43650              this.remove(this.panels.first());
43651         }
43652     },
43653
43654     /**
43655      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43656      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43657      * @param {Boolean} preservePanel Overrides the config preservePanel option
43658      * @return {Roo.ContentPanel} The panel that was removed
43659      */
43660     remove : function(panel, preservePanel){
43661         panel = this.getPanel(panel);
43662         if(!panel){
43663             return null;
43664         }
43665         var e = {};
43666         this.fireEvent("beforeremove", this, panel, e);
43667         if(e.cancel === true){
43668             return null;
43669         }
43670         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43671         var panelId = panel.getId();
43672         this.panels.removeKey(panelId);
43673         if(preservePanel){
43674             document.body.appendChild(panel.getEl().dom);
43675         }
43676         if(this.tabs){
43677             this.tabs.removeTab(panel.getEl().id);
43678         }else if (!preservePanel){
43679             this.bodyEl.dom.removeChild(panel.getEl().dom);
43680         }
43681         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43682             var p = this.panels.first();
43683             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43684             tempEl.appendChild(p.getEl().dom);
43685             this.bodyEl.update("");
43686             this.bodyEl.dom.appendChild(p.getEl().dom);
43687             tempEl = null;
43688             this.updateTitle(p.getTitle());
43689             this.tabs = null;
43690             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43691             this.setActivePanel(p);
43692         }
43693         panel.setRegion(null);
43694         if(this.activePanel == panel){
43695             this.activePanel = null;
43696         }
43697         if(this.config.autoDestroy !== false && preservePanel !== true){
43698             try{panel.destroy();}catch(e){}
43699         }
43700         this.fireEvent("panelremoved", this, panel);
43701         return panel;
43702     },
43703
43704     /**
43705      * Returns the TabPanel component used by this region
43706      * @return {Roo.TabPanel}
43707      */
43708     getTabs : function(){
43709         return this.tabs;
43710     },
43711
43712     createTool : function(parentEl, className){
43713         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43714             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43715         btn.addClassOnOver("x-layout-tools-button-over");
43716         return btn;
43717     }
43718 });/*
43719  * Based on:
43720  * Ext JS Library 1.1.1
43721  * Copyright(c) 2006-2007, Ext JS, LLC.
43722  *
43723  * Originally Released Under LGPL - original licence link has changed is not relivant.
43724  *
43725  * Fork - LGPL
43726  * <script type="text/javascript">
43727  */
43728  
43729
43730
43731 /**
43732  * @class Roo.SplitLayoutRegion
43733  * @extends Roo.LayoutRegion
43734  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43735  */
43736 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43737     this.cursor = cursor;
43738     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43739 };
43740
43741 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43742     splitTip : "Drag to resize.",
43743     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43744     useSplitTips : false,
43745
43746     applyConfig : function(config){
43747         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43748         if(config.split){
43749             if(!this.split){
43750                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43751                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43752                 /** The SplitBar for this region 
43753                 * @type Roo.SplitBar */
43754                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43755                 this.split.on("moved", this.onSplitMove, this);
43756                 this.split.useShim = config.useShim === true;
43757                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43758                 if(this.useSplitTips){
43759                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43760                 }
43761                 if(config.collapsible){
43762                     this.split.el.on("dblclick", this.collapse,  this);
43763                 }
43764             }
43765             if(typeof config.minSize != "undefined"){
43766                 this.split.minSize = config.minSize;
43767             }
43768             if(typeof config.maxSize != "undefined"){
43769                 this.split.maxSize = config.maxSize;
43770             }
43771             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43772                 this.hideSplitter();
43773             }
43774         }
43775     },
43776
43777     getHMaxSize : function(){
43778          var cmax = this.config.maxSize || 10000;
43779          var center = this.mgr.getRegion("center");
43780          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43781     },
43782
43783     getVMaxSize : function(){
43784          var cmax = this.config.maxSize || 10000;
43785          var center = this.mgr.getRegion("center");
43786          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43787     },
43788
43789     onSplitMove : function(split, newSize){
43790         this.fireEvent("resized", this, newSize);
43791     },
43792     
43793     /** 
43794      * Returns the {@link Roo.SplitBar} for this region.
43795      * @return {Roo.SplitBar}
43796      */
43797     getSplitBar : function(){
43798         return this.split;
43799     },
43800     
43801     hide : function(){
43802         this.hideSplitter();
43803         Roo.SplitLayoutRegion.superclass.hide.call(this);
43804     },
43805
43806     hideSplitter : function(){
43807         if(this.split){
43808             this.split.el.setLocation(-2000,-2000);
43809             this.split.el.hide();
43810         }
43811     },
43812
43813     show : function(){
43814         if(this.split){
43815             this.split.el.show();
43816         }
43817         Roo.SplitLayoutRegion.superclass.show.call(this);
43818     },
43819     
43820     beforeSlide: function(){
43821         if(Roo.isGecko){// firefox overflow auto bug workaround
43822             this.bodyEl.clip();
43823             if(this.tabs) this.tabs.bodyEl.clip();
43824             if(this.activePanel){
43825                 this.activePanel.getEl().clip();
43826                 
43827                 if(this.activePanel.beforeSlide){
43828                     this.activePanel.beforeSlide();
43829                 }
43830             }
43831         }
43832     },
43833     
43834     afterSlide : function(){
43835         if(Roo.isGecko){// firefox overflow auto bug workaround
43836             this.bodyEl.unclip();
43837             if(this.tabs) this.tabs.bodyEl.unclip();
43838             if(this.activePanel){
43839                 this.activePanel.getEl().unclip();
43840                 if(this.activePanel.afterSlide){
43841                     this.activePanel.afterSlide();
43842                 }
43843             }
43844         }
43845     },
43846
43847     initAutoHide : function(){
43848         if(this.autoHide !== false){
43849             if(!this.autoHideHd){
43850                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43851                 this.autoHideHd = {
43852                     "mouseout": function(e){
43853                         if(!e.within(this.el, true)){
43854                             st.delay(500);
43855                         }
43856                     },
43857                     "mouseover" : function(e){
43858                         st.cancel();
43859                     },
43860                     scope : this
43861                 };
43862             }
43863             this.el.on(this.autoHideHd);
43864         }
43865     },
43866
43867     clearAutoHide : function(){
43868         if(this.autoHide !== false){
43869             this.el.un("mouseout", this.autoHideHd.mouseout);
43870             this.el.un("mouseover", this.autoHideHd.mouseover);
43871         }
43872     },
43873
43874     clearMonitor : function(){
43875         Roo.get(document).un("click", this.slideInIf, this);
43876     },
43877
43878     // these names are backwards but not changed for compat
43879     slideOut : function(){
43880         if(this.isSlid || this.el.hasActiveFx()){
43881             return;
43882         }
43883         this.isSlid = true;
43884         if(this.collapseBtn){
43885             this.collapseBtn.hide();
43886         }
43887         this.closeBtnState = this.closeBtn.getStyle('display');
43888         this.closeBtn.hide();
43889         if(this.stickBtn){
43890             this.stickBtn.show();
43891         }
43892         this.el.show();
43893         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43894         this.beforeSlide();
43895         this.el.setStyle("z-index", 10001);
43896         this.el.slideIn(this.getSlideAnchor(), {
43897             callback: function(){
43898                 this.afterSlide();
43899                 this.initAutoHide();
43900                 Roo.get(document).on("click", this.slideInIf, this);
43901                 this.fireEvent("slideshow", this);
43902             },
43903             scope: this,
43904             block: true
43905         });
43906     },
43907
43908     afterSlideIn : function(){
43909         this.clearAutoHide();
43910         this.isSlid = false;
43911         this.clearMonitor();
43912         this.el.setStyle("z-index", "");
43913         if(this.collapseBtn){
43914             this.collapseBtn.show();
43915         }
43916         this.closeBtn.setStyle('display', this.closeBtnState);
43917         if(this.stickBtn){
43918             this.stickBtn.hide();
43919         }
43920         this.fireEvent("slidehide", this);
43921     },
43922
43923     slideIn : function(cb){
43924         if(!this.isSlid || this.el.hasActiveFx()){
43925             Roo.callback(cb);
43926             return;
43927         }
43928         this.isSlid = false;
43929         this.beforeSlide();
43930         this.el.slideOut(this.getSlideAnchor(), {
43931             callback: function(){
43932                 this.el.setLeftTop(-10000, -10000);
43933                 this.afterSlide();
43934                 this.afterSlideIn();
43935                 Roo.callback(cb);
43936             },
43937             scope: this,
43938             block: true
43939         });
43940     },
43941     
43942     slideInIf : function(e){
43943         if(!e.within(this.el)){
43944             this.slideIn();
43945         }
43946     },
43947
43948     animateCollapse : function(){
43949         this.beforeSlide();
43950         this.el.setStyle("z-index", 20000);
43951         var anchor = this.getSlideAnchor();
43952         this.el.slideOut(anchor, {
43953             callback : function(){
43954                 this.el.setStyle("z-index", "");
43955                 this.collapsedEl.slideIn(anchor, {duration:.3});
43956                 this.afterSlide();
43957                 this.el.setLocation(-10000,-10000);
43958                 this.el.hide();
43959                 this.fireEvent("collapsed", this);
43960             },
43961             scope: this,
43962             block: true
43963         });
43964     },
43965
43966     animateExpand : function(){
43967         this.beforeSlide();
43968         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
43969         this.el.setStyle("z-index", 20000);
43970         this.collapsedEl.hide({
43971             duration:.1
43972         });
43973         this.el.slideIn(this.getSlideAnchor(), {
43974             callback : function(){
43975                 this.el.setStyle("z-index", "");
43976                 this.afterSlide();
43977                 if(this.split){
43978                     this.split.el.show();
43979                 }
43980                 this.fireEvent("invalidated", this);
43981                 this.fireEvent("expanded", this);
43982             },
43983             scope: this,
43984             block: true
43985         });
43986     },
43987
43988     anchors : {
43989         "west" : "left",
43990         "east" : "right",
43991         "north" : "top",
43992         "south" : "bottom"
43993     },
43994
43995     sanchors : {
43996         "west" : "l",
43997         "east" : "r",
43998         "north" : "t",
43999         "south" : "b"
44000     },
44001
44002     canchors : {
44003         "west" : "tl-tr",
44004         "east" : "tr-tl",
44005         "north" : "tl-bl",
44006         "south" : "bl-tl"
44007     },
44008
44009     getAnchor : function(){
44010         return this.anchors[this.position];
44011     },
44012
44013     getCollapseAnchor : function(){
44014         return this.canchors[this.position];
44015     },
44016
44017     getSlideAnchor : function(){
44018         return this.sanchors[this.position];
44019     },
44020
44021     getAlignAdj : function(){
44022         var cm = this.cmargins;
44023         switch(this.position){
44024             case "west":
44025                 return [0, 0];
44026             break;
44027             case "east":
44028                 return [0, 0];
44029             break;
44030             case "north":
44031                 return [0, 0];
44032             break;
44033             case "south":
44034                 return [0, 0];
44035             break;
44036         }
44037     },
44038
44039     getExpandAdj : function(){
44040         var c = this.collapsedEl, cm = this.cmargins;
44041         switch(this.position){
44042             case "west":
44043                 return [-(cm.right+c.getWidth()+cm.left), 0];
44044             break;
44045             case "east":
44046                 return [cm.right+c.getWidth()+cm.left, 0];
44047             break;
44048             case "north":
44049                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44050             break;
44051             case "south":
44052                 return [0, cm.top+cm.bottom+c.getHeight()];
44053             break;
44054         }
44055     }
44056 });/*
44057  * Based on:
44058  * Ext JS Library 1.1.1
44059  * Copyright(c) 2006-2007, Ext JS, LLC.
44060  *
44061  * Originally Released Under LGPL - original licence link has changed is not relivant.
44062  *
44063  * Fork - LGPL
44064  * <script type="text/javascript">
44065  */
44066 /*
44067  * These classes are private internal classes
44068  */
44069 Roo.CenterLayoutRegion = function(mgr, config){
44070     Roo.LayoutRegion.call(this, mgr, config, "center");
44071     this.visible = true;
44072     this.minWidth = config.minWidth || 20;
44073     this.minHeight = config.minHeight || 20;
44074 };
44075
44076 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44077     hide : function(){
44078         // center panel can't be hidden
44079     },
44080     
44081     show : function(){
44082         // center panel can't be hidden
44083     },
44084     
44085     getMinWidth: function(){
44086         return this.minWidth;
44087     },
44088     
44089     getMinHeight: function(){
44090         return this.minHeight;
44091     }
44092 });
44093
44094
44095 Roo.NorthLayoutRegion = function(mgr, config){
44096     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44097     if(this.split){
44098         this.split.placement = Roo.SplitBar.TOP;
44099         this.split.orientation = Roo.SplitBar.VERTICAL;
44100         this.split.el.addClass("x-layout-split-v");
44101     }
44102     var size = config.initialSize || config.height;
44103     if(typeof size != "undefined"){
44104         this.el.setHeight(size);
44105     }
44106 };
44107 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44108     orientation: Roo.SplitBar.VERTICAL,
44109     getBox : function(){
44110         if(this.collapsed){
44111             return this.collapsedEl.getBox();
44112         }
44113         var box = this.el.getBox();
44114         if(this.split){
44115             box.height += this.split.el.getHeight();
44116         }
44117         return box;
44118     },
44119     
44120     updateBox : function(box){
44121         if(this.split && !this.collapsed){
44122             box.height -= this.split.el.getHeight();
44123             this.split.el.setLeft(box.x);
44124             this.split.el.setTop(box.y+box.height);
44125             this.split.el.setWidth(box.width);
44126         }
44127         if(this.collapsed){
44128             this.updateBody(box.width, null);
44129         }
44130         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44131     }
44132 });
44133
44134 Roo.SouthLayoutRegion = function(mgr, config){
44135     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44136     if(this.split){
44137         this.split.placement = Roo.SplitBar.BOTTOM;
44138         this.split.orientation = Roo.SplitBar.VERTICAL;
44139         this.split.el.addClass("x-layout-split-v");
44140     }
44141     var size = config.initialSize || config.height;
44142     if(typeof size != "undefined"){
44143         this.el.setHeight(size);
44144     }
44145 };
44146 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44147     orientation: Roo.SplitBar.VERTICAL,
44148     getBox : function(){
44149         if(this.collapsed){
44150             return this.collapsedEl.getBox();
44151         }
44152         var box = this.el.getBox();
44153         if(this.split){
44154             var sh = this.split.el.getHeight();
44155             box.height += sh;
44156             box.y -= sh;
44157         }
44158         return box;
44159     },
44160     
44161     updateBox : function(box){
44162         if(this.split && !this.collapsed){
44163             var sh = this.split.el.getHeight();
44164             box.height -= sh;
44165             box.y += sh;
44166             this.split.el.setLeft(box.x);
44167             this.split.el.setTop(box.y-sh);
44168             this.split.el.setWidth(box.width);
44169         }
44170         if(this.collapsed){
44171             this.updateBody(box.width, null);
44172         }
44173         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44174     }
44175 });
44176
44177 Roo.EastLayoutRegion = function(mgr, config){
44178     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44179     if(this.split){
44180         this.split.placement = Roo.SplitBar.RIGHT;
44181         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44182         this.split.el.addClass("x-layout-split-h");
44183     }
44184     var size = config.initialSize || config.width;
44185     if(typeof size != "undefined"){
44186         this.el.setWidth(size);
44187     }
44188 };
44189 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44190     orientation: Roo.SplitBar.HORIZONTAL,
44191     getBox : function(){
44192         if(this.collapsed){
44193             return this.collapsedEl.getBox();
44194         }
44195         var box = this.el.getBox();
44196         if(this.split){
44197             var sw = this.split.el.getWidth();
44198             box.width += sw;
44199             box.x -= sw;
44200         }
44201         return box;
44202     },
44203
44204     updateBox : function(box){
44205         if(this.split && !this.collapsed){
44206             var sw = this.split.el.getWidth();
44207             box.width -= sw;
44208             this.split.el.setLeft(box.x);
44209             this.split.el.setTop(box.y);
44210             this.split.el.setHeight(box.height);
44211             box.x += sw;
44212         }
44213         if(this.collapsed){
44214             this.updateBody(null, box.height);
44215         }
44216         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44217     }
44218 });
44219
44220 Roo.WestLayoutRegion = function(mgr, config){
44221     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44222     if(this.split){
44223         this.split.placement = Roo.SplitBar.LEFT;
44224         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44225         this.split.el.addClass("x-layout-split-h");
44226     }
44227     var size = config.initialSize || config.width;
44228     if(typeof size != "undefined"){
44229         this.el.setWidth(size);
44230     }
44231 };
44232 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44233     orientation: Roo.SplitBar.HORIZONTAL,
44234     getBox : function(){
44235         if(this.collapsed){
44236             return this.collapsedEl.getBox();
44237         }
44238         var box = this.el.getBox();
44239         if(this.split){
44240             box.width += this.split.el.getWidth();
44241         }
44242         return box;
44243     },
44244     
44245     updateBox : function(box){
44246         if(this.split && !this.collapsed){
44247             var sw = this.split.el.getWidth();
44248             box.width -= sw;
44249             this.split.el.setLeft(box.x+box.width);
44250             this.split.el.setTop(box.y);
44251             this.split.el.setHeight(box.height);
44252         }
44253         if(this.collapsed){
44254             this.updateBody(null, box.height);
44255         }
44256         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44257     }
44258 });
44259 /*
44260  * Based on:
44261  * Ext JS Library 1.1.1
44262  * Copyright(c) 2006-2007, Ext JS, LLC.
44263  *
44264  * Originally Released Under LGPL - original licence link has changed is not relivant.
44265  *
44266  * Fork - LGPL
44267  * <script type="text/javascript">
44268  */
44269  
44270  
44271 /*
44272  * Private internal class for reading and applying state
44273  */
44274 Roo.LayoutStateManager = function(layout){
44275      // default empty state
44276      this.state = {
44277         north: {},
44278         south: {},
44279         east: {},
44280         west: {}       
44281     };
44282 };
44283
44284 Roo.LayoutStateManager.prototype = {
44285     init : function(layout, provider){
44286         this.provider = provider;
44287         var state = provider.get(layout.id+"-layout-state");
44288         if(state){
44289             var wasUpdating = layout.isUpdating();
44290             if(!wasUpdating){
44291                 layout.beginUpdate();
44292             }
44293             for(var key in state){
44294                 if(typeof state[key] != "function"){
44295                     var rstate = state[key];
44296                     var r = layout.getRegion(key);
44297                     if(r && rstate){
44298                         if(rstate.size){
44299                             r.resizeTo(rstate.size);
44300                         }
44301                         if(rstate.collapsed == true){
44302                             r.collapse(true);
44303                         }else{
44304                             r.expand(null, true);
44305                         }
44306                     }
44307                 }
44308             }
44309             if(!wasUpdating){
44310                 layout.endUpdate();
44311             }
44312             this.state = state; 
44313         }
44314         this.layout = layout;
44315         layout.on("regionresized", this.onRegionResized, this);
44316         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44317         layout.on("regionexpanded", this.onRegionExpanded, this);
44318     },
44319     
44320     storeState : function(){
44321         this.provider.set(this.layout.id+"-layout-state", this.state);
44322     },
44323     
44324     onRegionResized : function(region, newSize){
44325         this.state[region.getPosition()].size = newSize;
44326         this.storeState();
44327     },
44328     
44329     onRegionCollapsed : function(region){
44330         this.state[region.getPosition()].collapsed = true;
44331         this.storeState();
44332     },
44333     
44334     onRegionExpanded : function(region){
44335         this.state[region.getPosition()].collapsed = false;
44336         this.storeState();
44337     }
44338 };/*
44339  * Based on:
44340  * Ext JS Library 1.1.1
44341  * Copyright(c) 2006-2007, Ext JS, LLC.
44342  *
44343  * Originally Released Under LGPL - original licence link has changed is not relivant.
44344  *
44345  * Fork - LGPL
44346  * <script type="text/javascript">
44347  */
44348 /**
44349  * @class Roo.ContentPanel
44350  * @extends Roo.util.Observable
44351  * A basic ContentPanel element.
44352  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44353  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44354  * @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
44355  * @cfg {Boolean} closable True if the panel can be closed/removed
44356  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44357  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44358  * @cfg {Toolbar} toolbar A toolbar for this panel
44359  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44360  * @cfg {String} title The title for this panel
44361  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44362  * @cfg {String} url Calls {@link #setUrl} with this value
44363  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44364  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44365  * @constructor
44366  * Create a new ContentPanel.
44367  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44368  * @param {String/Object} config A string to set only the title or a config object
44369  * @param {String} content (optional) Set the HTML content for this panel
44370  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44371  */
44372 Roo.ContentPanel = function(el, config, content){
44373     
44374      
44375     /*
44376     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44377         config = el;
44378         el = Roo.id();
44379     }
44380     if (config && config.parentLayout) { 
44381         el = config.parentLayout.el.createChild(); 
44382     }
44383     */
44384     if(el.autoCreate){ // xtype is available if this is called from factory
44385         config = el;
44386         el = Roo.id();
44387     }
44388     this.el = Roo.get(el);
44389     if(!this.el && config && config.autoCreate){
44390         if(typeof config.autoCreate == "object"){
44391             if(!config.autoCreate.id){
44392                 config.autoCreate.id = config.id||el;
44393             }
44394             this.el = Roo.DomHelper.append(document.body,
44395                         config.autoCreate, true);
44396         }else{
44397             this.el = Roo.DomHelper.append(document.body,
44398                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44399         }
44400     }
44401     this.closable = false;
44402     this.loaded = false;
44403     this.active = false;
44404     if(typeof config == "string"){
44405         this.title = config;
44406     }else{
44407         Roo.apply(this, config);
44408     }
44409     
44410     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44411         this.wrapEl = this.el.wrap();    
44412         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44413         
44414     }
44415     
44416     
44417     
44418     if(this.resizeEl){
44419         this.resizeEl = Roo.get(this.resizeEl, true);
44420     }else{
44421         this.resizeEl = this.el;
44422     }
44423     this.addEvents({
44424         /**
44425          * @event activate
44426          * Fires when this panel is activated. 
44427          * @param {Roo.ContentPanel} this
44428          */
44429         "activate" : true,
44430         /**
44431          * @event deactivate
44432          * Fires when this panel is activated. 
44433          * @param {Roo.ContentPanel} this
44434          */
44435         "deactivate" : true,
44436
44437         /**
44438          * @event resize
44439          * Fires when this panel is resized if fitToFrame is true.
44440          * @param {Roo.ContentPanel} this
44441          * @param {Number} width The width after any component adjustments
44442          * @param {Number} height The height after any component adjustments
44443          */
44444         "resize" : true
44445     });
44446     if(this.autoScroll){
44447         this.resizeEl.setStyle("overflow", "auto");
44448     }
44449     content = content || this.content;
44450     if(content){
44451         this.setContent(content);
44452     }
44453     if(config && config.url){
44454         this.setUrl(this.url, this.params, this.loadOnce);
44455     }
44456     
44457     
44458     
44459     Roo.ContentPanel.superclass.constructor.call(this);
44460 };
44461
44462 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44463     tabTip:'',
44464     setRegion : function(region){
44465         this.region = region;
44466         if(region){
44467            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44468         }else{
44469            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44470         } 
44471     },
44472     
44473     /**
44474      * Returns the toolbar for this Panel if one was configured. 
44475      * @return {Roo.Toolbar} 
44476      */
44477     getToolbar : function(){
44478         return this.toolbar;
44479     },
44480     
44481     setActiveState : function(active){
44482         this.active = active;
44483         if(!active){
44484             this.fireEvent("deactivate", this);
44485         }else{
44486             this.fireEvent("activate", this);
44487         }
44488     },
44489     /**
44490      * Updates this panel's element
44491      * @param {String} content The new content
44492      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44493     */
44494     setContent : function(content, loadScripts){
44495         this.el.update(content, loadScripts);
44496     },
44497
44498     ignoreResize : function(w, h){
44499         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44500             return true;
44501         }else{
44502             this.lastSize = {width: w, height: h};
44503             return false;
44504         }
44505     },
44506     /**
44507      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44508      * @return {Roo.UpdateManager} The UpdateManager
44509      */
44510     getUpdateManager : function(){
44511         return this.el.getUpdateManager();
44512     },
44513      /**
44514      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44515      * @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:
44516 <pre><code>
44517 panel.load({
44518     url: "your-url.php",
44519     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44520     callback: yourFunction,
44521     scope: yourObject, //(optional scope)
44522     discardUrl: false,
44523     nocache: false,
44524     text: "Loading...",
44525     timeout: 30,
44526     scripts: false
44527 });
44528 </code></pre>
44529      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44530      * 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.
44531      * @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}
44532      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44533      * @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.
44534      * @return {Roo.ContentPanel} this
44535      */
44536     load : function(){
44537         var um = this.el.getUpdateManager();
44538         um.update.apply(um, arguments);
44539         return this;
44540     },
44541
44542
44543     /**
44544      * 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.
44545      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44546      * @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)
44547      * @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)
44548      * @return {Roo.UpdateManager} The UpdateManager
44549      */
44550     setUrl : function(url, params, loadOnce){
44551         if(this.refreshDelegate){
44552             this.removeListener("activate", this.refreshDelegate);
44553         }
44554         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44555         this.on("activate", this.refreshDelegate);
44556         return this.el.getUpdateManager();
44557     },
44558     
44559     _handleRefresh : function(url, params, loadOnce){
44560         if(!loadOnce || !this.loaded){
44561             var updater = this.el.getUpdateManager();
44562             updater.update(url, params, this._setLoaded.createDelegate(this));
44563         }
44564     },
44565     
44566     _setLoaded : function(){
44567         this.loaded = true;
44568     }, 
44569     
44570     /**
44571      * Returns this panel's id
44572      * @return {String} 
44573      */
44574     getId : function(){
44575         return this.el.id;
44576     },
44577     
44578     /** 
44579      * Returns this panel's element - used by regiosn to add.
44580      * @return {Roo.Element} 
44581      */
44582     getEl : function(){
44583         return this.wrapEl || this.el;
44584     },
44585     
44586     adjustForComponents : function(width, height){
44587         if(this.resizeEl != this.el){
44588             width -= this.el.getFrameWidth('lr');
44589             height -= this.el.getFrameWidth('tb');
44590         }
44591         if(this.toolbar){
44592             var te = this.toolbar.getEl();
44593             height -= te.getHeight();
44594             te.setWidth(width);
44595         }
44596         if(this.adjustments){
44597             width += this.adjustments[0];
44598             height += this.adjustments[1];
44599         }
44600         return {"width": width, "height": height};
44601     },
44602     
44603     setSize : function(width, height){
44604         if(this.fitToFrame && !this.ignoreResize(width, height)){
44605             if(this.fitContainer && this.resizeEl != this.el){
44606                 this.el.setSize(width, height);
44607             }
44608             var size = this.adjustForComponents(width, height);
44609             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44610             this.fireEvent('resize', this, size.width, size.height);
44611         }
44612     },
44613     
44614     /**
44615      * Returns this panel's title
44616      * @return {String} 
44617      */
44618     getTitle : function(){
44619         return this.title;
44620     },
44621     
44622     /**
44623      * Set this panel's title
44624      * @param {String} title
44625      */
44626     setTitle : function(title){
44627         this.title = title;
44628         if(this.region){
44629             this.region.updatePanelTitle(this, title);
44630         }
44631     },
44632     
44633     /**
44634      * Returns true is this panel was configured to be closable
44635      * @return {Boolean} 
44636      */
44637     isClosable : function(){
44638         return this.closable;
44639     },
44640     
44641     beforeSlide : function(){
44642         this.el.clip();
44643         this.resizeEl.clip();
44644     },
44645     
44646     afterSlide : function(){
44647         this.el.unclip();
44648         this.resizeEl.unclip();
44649     },
44650     
44651     /**
44652      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44653      *   Will fail silently if the {@link #setUrl} method has not been called.
44654      *   This does not activate the panel, just updates its content.
44655      */
44656     refresh : function(){
44657         if(this.refreshDelegate){
44658            this.loaded = false;
44659            this.refreshDelegate();
44660         }
44661     },
44662     
44663     /**
44664      * Destroys this panel
44665      */
44666     destroy : function(){
44667         this.el.removeAllListeners();
44668         var tempEl = document.createElement("span");
44669         tempEl.appendChild(this.el.dom);
44670         tempEl.innerHTML = "";
44671         this.el.remove();
44672         this.el = null;
44673     },
44674     
44675       /**
44676      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44677      * <pre><code>
44678
44679 layout.addxtype({
44680        xtype : 'Form',
44681        items: [ .... ]
44682    }
44683 );
44684
44685 </code></pre>
44686      * @param {Object} cfg Xtype definition of item to add.
44687      */
44688     
44689     addxtype : function(cfg) {
44690         // add form..
44691         if (cfg.xtype.match(/^Form$/)) {
44692             var el = this.el.createChild();
44693
44694             this.form = new  Roo.form.Form(cfg);
44695             
44696             
44697             if ( this.form.allItems.length) this.form.render(el.dom);
44698             return this.form;
44699         }
44700         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
44701             // views..
44702             cfg.el = this.el;
44703             // factory?
44704             return new Roo[cfg.xtype](cfg);
44705             
44706         }
44707         return false;
44708         
44709     }
44710 });
44711
44712 /**
44713  * @class Roo.GridPanel
44714  * @extends Roo.ContentPanel
44715  * @constructor
44716  * Create a new GridPanel.
44717  * @param {Roo.grid.Grid} grid The grid for this panel
44718  * @param {String/Object} config A string to set only the panel's title, or a config object
44719  */
44720 Roo.GridPanel = function(grid, config){
44721     
44722   
44723     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44724         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44725         
44726     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44727     
44728     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44729     
44730     if(this.toolbar){
44731         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44732     }
44733     // xtype created footer. - not sure if will work as we normally have to render first..
44734     if (this.footer && !this.footer.el && this.footer.xtype) {
44735         
44736         this.footer.container = this.grid.getView().getFooterPanel(true);
44737         this.footer.dataSource = this.grid.dataSource;
44738         this.footer = Roo.factory(this.footer, Roo);
44739         
44740     }
44741     
44742     grid.monitorWindowResize = false; // turn off autosizing
44743     grid.autoHeight = false;
44744     grid.autoWidth = false;
44745     this.grid = grid;
44746     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44747 };
44748
44749 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44750     getId : function(){
44751         return this.grid.id;
44752     },
44753     
44754     /**
44755      * Returns the grid for this panel
44756      * @return {Roo.grid.Grid} 
44757      */
44758     getGrid : function(){
44759         return this.grid;    
44760     },
44761     
44762     setSize : function(width, height){
44763         if(!this.ignoreResize(width, height)){
44764             var grid = this.grid;
44765             var size = this.adjustForComponents(width, height);
44766             grid.getGridEl().setSize(size.width, size.height);
44767             grid.autoSize();
44768         }
44769     },
44770     
44771     beforeSlide : function(){
44772         this.grid.getView().scroller.clip();
44773     },
44774     
44775     afterSlide : function(){
44776         this.grid.getView().scroller.unclip();
44777     },
44778     
44779     destroy : function(){
44780         this.grid.destroy();
44781         delete this.grid;
44782         Roo.GridPanel.superclass.destroy.call(this); 
44783     }
44784 });
44785
44786
44787 /**
44788  * @class Roo.NestedLayoutPanel
44789  * @extends Roo.ContentPanel
44790  * @constructor
44791  * Create a new NestedLayoutPanel.
44792  * 
44793  * 
44794  * @param {Roo.BorderLayout} layout The layout for this panel
44795  * @param {String/Object} config A string to set only the title or a config object
44796  */
44797 Roo.NestedLayoutPanel = function(layout, config)
44798 {
44799     // construct with only one argument..
44800     /* FIXME - implement nicer consturctors
44801     if (layout.layout) {
44802         config = layout;
44803         layout = config.layout;
44804         delete config.layout;
44805     }
44806     if (layout.xtype && !layout.getEl) {
44807         // then layout needs constructing..
44808         layout = Roo.factory(layout, Roo);
44809     }
44810     */
44811     
44812     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44813     
44814     layout.monitorWindowResize = false; // turn off autosizing
44815     this.layout = layout;
44816     this.layout.getEl().addClass("x-layout-nested-layout");
44817     
44818     
44819     
44820 };
44821
44822 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44823
44824     setSize : function(width, height){
44825         if(!this.ignoreResize(width, height)){
44826             var size = this.adjustForComponents(width, height);
44827             var el = this.layout.getEl();
44828             el.setSize(size.width, size.height);
44829             var touch = el.dom.offsetWidth;
44830             this.layout.layout();
44831             // ie requires a double layout on the first pass
44832             if(Roo.isIE && !this.initialized){
44833                 this.initialized = true;
44834                 this.layout.layout();
44835             }
44836         }
44837     },
44838     
44839     // activate all subpanels if not currently active..
44840     
44841     setActiveState : function(active){
44842         this.active = active;
44843         if(!active){
44844             this.fireEvent("deactivate", this);
44845             return;
44846         }
44847         
44848         this.fireEvent("activate", this);
44849         // not sure if this should happen before or after..
44850         if (!this.layout) {
44851             return; // should not happen..
44852         }
44853         var reg = false;
44854         for (var r in this.layout.regions) {
44855             reg = this.layout.getRegion(r);
44856             if (reg.getActivePanel()) {
44857                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44858                 reg.setActivePanel(reg.getActivePanel());
44859                 continue;
44860             }
44861             if (!reg.panels.length) {
44862                 continue;
44863             }
44864             reg.showPanel(reg.getPanel(0));
44865         }
44866         
44867         
44868         
44869         
44870     },
44871     
44872     /**
44873      * Returns the nested BorderLayout for this panel
44874      * @return {Roo.BorderLayout} 
44875      */
44876     getLayout : function(){
44877         return this.layout;
44878     },
44879     
44880      /**
44881      * Adds a xtype elements to the layout of the nested panel
44882      * <pre><code>
44883
44884 panel.addxtype({
44885        xtype : 'ContentPanel',
44886        region: 'west',
44887        items: [ .... ]
44888    }
44889 );
44890
44891 panel.addxtype({
44892         xtype : 'NestedLayoutPanel',
44893         region: 'west',
44894         layout: {
44895            center: { },
44896            west: { }   
44897         },
44898         items : [ ... list of content panels or nested layout panels.. ]
44899    }
44900 );
44901 </code></pre>
44902      * @param {Object} cfg Xtype definition of item to add.
44903      */
44904     addxtype : function(cfg) {
44905         return this.layout.addxtype(cfg);
44906     
44907     }
44908 });
44909
44910 Roo.ScrollPanel = function(el, config, content){
44911     config = config || {};
44912     config.fitToFrame = true;
44913     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
44914     
44915     this.el.dom.style.overflow = "hidden";
44916     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
44917     this.el.removeClass("x-layout-inactive-content");
44918     this.el.on("mousewheel", this.onWheel, this);
44919
44920     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
44921     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
44922     up.unselectable(); down.unselectable();
44923     up.on("click", this.scrollUp, this);
44924     down.on("click", this.scrollDown, this);
44925     up.addClassOnOver("x-scroller-btn-over");
44926     down.addClassOnOver("x-scroller-btn-over");
44927     up.addClassOnClick("x-scroller-btn-click");
44928     down.addClassOnClick("x-scroller-btn-click");
44929     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
44930
44931     this.resizeEl = this.el;
44932     this.el = wrap; this.up = up; this.down = down;
44933 };
44934
44935 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
44936     increment : 100,
44937     wheelIncrement : 5,
44938     scrollUp : function(){
44939         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
44940     },
44941
44942     scrollDown : function(){
44943         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
44944     },
44945
44946     afterScroll : function(){
44947         var el = this.resizeEl;
44948         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
44949         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44950         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
44951     },
44952
44953     setSize : function(){
44954         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
44955         this.afterScroll();
44956     },
44957
44958     onWheel : function(e){
44959         var d = e.getWheelDelta();
44960         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
44961         this.afterScroll();
44962         e.stopEvent();
44963     },
44964
44965     setContent : function(content, loadScripts){
44966         this.resizeEl.update(content, loadScripts);
44967     }
44968
44969 });
44970
44971
44972
44973
44974
44975
44976
44977
44978
44979 /**
44980  * @class Roo.TreePanel
44981  * @extends Roo.ContentPanel
44982  * @constructor
44983  * Create a new TreePanel.
44984  * @param {String/Object} config A string to set only the panel's title, or a config object
44985  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
44986  */
44987 Roo.TreePanel = function(config){
44988     var el = config.el;
44989     var tree = config.tree;
44990     delete config.tree; 
44991     delete config.el; // hopefull!
44992     Roo.TreePanel.superclass.constructor.call(this, el, config);
44993     var treeEl = el.createChild();
44994     this.tree = new Roo.tree.TreePanel(treeEl , tree);
44995     //console.log(tree);
44996     this.on('activate', function()
44997     {
44998         if (this.tree.rendered) {
44999             return;
45000         }
45001         //console.log('render tree');
45002         this.tree.render();
45003     });
45004     
45005     this.on('resize',  function (cp, w, h) {
45006             this.tree.innerCt.setWidth(w);
45007             this.tree.innerCt.setHeight(h);
45008             this.tree.innerCt.setStyle('overflow-y', 'auto');
45009     });
45010
45011         
45012     
45013 };
45014
45015 Roo.extend(Roo.TreePanel, Roo.ContentPanel);
45016
45017
45018
45019
45020
45021
45022
45023
45024
45025
45026
45027 /*
45028  * Based on:
45029  * Ext JS Library 1.1.1
45030  * Copyright(c) 2006-2007, Ext JS, LLC.
45031  *
45032  * Originally Released Under LGPL - original licence link has changed is not relivant.
45033  *
45034  * Fork - LGPL
45035  * <script type="text/javascript">
45036  */
45037  
45038
45039 /**
45040  * @class Roo.ReaderLayout
45041  * @extends Roo.BorderLayout
45042  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45043  * center region containing two nested regions (a top one for a list view and one for item preview below),
45044  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45045  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45046  * expedites the setup of the overall layout and regions for this common application style.
45047  * Example:
45048  <pre><code>
45049 var reader = new Roo.ReaderLayout();
45050 var CP = Roo.ContentPanel;  // shortcut for adding
45051
45052 reader.beginUpdate();
45053 reader.add("north", new CP("north", "North"));
45054 reader.add("west", new CP("west", {title: "West"}));
45055 reader.add("east", new CP("east", {title: "East"}));
45056
45057 reader.regions.listView.add(new CP("listView", "List"));
45058 reader.regions.preview.add(new CP("preview", "Preview"));
45059 reader.endUpdate();
45060 </code></pre>
45061 * @constructor
45062 * Create a new ReaderLayout
45063 * @param {Object} config Configuration options
45064 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45065 * document.body if omitted)
45066 */
45067 Roo.ReaderLayout = function(config, renderTo){
45068     var c = config || {size:{}};
45069     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45070         north: c.north !== false ? Roo.apply({
45071             split:false,
45072             initialSize: 32,
45073             titlebar: false
45074         }, c.north) : false,
45075         west: c.west !== false ? Roo.apply({
45076             split:true,
45077             initialSize: 200,
45078             minSize: 175,
45079             maxSize: 400,
45080             titlebar: true,
45081             collapsible: true,
45082             animate: true,
45083             margins:{left:5,right:0,bottom:5,top:5},
45084             cmargins:{left:5,right:5,bottom:5,top:5}
45085         }, c.west) : false,
45086         east: c.east !== false ? Roo.apply({
45087             split:true,
45088             initialSize: 200,
45089             minSize: 175,
45090             maxSize: 400,
45091             titlebar: true,
45092             collapsible: true,
45093             animate: true,
45094             margins:{left:0,right:5,bottom:5,top:5},
45095             cmargins:{left:5,right:5,bottom:5,top:5}
45096         }, c.east) : false,
45097         center: Roo.apply({
45098             tabPosition: 'top',
45099             autoScroll:false,
45100             closeOnTab: true,
45101             titlebar:false,
45102             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45103         }, c.center)
45104     });
45105
45106     this.el.addClass('x-reader');
45107
45108     this.beginUpdate();
45109
45110     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45111         south: c.preview !== false ? Roo.apply({
45112             split:true,
45113             initialSize: 200,
45114             minSize: 100,
45115             autoScroll:true,
45116             collapsible:true,
45117             titlebar: true,
45118             cmargins:{top:5,left:0, right:0, bottom:0}
45119         }, c.preview) : false,
45120         center: Roo.apply({
45121             autoScroll:false,
45122             titlebar:false,
45123             minHeight:200
45124         }, c.listView)
45125     });
45126     this.add('center', new Roo.NestedLayoutPanel(inner,
45127             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45128
45129     this.endUpdate();
45130
45131     this.regions.preview = inner.getRegion('south');
45132     this.regions.listView = inner.getRegion('center');
45133 };
45134
45135 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45136  * Based on:
45137  * Ext JS Library 1.1.1
45138  * Copyright(c) 2006-2007, Ext JS, LLC.
45139  *
45140  * Originally Released Under LGPL - original licence link has changed is not relivant.
45141  *
45142  * Fork - LGPL
45143  * <script type="text/javascript">
45144  */
45145  
45146 /**
45147  * @class Roo.grid.Grid
45148  * @extends Roo.util.Observable
45149  * This class represents the primary interface of a component based grid control.
45150  * <br><br>Usage:<pre><code>
45151  var grid = new Roo.grid.Grid("my-container-id", {
45152      ds: myDataStore,
45153      cm: myColModel,
45154      selModel: mySelectionModel,
45155      autoSizeColumns: true,
45156      monitorWindowResize: false,
45157      trackMouseOver: true
45158  });
45159  // set any options
45160  grid.render();
45161  * </code></pre>
45162  * <b>Common Problems:</b><br/>
45163  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45164  * element will correct this<br/>
45165  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45166  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45167  * are unpredictable.<br/>
45168  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45169  * grid to calculate dimensions/offsets.<br/>
45170   * @constructor
45171  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45172  * The container MUST have some type of size defined for the grid to fill. The container will be
45173  * automatically set to position relative if it isn't already.
45174  * @param {Object} config A config object that sets properties on this grid.
45175  */
45176 Roo.grid.Grid = function(container, config){
45177         // initialize the container
45178         this.container = Roo.get(container);
45179         this.container.update("");
45180         this.container.setStyle("overflow", "hidden");
45181     this.container.addClass('x-grid-container');
45182
45183     this.id = this.container.id;
45184
45185     Roo.apply(this, config);
45186     // check and correct shorthanded configs
45187     if(this.ds){
45188         this.dataSource = this.ds;
45189         delete this.ds;
45190     }
45191     if(this.cm){
45192         this.colModel = this.cm;
45193         delete this.cm;
45194     }
45195     if(this.sm){
45196         this.selModel = this.sm;
45197         delete this.sm;
45198     }
45199
45200     if (this.selModel) {
45201         this.selModel = Roo.factory(this.selModel, Roo.grid);
45202         this.sm = this.selModel;
45203         this.sm.xmodule = this.xmodule || false;
45204     }
45205     if (typeof(this.colModel.config) == 'undefined') {
45206         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45207         this.cm = this.colModel;
45208         this.cm.xmodule = this.xmodule || false;
45209     }
45210     if (this.dataSource) {
45211         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45212         this.ds = this.dataSource;
45213         this.ds.xmodule = this.xmodule || false;
45214         
45215     }
45216     
45217     
45218     
45219     if(this.width){
45220         this.container.setWidth(this.width);
45221     }
45222
45223     if(this.height){
45224         this.container.setHeight(this.height);
45225     }
45226     /** @private */
45227         this.addEvents({
45228             // raw events
45229             /**
45230              * @event click
45231              * The raw click event for the entire grid.
45232              * @param {Roo.EventObject} e
45233              */
45234             "click" : true,
45235             /**
45236              * @event dblclick
45237              * The raw dblclick event for the entire grid.
45238              * @param {Roo.EventObject} e
45239              */
45240             "dblclick" : true,
45241             /**
45242              * @event contextmenu
45243              * The raw contextmenu event for the entire grid.
45244              * @param {Roo.EventObject} e
45245              */
45246             "contextmenu" : true,
45247             /**
45248              * @event mousedown
45249              * The raw mousedown event for the entire grid.
45250              * @param {Roo.EventObject} e
45251              */
45252             "mousedown" : true,
45253             /**
45254              * @event mouseup
45255              * The raw mouseup event for the entire grid.
45256              * @param {Roo.EventObject} e
45257              */
45258             "mouseup" : true,
45259             /**
45260              * @event mouseover
45261              * The raw mouseover event for the entire grid.
45262              * @param {Roo.EventObject} e
45263              */
45264             "mouseover" : true,
45265             /**
45266              * @event mouseout
45267              * The raw mouseout event for the entire grid.
45268              * @param {Roo.EventObject} e
45269              */
45270             "mouseout" : true,
45271             /**
45272              * @event keypress
45273              * The raw keypress event for the entire grid.
45274              * @param {Roo.EventObject} e
45275              */
45276             "keypress" : true,
45277             /**
45278              * @event keydown
45279              * The raw keydown event for the entire grid.
45280              * @param {Roo.EventObject} e
45281              */
45282             "keydown" : true,
45283
45284             // custom events
45285
45286             /**
45287              * @event cellclick
45288              * Fires when a cell is clicked
45289              * @param {Grid} this
45290              * @param {Number} rowIndex
45291              * @param {Number} columnIndex
45292              * @param {Roo.EventObject} e
45293              */
45294             "cellclick" : true,
45295             /**
45296              * @event celldblclick
45297              * Fires when a cell is double clicked
45298              * @param {Grid} this
45299              * @param {Number} rowIndex
45300              * @param {Number} columnIndex
45301              * @param {Roo.EventObject} e
45302              */
45303             "celldblclick" : true,
45304             /**
45305              * @event rowclick
45306              * Fires when a row is clicked
45307              * @param {Grid} this
45308              * @param {Number} rowIndex
45309              * @param {Roo.EventObject} e
45310              */
45311             "rowclick" : true,
45312             /**
45313              * @event rowdblclick
45314              * Fires when a row is double clicked
45315              * @param {Grid} this
45316              * @param {Number} rowIndex
45317              * @param {Roo.EventObject} e
45318              */
45319             "rowdblclick" : true,
45320             /**
45321              * @event headerclick
45322              * Fires when a header is clicked
45323              * @param {Grid} this
45324              * @param {Number} columnIndex
45325              * @param {Roo.EventObject} e
45326              */
45327             "headerclick" : true,
45328             /**
45329              * @event headerdblclick
45330              * Fires when a header cell is double clicked
45331              * @param {Grid} this
45332              * @param {Number} columnIndex
45333              * @param {Roo.EventObject} e
45334              */
45335             "headerdblclick" : true,
45336             /**
45337              * @event rowcontextmenu
45338              * Fires when a row is right clicked
45339              * @param {Grid} this
45340              * @param {Number} rowIndex
45341              * @param {Roo.EventObject} e
45342              */
45343             "rowcontextmenu" : true,
45344             /**
45345          * @event cellcontextmenu
45346          * Fires when a cell is right clicked
45347          * @param {Grid} this
45348          * @param {Number} rowIndex
45349          * @param {Number} cellIndex
45350          * @param {Roo.EventObject} e
45351          */
45352          "cellcontextmenu" : true,
45353             /**
45354              * @event headercontextmenu
45355              * Fires when a header is right clicked
45356              * @param {Grid} this
45357              * @param {Number} columnIndex
45358              * @param {Roo.EventObject} e
45359              */
45360             "headercontextmenu" : true,
45361             /**
45362              * @event bodyscroll
45363              * Fires when the body element is scrolled
45364              * @param {Number} scrollLeft
45365              * @param {Number} scrollTop
45366              */
45367             "bodyscroll" : true,
45368             /**
45369              * @event columnresize
45370              * Fires when the user resizes a column
45371              * @param {Number} columnIndex
45372              * @param {Number} newSize
45373              */
45374             "columnresize" : true,
45375             /**
45376              * @event columnmove
45377              * Fires when the user moves a column
45378              * @param {Number} oldIndex
45379              * @param {Number} newIndex
45380              */
45381             "columnmove" : true,
45382             /**
45383              * @event startdrag
45384              * Fires when row(s) start being dragged
45385              * @param {Grid} this
45386              * @param {Roo.GridDD} dd The drag drop object
45387              * @param {event} e The raw browser event
45388              */
45389             "startdrag" : true,
45390             /**
45391              * @event enddrag
45392              * Fires when a drag operation is complete
45393              * @param {Grid} this
45394              * @param {Roo.GridDD} dd The drag drop object
45395              * @param {event} e The raw browser event
45396              */
45397             "enddrag" : true,
45398             /**
45399              * @event dragdrop
45400              * Fires when dragged row(s) are dropped on a valid DD target
45401              * @param {Grid} this
45402              * @param {Roo.GridDD} dd The drag drop object
45403              * @param {String} targetId The target drag drop object
45404              * @param {event} e The raw browser event
45405              */
45406             "dragdrop" : true,
45407             /**
45408              * @event dragover
45409              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45410              * @param {Grid} this
45411              * @param {Roo.GridDD} dd The drag drop object
45412              * @param {String} targetId The target drag drop object
45413              * @param {event} e The raw browser event
45414              */
45415             "dragover" : true,
45416             /**
45417              * @event dragenter
45418              *  Fires when the dragged row(s) first cross another DD target while being dragged
45419              * @param {Grid} this
45420              * @param {Roo.GridDD} dd The drag drop object
45421              * @param {String} targetId The target drag drop object
45422              * @param {event} e The raw browser event
45423              */
45424             "dragenter" : true,
45425             /**
45426              * @event dragout
45427              * Fires when the dragged row(s) leave another DD target while being dragged
45428              * @param {Grid} this
45429              * @param {Roo.GridDD} dd The drag drop object
45430              * @param {String} targetId The target drag drop object
45431              * @param {event} e The raw browser event
45432              */
45433             "dragout" : true,
45434         /**
45435          * @event render
45436          * Fires when the grid is rendered
45437          * @param {Grid} grid
45438          */
45439         render : true
45440     });
45441
45442     Roo.grid.Grid.superclass.constructor.call(this);
45443 };
45444 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45445     /**
45446      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45447          */
45448         minColumnWidth : 25,
45449
45450     /**
45451          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45452          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45453          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45454          */
45455         autoSizeColumns : false,
45456
45457         /**
45458          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45459          */
45460         autoSizeHeaders : true,
45461
45462         /**
45463          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45464          */
45465         monitorWindowResize : true,
45466
45467         /**
45468          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45469          * rows measured to get a columns size. Default is 0 (all rows).
45470          */
45471         maxRowsToMeasure : 0,
45472
45473         /**
45474          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45475          */
45476         trackMouseOver : true,
45477
45478         /**
45479          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45480          */
45481         enableDragDrop : false,
45482
45483         /**
45484          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45485          */
45486         enableColumnMove : true,
45487
45488         /**
45489          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45490          */
45491         enableColumnHide : true,
45492
45493         /**
45494          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45495          */
45496         enableRowHeightSync : false,
45497
45498         /**
45499          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45500          */
45501         stripeRows : true,
45502
45503         /**
45504          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45505          */
45506         autoHeight : false,
45507
45508     /**
45509      * @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.
45510      */
45511     autoExpandColumn : false,
45512
45513     /**
45514     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45515     * Default is 50.
45516     */
45517     autoExpandMin : 50,
45518
45519     /**
45520     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45521     */
45522     autoExpandMax : 1000,
45523
45524     /**
45525          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45526          */
45527         view : null,
45528
45529         /**
45530      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45531          */
45532         loadMask : false,
45533
45534     // private
45535     rendered : false,
45536
45537     /**
45538     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45539     * of a fixed width. Default is false.
45540     */
45541     /**
45542     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45543     */
45544     /**
45545      * Called once after all setup has been completed and the grid is ready to be rendered.
45546      * @return {Roo.grid.Grid} this
45547      */
45548     render : function(){
45549         var c = this.container;
45550         // try to detect autoHeight/width mode
45551         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45552             this.autoHeight = true;
45553         }
45554         var view = this.getView();
45555         view.init(this);
45556
45557         c.on("click", this.onClick, this);
45558         c.on("dblclick", this.onDblClick, this);
45559         c.on("contextmenu", this.onContextMenu, this);
45560         c.on("keydown", this.onKeyDown, this);
45561
45562         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45563
45564         this.getSelectionModel().init(this);
45565
45566         view.render();
45567
45568         if(this.loadMask){
45569             this.loadMask = new Roo.LoadMask(this.container,
45570                     Roo.apply({store:this.dataSource}, this.loadMask));
45571         }
45572         
45573         
45574         if (this.toolbar && this.toolbar.xtype) {
45575             this.toolbar.container = this.getView().getHeaderPanel(true);
45576             this.toolbar = new Ext.Toolbar(this.toolbar);
45577         }
45578         if (this.footer && this.footer.xtype) {
45579             this.footer.dataSource = this.getDataSource();
45580             this.footer.container = this.getView().getFooterPanel(true);
45581             this.footer = Roo.factory(this.footer, Roo);
45582         }
45583         this.rendered = true;
45584         this.fireEvent('render', this);
45585         return this;
45586     },
45587
45588         /**
45589          * Reconfigures the grid to use a different Store and Column Model.
45590          * The View will be bound to the new objects and refreshed.
45591          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45592          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45593          */
45594     reconfigure : function(dataSource, colModel){
45595         if(this.loadMask){
45596             this.loadMask.destroy();
45597             this.loadMask = new Roo.LoadMask(this.container,
45598                     Roo.apply({store:dataSource}, this.loadMask));
45599         }
45600         this.view.bind(dataSource, colModel);
45601         this.dataSource = dataSource;
45602         this.colModel = colModel;
45603         this.view.refresh(true);
45604     },
45605
45606     // private
45607     onKeyDown : function(e){
45608         this.fireEvent("keydown", e);
45609     },
45610
45611     /**
45612      * Destroy this grid.
45613      * @param {Boolean} removeEl True to remove the element
45614      */
45615     destroy : function(removeEl, keepListeners){
45616         if(this.loadMask){
45617             this.loadMask.destroy();
45618         }
45619         var c = this.container;
45620         c.removeAllListeners();
45621         this.view.destroy();
45622         this.colModel.purgeListeners();
45623         if(!keepListeners){
45624             this.purgeListeners();
45625         }
45626         c.update("");
45627         if(removeEl === true){
45628             c.remove();
45629         }
45630     },
45631
45632     // private
45633     processEvent : function(name, e){
45634         this.fireEvent(name, e);
45635         var t = e.getTarget();
45636         var v = this.view;
45637         var header = v.findHeaderIndex(t);
45638         if(header !== false){
45639             this.fireEvent("header" + name, this, header, e);
45640         }else{
45641             var row = v.findRowIndex(t);
45642             var cell = v.findCellIndex(t);
45643             if(row !== false){
45644                 this.fireEvent("row" + name, this, row, e);
45645                 if(cell !== false){
45646                     this.fireEvent("cell" + name, this, row, cell, e);
45647                 }
45648             }
45649         }
45650     },
45651
45652     // private
45653     onClick : function(e){
45654         this.processEvent("click", e);
45655     },
45656
45657     // private
45658     onContextMenu : function(e, t){
45659         this.processEvent("contextmenu", e);
45660     },
45661
45662     // private
45663     onDblClick : function(e){
45664         this.processEvent("dblclick", e);
45665     },
45666
45667     // private
45668     walkCells : function(row, col, step, fn, scope){
45669         var cm = this.colModel, clen = cm.getColumnCount();
45670         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45671         if(step < 0){
45672             if(col < 0){
45673                 row--;
45674                 first = false;
45675             }
45676             while(row >= 0){
45677                 if(!first){
45678                     col = clen-1;
45679                 }
45680                 first = false;
45681                 while(col >= 0){
45682                     if(fn.call(scope || this, row, col, cm) === true){
45683                         return [row, col];
45684                     }
45685                     col--;
45686                 }
45687                 row--;
45688             }
45689         } else {
45690             if(col >= clen){
45691                 row++;
45692                 first = false;
45693             }
45694             while(row < rlen){
45695                 if(!first){
45696                     col = 0;
45697                 }
45698                 first = false;
45699                 while(col < clen){
45700                     if(fn.call(scope || this, row, col, cm) === true){
45701                         return [row, col];
45702                     }
45703                     col++;
45704                 }
45705                 row++;
45706             }
45707         }
45708         return null;
45709     },
45710
45711     // private
45712     getSelections : function(){
45713         return this.selModel.getSelections();
45714     },
45715
45716     /**
45717      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45718      * but if manual update is required this method will initiate it.
45719      */
45720     autoSize : function(){
45721         if(this.rendered){
45722             this.view.layout();
45723             if(this.view.adjustForScroll){
45724                 this.view.adjustForScroll();
45725             }
45726         }
45727     },
45728
45729     /**
45730      * Returns the grid's underlying element.
45731      * @return {Element} The element
45732      */
45733     getGridEl : function(){
45734         return this.container;
45735     },
45736
45737     // private for compatibility, overridden by editor grid
45738     stopEditing : function(){},
45739
45740     /**
45741      * Returns the grid's SelectionModel.
45742      * @return {SelectionModel}
45743      */
45744     getSelectionModel : function(){
45745         if(!this.selModel){
45746             this.selModel = new Roo.grid.RowSelectionModel();
45747         }
45748         return this.selModel;
45749     },
45750
45751     /**
45752      * Returns the grid's DataSource.
45753      * @return {DataSource}
45754      */
45755     getDataSource : function(){
45756         return this.dataSource;
45757     },
45758
45759     /**
45760      * Returns the grid's ColumnModel.
45761      * @return {ColumnModel}
45762      */
45763     getColumnModel : function(){
45764         return this.colModel;
45765     },
45766
45767     /**
45768      * Returns the grid's GridView object.
45769      * @return {GridView}
45770      */
45771     getView : function(){
45772         if(!this.view){
45773             this.view = new Roo.grid.GridView(this.viewConfig);
45774         }
45775         return this.view;
45776     },
45777     /**
45778      * Called to get grid's drag proxy text, by default returns this.ddText.
45779      * @return {String}
45780      */
45781     getDragDropText : function(){
45782         var count = this.selModel.getCount();
45783         return String.format(this.ddText, count, count == 1 ? '' : 's');
45784     }
45785 });
45786 /**
45787  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45788  * %0 is replaced with the number of selected rows.
45789  * @type String
45790  */
45791 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45792  * Based on:
45793  * Ext JS Library 1.1.1
45794  * Copyright(c) 2006-2007, Ext JS, LLC.
45795  *
45796  * Originally Released Under LGPL - original licence link has changed is not relivant.
45797  *
45798  * Fork - LGPL
45799  * <script type="text/javascript">
45800  */
45801  
45802 Roo.grid.AbstractGridView = function(){
45803         this.grid = null;
45804         
45805         this.events = {
45806             "beforerowremoved" : true,
45807             "beforerowsinserted" : true,
45808             "beforerefresh" : true,
45809             "rowremoved" : true,
45810             "rowsinserted" : true,
45811             "rowupdated" : true,
45812             "refresh" : true
45813         };
45814     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45815 };
45816
45817 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45818     rowClass : "x-grid-row",
45819     cellClass : "x-grid-cell",
45820     tdClass : "x-grid-td",
45821     hdClass : "x-grid-hd",
45822     splitClass : "x-grid-hd-split",
45823     
45824         init: function(grid){
45825         this.grid = grid;
45826                 var cid = this.grid.getGridEl().id;
45827         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45828         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45829         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45830         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45831         },
45832         
45833         getColumnRenderers : function(){
45834         var renderers = [];
45835         var cm = this.grid.colModel;
45836         var colCount = cm.getColumnCount();
45837         for(var i = 0; i < colCount; i++){
45838             renderers[i] = cm.getRenderer(i);
45839         }
45840         return renderers;
45841     },
45842     
45843     getColumnIds : function(){
45844         var ids = [];
45845         var cm = this.grid.colModel;
45846         var colCount = cm.getColumnCount();
45847         for(var i = 0; i < colCount; i++){
45848             ids[i] = cm.getColumnId(i);
45849         }
45850         return ids;
45851     },
45852     
45853     getDataIndexes : function(){
45854         if(!this.indexMap){
45855             this.indexMap = this.buildIndexMap();
45856         }
45857         return this.indexMap.colToData;
45858     },
45859     
45860     getColumnIndexByDataIndex : function(dataIndex){
45861         if(!this.indexMap){
45862             this.indexMap = this.buildIndexMap();
45863         }
45864         return this.indexMap.dataToCol[dataIndex];
45865     },
45866     
45867     /**
45868      * Set a css style for a column dynamically. 
45869      * @param {Number} colIndex The index of the column
45870      * @param {String} name The css property name
45871      * @param {String} value The css value
45872      */
45873     setCSSStyle : function(colIndex, name, value){
45874         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
45875         Roo.util.CSS.updateRule(selector, name, value);
45876     },
45877     
45878     generateRules : function(cm){
45879         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
45880         Roo.util.CSS.removeStyleSheet(rulesId);
45881         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
45882             var cid = cm.getColumnId(i);
45883             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
45884                          this.tdSelector, cid, " {\n}\n",
45885                          this.hdSelector, cid, " {\n}\n",
45886                          this.splitSelector, cid, " {\n}\n");
45887         }
45888         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
45889     }
45890 });/*
45891  * Based on:
45892  * Ext JS Library 1.1.1
45893  * Copyright(c) 2006-2007, Ext JS, LLC.
45894  *
45895  * Originally Released Under LGPL - original licence link has changed is not relivant.
45896  *
45897  * Fork - LGPL
45898  * <script type="text/javascript">
45899  */
45900
45901 // private
45902 // This is a support class used internally by the Grid components
45903 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
45904     this.grid = grid;
45905     this.view = grid.getView();
45906     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45907     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
45908     if(hd2){
45909         this.setHandleElId(Roo.id(hd));
45910         this.setOuterHandleElId(Roo.id(hd2));
45911     }
45912     this.scroll = false;
45913 };
45914 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
45915     maxDragWidth: 120,
45916     getDragData : function(e){
45917         var t = Roo.lib.Event.getTarget(e);
45918         var h = this.view.findHeaderCell(t);
45919         if(h){
45920             return {ddel: h.firstChild, header:h};
45921         }
45922         return false;
45923     },
45924
45925     onInitDrag : function(e){
45926         this.view.headersDisabled = true;
45927         var clone = this.dragData.ddel.cloneNode(true);
45928         clone.id = Roo.id();
45929         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
45930         this.proxy.update(clone);
45931         return true;
45932     },
45933
45934     afterValidDrop : function(){
45935         var v = this.view;
45936         setTimeout(function(){
45937             v.headersDisabled = false;
45938         }, 50);
45939     },
45940
45941     afterInvalidDrop : function(){
45942         var v = this.view;
45943         setTimeout(function(){
45944             v.headersDisabled = false;
45945         }, 50);
45946     }
45947 });
45948 /*
45949  * Based on:
45950  * Ext JS Library 1.1.1
45951  * Copyright(c) 2006-2007, Ext JS, LLC.
45952  *
45953  * Originally Released Under LGPL - original licence link has changed is not relivant.
45954  *
45955  * Fork - LGPL
45956  * <script type="text/javascript">
45957  */
45958 // private
45959 // This is a support class used internally by the Grid components
45960 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
45961     this.grid = grid;
45962     this.view = grid.getView();
45963     // split the proxies so they don't interfere with mouse events
45964     this.proxyTop = Roo.DomHelper.append(document.body, {
45965         cls:"col-move-top", html:"&#160;"
45966     }, true);
45967     this.proxyBottom = Roo.DomHelper.append(document.body, {
45968         cls:"col-move-bottom", html:"&#160;"
45969     }, true);
45970     this.proxyTop.hide = this.proxyBottom.hide = function(){
45971         this.setLeftTop(-100,-100);
45972         this.setStyle("visibility", "hidden");
45973     };
45974     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
45975     // temporarily disabled
45976     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
45977     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
45978 };
45979 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
45980     proxyOffsets : [-4, -9],
45981     fly: Roo.Element.fly,
45982
45983     getTargetFromEvent : function(e){
45984         var t = Roo.lib.Event.getTarget(e);
45985         var cindex = this.view.findCellIndex(t);
45986         if(cindex !== false){
45987             return this.view.getHeaderCell(cindex);
45988         }
45989     },
45990
45991     nextVisible : function(h){
45992         var v = this.view, cm = this.grid.colModel;
45993         h = h.nextSibling;
45994         while(h){
45995             if(!cm.isHidden(v.getCellIndex(h))){
45996                 return h;
45997             }
45998             h = h.nextSibling;
45999         }
46000         return null;
46001     },
46002
46003     prevVisible : function(h){
46004         var v = this.view, cm = this.grid.colModel;
46005         h = h.prevSibling;
46006         while(h){
46007             if(!cm.isHidden(v.getCellIndex(h))){
46008                 return h;
46009             }
46010             h = h.prevSibling;
46011         }
46012         return null;
46013     },
46014
46015     positionIndicator : function(h, n, e){
46016         var x = Roo.lib.Event.getPageX(e);
46017         var r = Roo.lib.Dom.getRegion(n.firstChild);
46018         var px, pt, py = r.top + this.proxyOffsets[1];
46019         if((r.right - x) <= (r.right-r.left)/2){
46020             px = r.right+this.view.borderWidth;
46021             pt = "after";
46022         }else{
46023             px = r.left;
46024             pt = "before";
46025         }
46026         var oldIndex = this.view.getCellIndex(h);
46027         var newIndex = this.view.getCellIndex(n);
46028
46029         if(this.grid.colModel.isFixed(newIndex)){
46030             return false;
46031         }
46032
46033         var locked = this.grid.colModel.isLocked(newIndex);
46034
46035         if(pt == "after"){
46036             newIndex++;
46037         }
46038         if(oldIndex < newIndex){
46039             newIndex--;
46040         }
46041         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46042             return false;
46043         }
46044         px +=  this.proxyOffsets[0];
46045         this.proxyTop.setLeftTop(px, py);
46046         this.proxyTop.show();
46047         if(!this.bottomOffset){
46048             this.bottomOffset = this.view.mainHd.getHeight();
46049         }
46050         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46051         this.proxyBottom.show();
46052         return pt;
46053     },
46054
46055     onNodeEnter : function(n, dd, e, data){
46056         if(data.header != n){
46057             this.positionIndicator(data.header, n, e);
46058         }
46059     },
46060
46061     onNodeOver : function(n, dd, e, data){
46062         var result = false;
46063         if(data.header != n){
46064             result = this.positionIndicator(data.header, n, e);
46065         }
46066         if(!result){
46067             this.proxyTop.hide();
46068             this.proxyBottom.hide();
46069         }
46070         return result ? this.dropAllowed : this.dropNotAllowed;
46071     },
46072
46073     onNodeOut : function(n, dd, e, data){
46074         this.proxyTop.hide();
46075         this.proxyBottom.hide();
46076     },
46077
46078     onNodeDrop : function(n, dd, e, data){
46079         var h = data.header;
46080         if(h != n){
46081             var cm = this.grid.colModel;
46082             var x = Roo.lib.Event.getPageX(e);
46083             var r = Roo.lib.Dom.getRegion(n.firstChild);
46084             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46085             var oldIndex = this.view.getCellIndex(h);
46086             var newIndex = this.view.getCellIndex(n);
46087             var locked = cm.isLocked(newIndex);
46088             if(pt == "after"){
46089                 newIndex++;
46090             }
46091             if(oldIndex < newIndex){
46092                 newIndex--;
46093             }
46094             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46095                 return false;
46096             }
46097             cm.setLocked(oldIndex, locked, true);
46098             cm.moveColumn(oldIndex, newIndex);
46099             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46100             return true;
46101         }
46102         return false;
46103     }
46104 });
46105 /*
46106  * Based on:
46107  * Ext JS Library 1.1.1
46108  * Copyright(c) 2006-2007, Ext JS, LLC.
46109  *
46110  * Originally Released Under LGPL - original licence link has changed is not relivant.
46111  *
46112  * Fork - LGPL
46113  * <script type="text/javascript">
46114  */
46115   
46116 /**
46117  * @class Roo.grid.GridView
46118  * @extends Roo.util.Observable
46119  *
46120  * @constructor
46121  * @param {Object} config
46122  */
46123 Roo.grid.GridView = function(config){
46124     Roo.grid.GridView.superclass.constructor.call(this);
46125     this.el = null;
46126
46127     Roo.apply(this, config);
46128 };
46129
46130 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46131
46132     /**
46133      * Override this function to apply custom css classes to rows during rendering
46134      * @param {Record} record The record
46135      * @param {Number} index
46136      * @method getRowClass
46137      */
46138     rowClass : "x-grid-row",
46139
46140     cellClass : "x-grid-col",
46141
46142     tdClass : "x-grid-td",
46143
46144     hdClass : "x-grid-hd",
46145
46146     splitClass : "x-grid-split",
46147
46148     sortClasses : ["sort-asc", "sort-desc"],
46149
46150     enableMoveAnim : false,
46151
46152     hlColor: "C3DAF9",
46153
46154     dh : Roo.DomHelper,
46155
46156     fly : Roo.Element.fly,
46157
46158     css : Roo.util.CSS,
46159
46160     borderWidth: 1,
46161
46162     splitOffset: 3,
46163
46164     scrollIncrement : 22,
46165
46166     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46167
46168     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46169
46170     bind : function(ds, cm){
46171         if(this.ds){
46172             this.ds.un("load", this.onLoad, this);
46173             this.ds.un("datachanged", this.onDataChange, this);
46174             this.ds.un("add", this.onAdd, this);
46175             this.ds.un("remove", this.onRemove, this);
46176             this.ds.un("update", this.onUpdate, this);
46177             this.ds.un("clear", this.onClear, this);
46178         }
46179         if(ds){
46180             ds.on("load", this.onLoad, this);
46181             ds.on("datachanged", this.onDataChange, this);
46182             ds.on("add", this.onAdd, this);
46183             ds.on("remove", this.onRemove, this);
46184             ds.on("update", this.onUpdate, this);
46185             ds.on("clear", this.onClear, this);
46186         }
46187         this.ds = ds;
46188
46189         if(this.cm){
46190             this.cm.un("widthchange", this.onColWidthChange, this);
46191             this.cm.un("headerchange", this.onHeaderChange, this);
46192             this.cm.un("hiddenchange", this.onHiddenChange, this);
46193             this.cm.un("columnmoved", this.onColumnMove, this);
46194             this.cm.un("columnlockchange", this.onColumnLock, this);
46195         }
46196         if(cm){
46197             this.generateRules(cm);
46198             cm.on("widthchange", this.onColWidthChange, this);
46199             cm.on("headerchange", this.onHeaderChange, this);
46200             cm.on("hiddenchange", this.onHiddenChange, this);
46201             cm.on("columnmoved", this.onColumnMove, this);
46202             cm.on("columnlockchange", this.onColumnLock, this);
46203         }
46204         this.cm = cm;
46205     },
46206
46207     init: function(grid){
46208                 Roo.grid.GridView.superclass.init.call(this, grid);
46209
46210                 this.bind(grid.dataSource, grid.colModel);
46211
46212             grid.on("headerclick", this.handleHeaderClick, this);
46213
46214         if(grid.trackMouseOver){
46215             grid.on("mouseover", this.onRowOver, this);
46216                 grid.on("mouseout", this.onRowOut, this);
46217             }
46218             grid.cancelTextSelection = function(){};
46219                 this.gridId = grid.id;
46220
46221                 var tpls = this.templates || {};
46222
46223                 if(!tpls.master){
46224                     tpls.master = new Roo.Template(
46225                        '<div class="x-grid" hidefocus="true">',
46226                           '<div class="x-grid-topbar"></div>',
46227                           '<div class="x-grid-scroller"><div></div></div>',
46228                           '<div class="x-grid-locked">',
46229                               '<div class="x-grid-header">{lockedHeader}</div>',
46230                               '<div class="x-grid-body">{lockedBody}</div>',
46231                           "</div>",
46232                           '<div class="x-grid-viewport">',
46233                               '<div class="x-grid-header">{header}</div>',
46234                               '<div class="x-grid-body">{body}</div>',
46235                           "</div>",
46236                           '<div class="x-grid-bottombar"></div>',
46237                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46238                           '<div class="x-grid-resize-proxy">&#160;</div>',
46239                        "</div>"
46240                     );
46241                     tpls.master.disableformats = true;
46242                 }
46243
46244                 if(!tpls.header){
46245                     tpls.header = new Roo.Template(
46246                        '<table border="0" cellspacing="0" cellpadding="0">',
46247                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46248                        "</table>{splits}"
46249                     );
46250                     tpls.header.disableformats = true;
46251                 }
46252                 tpls.header.compile();
46253
46254                 if(!tpls.hcell){
46255                     tpls.hcell = new Roo.Template(
46256                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46257                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46258                         "</div></td>"
46259                      );
46260                      tpls.hcell.disableFormats = true;
46261                 }
46262                 tpls.hcell.compile();
46263
46264                 if(!tpls.hsplit){
46265                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46266                     tpls.hsplit.disableFormats = true;
46267                 }
46268                 tpls.hsplit.compile();
46269
46270                 if(!tpls.body){
46271                     tpls.body = new Roo.Template(
46272                        '<table border="0" cellspacing="0" cellpadding="0">',
46273                        "<tbody>{rows}</tbody>",
46274                        "</table>"
46275                     );
46276                     tpls.body.disableFormats = true;
46277                 }
46278                 tpls.body.compile();
46279
46280                 if(!tpls.row){
46281                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46282                     tpls.row.disableFormats = true;
46283                 }
46284                 tpls.row.compile();
46285
46286                 if(!tpls.cell){
46287                     tpls.cell = new Roo.Template(
46288                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46289                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46290                         "</td>"
46291                     );
46292             tpls.cell.disableFormats = true;
46293         }
46294                 tpls.cell.compile();
46295
46296                 this.templates = tpls;
46297         },
46298
46299         // remap these for backwards compat
46300     onColWidthChange : function(){
46301         this.updateColumns.apply(this, arguments);
46302     },
46303     onHeaderChange : function(){
46304         this.updateHeaders.apply(this, arguments);
46305     }, 
46306     onHiddenChange : function(){
46307         this.handleHiddenChange.apply(this, arguments);
46308     },
46309     onColumnMove : function(){
46310         this.handleColumnMove.apply(this, arguments);
46311     },
46312     onColumnLock : function(){
46313         this.handleLockChange.apply(this, arguments);
46314     },
46315
46316     onDataChange : function(){
46317         this.refresh();
46318         this.updateHeaderSortState();
46319     },
46320
46321         onClear : function(){
46322         this.refresh();
46323     },
46324
46325         onUpdate : function(ds, record){
46326         this.refreshRow(record);
46327     },
46328
46329     refreshRow : function(record){
46330         var ds = this.ds, index;
46331         if(typeof record == 'number'){
46332             index = record;
46333             record = ds.getAt(index);
46334         }else{
46335             index = ds.indexOf(record);
46336         }
46337         this.insertRows(ds, index, index, true);
46338         this.onRemove(ds, record, index+1, true);
46339         this.syncRowHeights(index, index);
46340         this.layout();
46341         this.fireEvent("rowupdated", this, index, record);
46342     },
46343
46344     onAdd : function(ds, records, index){
46345         this.insertRows(ds, index, index + (records.length-1));
46346     },
46347
46348     onRemove : function(ds, record, index, isUpdate){
46349         if(isUpdate !== true){
46350             this.fireEvent("beforerowremoved", this, index, record);
46351         }
46352         var bt = this.getBodyTable(), lt = this.getLockedTable();
46353         if(bt.rows[index]){
46354             bt.firstChild.removeChild(bt.rows[index]);
46355         }
46356         if(lt.rows[index]){
46357             lt.firstChild.removeChild(lt.rows[index]);
46358         }
46359         if(isUpdate !== true){
46360             this.stripeRows(index);
46361             this.syncRowHeights(index, index);
46362             this.layout();
46363             this.fireEvent("rowremoved", this, index, record);
46364         }
46365     },
46366
46367     onLoad : function(){
46368         this.scrollToTop();
46369     },
46370
46371     /**
46372      * Scrolls the grid to the top
46373      */
46374     scrollToTop : function(){
46375         if(this.scroller){
46376             this.scroller.dom.scrollTop = 0;
46377             this.syncScroll();
46378         }
46379     },
46380
46381     /**
46382      * Gets a panel in the header of the grid that can be used for toolbars etc.
46383      * After modifying the contents of this panel a call to grid.autoSize() may be
46384      * required to register any changes in size.
46385      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46386      * @return Roo.Element
46387      */
46388     getHeaderPanel : function(doShow){
46389         if(doShow){
46390             this.headerPanel.show();
46391         }
46392         return this.headerPanel;
46393         },
46394
46395         /**
46396      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46397      * After modifying the contents of this panel a call to grid.autoSize() may be
46398      * required to register any changes in size.
46399      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46400      * @return Roo.Element
46401      */
46402     getFooterPanel : function(doShow){
46403         if(doShow){
46404             this.footerPanel.show();
46405         }
46406         return this.footerPanel;
46407         },
46408
46409         initElements : function(){
46410             var E = Roo.Element;
46411             var el = this.grid.getGridEl().dom.firstChild;
46412             var cs = el.childNodes;
46413
46414             this.el = new E(el);
46415             this.headerPanel = new E(el.firstChild);
46416             this.headerPanel.enableDisplayMode("block");
46417
46418         this.scroller = new E(cs[1]);
46419             this.scrollSizer = new E(this.scroller.dom.firstChild);
46420
46421             this.lockedWrap = new E(cs[2]);
46422             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46423             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46424
46425             this.mainWrap = new E(cs[3]);
46426             this.mainHd = new E(this.mainWrap.dom.firstChild);
46427             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46428
46429             this.footerPanel = new E(cs[4]);
46430             this.footerPanel.enableDisplayMode("block");
46431
46432         this.focusEl = new E(cs[5]);
46433         this.focusEl.swallowEvent("click", true);
46434         this.resizeProxy = new E(cs[6]);
46435
46436             this.headerSelector = String.format(
46437                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46438                this.lockedHd.id, this.mainHd.id
46439             );
46440
46441             this.splitterSelector = String.format(
46442                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46443                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46444             );
46445     },
46446     idToCssName : function(s)
46447     {
46448         return s.replace(/[^a-z0-9]+/ig, '-');
46449     },
46450
46451         getHeaderCell : function(index){
46452             return Roo.DomQuery.select(this.headerSelector)[index];
46453         },
46454
46455         getHeaderCellMeasure : function(index){
46456             return this.getHeaderCell(index).firstChild;
46457         },
46458
46459         getHeaderCellText : function(index){
46460             return this.getHeaderCell(index).firstChild.firstChild;
46461         },
46462
46463         getLockedTable : function(){
46464             return this.lockedBody.dom.firstChild;
46465         },
46466
46467         getBodyTable : function(){
46468             return this.mainBody.dom.firstChild;
46469         },
46470
46471         getLockedRow : function(index){
46472             return this.getLockedTable().rows[index];
46473         },
46474
46475         getRow : function(index){
46476             return this.getBodyTable().rows[index];
46477         },
46478
46479         getRowComposite : function(index){
46480             if(!this.rowEl){
46481                 this.rowEl = new Roo.CompositeElementLite();
46482             }
46483         var els = [], lrow, mrow;
46484         if(lrow = this.getLockedRow(index)){
46485             els.push(lrow);
46486         }
46487         if(mrow = this.getRow(index)){
46488             els.push(mrow);
46489         }
46490         this.rowEl.elements = els;
46491             return this.rowEl;
46492         },
46493
46494         getCell : function(rowIndex, colIndex){
46495             var locked = this.cm.getLockedCount();
46496             var source;
46497             if(colIndex < locked){
46498                 source = this.lockedBody.dom.firstChild;
46499             }else{
46500                 source = this.mainBody.dom.firstChild;
46501                 colIndex -= locked;
46502             }
46503         return source.rows[rowIndex].childNodes[colIndex];
46504         },
46505
46506         getCellText : function(rowIndex, colIndex){
46507             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46508         },
46509
46510         getCellBox : function(cell){
46511             var b = this.fly(cell).getBox();
46512         if(Roo.isOpera){ // opera fails to report the Y
46513             b.y = cell.offsetTop + this.mainBody.getY();
46514         }
46515         return b;
46516     },
46517
46518     getCellIndex : function(cell){
46519         var id = String(cell.className).match(this.cellRE);
46520         if(id){
46521             return parseInt(id[1], 10);
46522         }
46523         return 0;
46524     },
46525
46526     findHeaderIndex : function(n){
46527         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46528         return r ? this.getCellIndex(r) : false;
46529     },
46530
46531     findHeaderCell : function(n){
46532         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46533         return r ? r : false;
46534     },
46535
46536     findRowIndex : function(n){
46537         if(!n){
46538             return false;
46539         }
46540         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46541         return r ? r.rowIndex : false;
46542     },
46543
46544     findCellIndex : function(node){
46545         var stop = this.el.dom;
46546         while(node && node != stop){
46547             if(this.findRE.test(node.className)){
46548                 return this.getCellIndex(node);
46549             }
46550             node = node.parentNode;
46551         }
46552         return false;
46553     },
46554
46555     getColumnId : function(index){
46556             return this.cm.getColumnId(index);
46557         },
46558
46559         getSplitters : function(){
46560             if(this.splitterSelector){
46561                return Roo.DomQuery.select(this.splitterSelector);
46562             }else{
46563                 return null;
46564             }
46565         },
46566
46567         getSplitter : function(index){
46568             return this.getSplitters()[index];
46569         },
46570
46571     onRowOver : function(e, t){
46572         var row;
46573         if((row = this.findRowIndex(t)) !== false){
46574             this.getRowComposite(row).addClass("x-grid-row-over");
46575         }
46576     },
46577
46578     onRowOut : function(e, t){
46579         var row;
46580         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46581             this.getRowComposite(row).removeClass("x-grid-row-over");
46582         }
46583     },
46584
46585     renderHeaders : function(){
46586             var cm = this.cm;
46587         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46588         var cb = [], lb = [], sb = [], lsb = [], p = {};
46589         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46590             p.cellId = "x-grid-hd-0-" + i;
46591             p.splitId = "x-grid-csplit-0-" + i;
46592             p.id = cm.getColumnId(i);
46593             p.title = cm.getColumnTooltip(i) || "";
46594             p.value = cm.getColumnHeader(i) || "";
46595             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46596             if(!cm.isLocked(i)){
46597                 cb[cb.length] = ct.apply(p);
46598                 sb[sb.length] = st.apply(p);
46599             }else{
46600                 lb[lb.length] = ct.apply(p);
46601                 lsb[lsb.length] = st.apply(p);
46602             }
46603         }
46604         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46605                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46606         },
46607
46608         updateHeaders : function(){
46609         var html = this.renderHeaders();
46610         this.lockedHd.update(html[0]);
46611         this.mainHd.update(html[1]);
46612     },
46613
46614     /**
46615      * Focuses the specified row.
46616      * @param {Number} row The row index
46617      */
46618     focusRow : function(row){
46619         var x = this.scroller.dom.scrollLeft;
46620         this.focusCell(row, 0, false);
46621         this.scroller.dom.scrollLeft = x;
46622     },
46623
46624     /**
46625      * Focuses the specified cell.
46626      * @param {Number} row The row index
46627      * @param {Number} col The column index
46628      * @param {Boolean} hscroll false to disable horizontal scrolling
46629      */
46630     focusCell : function(row, col, hscroll){
46631         var el = this.ensureVisible(row, col, hscroll);
46632         this.focusEl.alignTo(el, "tl-tl");
46633         if(Roo.isGecko){
46634             this.focusEl.focus();
46635         }else{
46636             this.focusEl.focus.defer(1, this.focusEl);
46637         }
46638     },
46639
46640     /**
46641      * Scrolls the specified cell into view
46642      * @param {Number} row The row index
46643      * @param {Number} col The column index
46644      * @param {Boolean} hscroll false to disable horizontal scrolling
46645      */
46646     ensureVisible : function(row, col, hscroll){
46647         if(typeof row != "number"){
46648             row = row.rowIndex;
46649         }
46650         if(row < 0 && row >= this.ds.getCount()){
46651             return;
46652         }
46653         col = (col !== undefined ? col : 0);
46654         var cm = this.grid.colModel;
46655         while(cm.isHidden(col)){
46656             col++;
46657         }
46658
46659         var el = this.getCell(row, col);
46660         if(!el){
46661             return;
46662         }
46663         var c = this.scroller.dom;
46664
46665         var ctop = parseInt(el.offsetTop, 10);
46666         var cleft = parseInt(el.offsetLeft, 10);
46667         var cbot = ctop + el.offsetHeight;
46668         var cright = cleft + el.offsetWidth;
46669
46670         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46671         var stop = parseInt(c.scrollTop, 10);
46672         var sleft = parseInt(c.scrollLeft, 10);
46673         var sbot = stop + ch;
46674         var sright = sleft + c.clientWidth;
46675
46676         if(ctop < stop){
46677                 c.scrollTop = ctop;
46678         }else if(cbot > sbot){
46679             c.scrollTop = cbot-ch;
46680         }
46681
46682         if(hscroll !== false){
46683             if(cleft < sleft){
46684                 c.scrollLeft = cleft;
46685             }else if(cright > sright){
46686                 c.scrollLeft = cright-c.clientWidth;
46687             }
46688         }
46689         return el;
46690     },
46691
46692     updateColumns : function(){
46693         this.grid.stopEditing();
46694         var cm = this.grid.colModel, colIds = this.getColumnIds();
46695         //var totalWidth = cm.getTotalWidth();
46696         var pos = 0;
46697         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46698             //if(cm.isHidden(i)) continue;
46699             var w = cm.getColumnWidth(i);
46700             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46701             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46702         }
46703         this.updateSplitters();
46704     },
46705
46706     generateRules : function(cm){
46707         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46708         Roo.util.CSS.removeStyleSheet(rulesId);
46709         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46710             var cid = cm.getColumnId(i);
46711             var align = '';
46712             if(cm.config[i].align){
46713                 align = 'text-align:'+cm.config[i].align+';';
46714             }
46715             var hidden = '';
46716             if(cm.isHidden(i)){
46717                 hidden = 'display:none;';
46718             }
46719             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46720             ruleBuf.push(
46721                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46722                     this.hdSelector, cid, " {\n", align, width, "}\n",
46723                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46724                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46725         }
46726         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46727     },
46728
46729     updateSplitters : function(){
46730         var cm = this.cm, s = this.getSplitters();
46731         if(s){ // splitters not created yet
46732             var pos = 0, locked = true;
46733             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46734                 if(cm.isHidden(i)) continue;
46735                 var w = cm.getColumnWidth(i);
46736                 if(!cm.isLocked(i) && locked){
46737                     pos = 0;
46738                     locked = false;
46739                 }
46740                 pos += w;
46741                 s[i].style.left = (pos-this.splitOffset) + "px";
46742             }
46743         }
46744     },
46745
46746     handleHiddenChange : function(colModel, colIndex, hidden){
46747         if(hidden){
46748             this.hideColumn(colIndex);
46749         }else{
46750             this.unhideColumn(colIndex);
46751         }
46752     },
46753
46754     hideColumn : function(colIndex){
46755         var cid = this.getColumnId(colIndex);
46756         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46757         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46758         if(Roo.isSafari){
46759             this.updateHeaders();
46760         }
46761         this.updateSplitters();
46762         this.layout();
46763     },
46764
46765     unhideColumn : function(colIndex){
46766         var cid = this.getColumnId(colIndex);
46767         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46768         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46769
46770         if(Roo.isSafari){
46771             this.updateHeaders();
46772         }
46773         this.updateSplitters();
46774         this.layout();
46775     },
46776
46777     insertRows : function(dm, firstRow, lastRow, isUpdate){
46778         if(firstRow == 0 && lastRow == dm.getCount()-1){
46779             this.refresh();
46780         }else{
46781             if(!isUpdate){
46782                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46783             }
46784             var s = this.getScrollState();
46785             var markup = this.renderRows(firstRow, lastRow);
46786             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46787             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46788             this.restoreScroll(s);
46789             if(!isUpdate){
46790                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46791                 this.syncRowHeights(firstRow, lastRow);
46792                 this.stripeRows(firstRow);
46793                 this.layout();
46794             }
46795         }
46796     },
46797
46798     bufferRows : function(markup, target, index){
46799         var before = null, trows = target.rows, tbody = target.tBodies[0];
46800         if(index < trows.length){
46801             before = trows[index];
46802         }
46803         var b = document.createElement("div");
46804         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46805         var rows = b.firstChild.rows;
46806         for(var i = 0, len = rows.length; i < len; i++){
46807             if(before){
46808                 tbody.insertBefore(rows[0], before);
46809             }else{
46810                 tbody.appendChild(rows[0]);
46811             }
46812         }
46813         b.innerHTML = "";
46814         b = null;
46815     },
46816
46817     deleteRows : function(dm, firstRow, lastRow){
46818         if(dm.getRowCount()<1){
46819             this.fireEvent("beforerefresh", this);
46820             this.mainBody.update("");
46821             this.lockedBody.update("");
46822             this.fireEvent("refresh", this);
46823         }else{
46824             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46825             var bt = this.getBodyTable();
46826             var tbody = bt.firstChild;
46827             var rows = bt.rows;
46828             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46829                 tbody.removeChild(rows[firstRow]);
46830             }
46831             this.stripeRows(firstRow);
46832             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46833         }
46834     },
46835
46836     updateRows : function(dataSource, firstRow, lastRow){
46837         var s = this.getScrollState();
46838         this.refresh();
46839         this.restoreScroll(s);
46840     },
46841
46842     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46843         if(!noRefresh){
46844            this.refresh();
46845         }
46846         this.updateHeaderSortState();
46847     },
46848
46849     getScrollState : function(){
46850         var sb = this.scroller.dom;
46851         return {left: sb.scrollLeft, top: sb.scrollTop};
46852     },
46853
46854     stripeRows : function(startRow){
46855         if(!this.grid.stripeRows || this.ds.getCount() < 1){
46856             return;
46857         }
46858         startRow = startRow || 0;
46859         var rows = this.getBodyTable().rows;
46860         var lrows = this.getLockedTable().rows;
46861         var cls = ' x-grid-row-alt ';
46862         for(var i = startRow, len = rows.length; i < len; i++){
46863             var row = rows[i], lrow = lrows[i];
46864             var isAlt = ((i+1) % 2 == 0);
46865             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
46866             if(isAlt == hasAlt){
46867                 continue;
46868             }
46869             if(isAlt){
46870                 row.className += " x-grid-row-alt";
46871             }else{
46872                 row.className = row.className.replace("x-grid-row-alt", "");
46873             }
46874             if(lrow){
46875                 lrow.className = row.className;
46876             }
46877         }
46878     },
46879
46880     restoreScroll : function(state){
46881         var sb = this.scroller.dom;
46882         sb.scrollLeft = state.left;
46883         sb.scrollTop = state.top;
46884         this.syncScroll();
46885     },
46886
46887     syncScroll : function(){
46888         var sb = this.scroller.dom;
46889         var sh = this.mainHd.dom;
46890         var bs = this.mainBody.dom;
46891         var lv = this.lockedBody.dom;
46892         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
46893         lv.scrollTop = bs.scrollTop = sb.scrollTop;
46894     },
46895
46896     handleScroll : function(e){
46897         this.syncScroll();
46898         var sb = this.scroller.dom;
46899         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
46900         e.stopEvent();
46901     },
46902
46903     handleWheel : function(e){
46904         var d = e.getWheelDelta();
46905         this.scroller.dom.scrollTop -= d*22;
46906         // set this here to prevent jumpy scrolling on large tables
46907         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
46908         e.stopEvent();
46909     },
46910
46911     renderRows : function(startRow, endRow){
46912         // pull in all the crap needed to render rows
46913         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
46914         var colCount = cm.getColumnCount();
46915
46916         if(ds.getCount() < 1){
46917             return ["", ""];
46918         }
46919
46920         // build a map for all the columns
46921         var cs = [];
46922         for(var i = 0; i < colCount; i++){
46923             var name = cm.getDataIndex(i);
46924             cs[i] = {
46925                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
46926                 renderer : cm.getRenderer(i),
46927                 id : cm.getColumnId(i),
46928                 locked : cm.isLocked(i)
46929             };
46930         }
46931
46932         startRow = startRow || 0;
46933         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
46934
46935         // records to render
46936         var rs = ds.getRange(startRow, endRow);
46937
46938         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
46939     },
46940
46941     // As much as I hate to duplicate code, this was branched because FireFox really hates
46942     // [].join("") on strings. The performance difference was substantial enough to
46943     // branch this function
46944     doRender : Roo.isGecko ?
46945             function(cs, rs, ds, startRow, colCount, stripe){
46946                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46947                 // buffers
46948                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46949                 for(var j = 0, len = rs.length; j < len; j++){
46950                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
46951                     for(var i = 0; i < colCount; i++){
46952                         c = cs[i];
46953                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46954                         p.id = c.id;
46955                         p.css = p.attr = "";
46956                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46957                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46958                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
46959                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
46960                         }
46961                         var markup = ct.apply(p);
46962                         if(!c.locked){
46963                             cb+= markup;
46964                         }else{
46965                             lcb+= markup;
46966                         }
46967                     }
46968                     var alt = [];
46969                     if(stripe && ((rowIndex+1) % 2 == 0)){
46970                         alt[0] = "x-grid-row-alt";
46971                     }
46972                     if(r.dirty){
46973                         alt[1] = " x-grid-dirty-row";
46974                     }
46975                     rp.cells = lcb;
46976                     if(this.getRowClass){
46977                         alt[2] = this.getRowClass(r, rowIndex);
46978                     }
46979                     rp.alt = alt.join(" ");
46980                     lbuf+= rt.apply(rp);
46981                     rp.cells = cb;
46982                     buf+=  rt.apply(rp);
46983                 }
46984                 return [lbuf, buf];
46985             } :
46986             function(cs, rs, ds, startRow, colCount, stripe){
46987                 var ts = this.templates, ct = ts.cell, rt = ts.row;
46988                 // buffers
46989                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
46990                 for(var j = 0, len = rs.length; j < len; j++){
46991                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
46992                     for(var i = 0; i < colCount; i++){
46993                         c = cs[i];
46994                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
46995                         p.id = c.id;
46996                         p.css = p.attr = "";
46997                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
46998                         if(p.value == undefined || p.value === "") p.value = "&#160;";
46999                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47000                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47001                         }
47002                         var markup = ct.apply(p);
47003                         if(!c.locked){
47004                             cb[cb.length] = markup;
47005                         }else{
47006                             lcb[lcb.length] = markup;
47007                         }
47008                     }
47009                     var alt = [];
47010                     if(stripe && ((rowIndex+1) % 2 == 0)){
47011                         alt[0] = "x-grid-row-alt";
47012                     }
47013                     if(r.dirty){
47014                         alt[1] = " x-grid-dirty-row";
47015                     }
47016                     rp.cells = lcb;
47017                     if(this.getRowClass){
47018                         alt[2] = this.getRowClass(r, rowIndex);
47019                     }
47020                     rp.alt = alt.join(" ");
47021                     rp.cells = lcb.join("");
47022                     lbuf[lbuf.length] = rt.apply(rp);
47023                     rp.cells = cb.join("");
47024                     buf[buf.length] =  rt.apply(rp);
47025                 }
47026                 return [lbuf.join(""), buf.join("")];
47027             },
47028
47029     renderBody : function(){
47030         var markup = this.renderRows();
47031         var bt = this.templates.body;
47032         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47033     },
47034
47035     /**
47036      * Refreshes the grid
47037      * @param {Boolean} headersToo
47038      */
47039     refresh : function(headersToo){
47040         this.fireEvent("beforerefresh", this);
47041         this.grid.stopEditing();
47042         var result = this.renderBody();
47043         this.lockedBody.update(result[0]);
47044         this.mainBody.update(result[1]);
47045         if(headersToo === true){
47046             this.updateHeaders();
47047             this.updateColumns();
47048             this.updateSplitters();
47049             this.updateHeaderSortState();
47050         }
47051         this.syncRowHeights();
47052         this.layout();
47053         this.fireEvent("refresh", this);
47054     },
47055
47056     handleColumnMove : function(cm, oldIndex, newIndex){
47057         this.indexMap = null;
47058         var s = this.getScrollState();
47059         this.refresh(true);
47060         this.restoreScroll(s);
47061         this.afterMove(newIndex);
47062     },
47063
47064     afterMove : function(colIndex){
47065         if(this.enableMoveAnim && Roo.enableFx){
47066             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47067         }
47068     },
47069
47070     updateCell : function(dm, rowIndex, dataIndex){
47071         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47072         if(typeof colIndex == "undefined"){ // not present in grid
47073             return;
47074         }
47075         var cm = this.grid.colModel;
47076         var cell = this.getCell(rowIndex, colIndex);
47077         var cellText = this.getCellText(rowIndex, colIndex);
47078
47079         var p = {
47080             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47081             id : cm.getColumnId(colIndex),
47082             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47083         };
47084         var renderer = cm.getRenderer(colIndex);
47085         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47086         if(typeof val == "undefined" || val === "") val = "&#160;";
47087         cellText.innerHTML = val;
47088         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47089         this.syncRowHeights(rowIndex, rowIndex);
47090     },
47091
47092     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47093         var maxWidth = 0;
47094         if(this.grid.autoSizeHeaders){
47095             var h = this.getHeaderCellMeasure(colIndex);
47096             maxWidth = Math.max(maxWidth, h.scrollWidth);
47097         }
47098         var tb, index;
47099         if(this.cm.isLocked(colIndex)){
47100             tb = this.getLockedTable();
47101             index = colIndex;
47102         }else{
47103             tb = this.getBodyTable();
47104             index = colIndex - this.cm.getLockedCount();
47105         }
47106         if(tb && tb.rows){
47107             var rows = tb.rows;
47108             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47109             for(var i = 0; i < stopIndex; i++){
47110                 var cell = rows[i].childNodes[index].firstChild;
47111                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47112             }
47113         }
47114         return maxWidth + /*margin for error in IE*/ 5;
47115     },
47116     /**
47117      * Autofit a column to its content.
47118      * @param {Number} colIndex
47119      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47120      */
47121      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47122          if(this.cm.isHidden(colIndex)){
47123              return; // can't calc a hidden column
47124          }
47125         if(forceMinSize){
47126             var cid = this.cm.getColumnId(colIndex);
47127             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47128            if(this.grid.autoSizeHeaders){
47129                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47130            }
47131         }
47132         var newWidth = this.calcColumnWidth(colIndex);
47133         this.cm.setColumnWidth(colIndex,
47134             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47135         if(!suppressEvent){
47136             this.grid.fireEvent("columnresize", colIndex, newWidth);
47137         }
47138     },
47139
47140     /**
47141      * Autofits all columns to their content and then expands to fit any extra space in the grid
47142      */
47143      autoSizeColumns : function(){
47144         var cm = this.grid.colModel;
47145         var colCount = cm.getColumnCount();
47146         for(var i = 0; i < colCount; i++){
47147             this.autoSizeColumn(i, true, true);
47148         }
47149         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47150             this.fitColumns();
47151         }else{
47152             this.updateColumns();
47153             this.layout();
47154         }
47155     },
47156
47157     /**
47158      * Autofits all columns to the grid's width proportionate with their current size
47159      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47160      */
47161     fitColumns : function(reserveScrollSpace){
47162         var cm = this.grid.colModel;
47163         var colCount = cm.getColumnCount();
47164         var cols = [];
47165         var width = 0;
47166         var i, w;
47167         for (i = 0; i < colCount; i++){
47168             if(!cm.isHidden(i) && !cm.isFixed(i)){
47169                 w = cm.getColumnWidth(i);
47170                 cols.push(i);
47171                 cols.push(w);
47172                 width += w;
47173             }
47174         }
47175         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47176         if(reserveScrollSpace){
47177             avail -= 17;
47178         }
47179         var frac = (avail - cm.getTotalWidth())/width;
47180         while (cols.length){
47181             w = cols.pop();
47182             i = cols.pop();
47183             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47184         }
47185         this.updateColumns();
47186         this.layout();
47187     },
47188
47189     onRowSelect : function(rowIndex){
47190         var row = this.getRowComposite(rowIndex);
47191         row.addClass("x-grid-row-selected");
47192     },
47193
47194     onRowDeselect : function(rowIndex){
47195         var row = this.getRowComposite(rowIndex);
47196         row.removeClass("x-grid-row-selected");
47197     },
47198
47199     onCellSelect : function(row, col){
47200         var cell = this.getCell(row, col);
47201         if(cell){
47202             Roo.fly(cell).addClass("x-grid-cell-selected");
47203         }
47204     },
47205
47206     onCellDeselect : function(row, col){
47207         var cell = this.getCell(row, col);
47208         if(cell){
47209             Roo.fly(cell).removeClass("x-grid-cell-selected");
47210         }
47211     },
47212
47213     updateHeaderSortState : function(){
47214         var state = this.ds.getSortState();
47215         if(!state){
47216             return;
47217         }
47218         this.sortState = state;
47219         var sortColumn = this.cm.findColumnIndex(state.field);
47220         if(sortColumn != -1){
47221             var sortDir = state.direction;
47222             var sc = this.sortClasses;
47223             var hds = this.el.select(this.headerSelector).removeClass(sc);
47224             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47225         }
47226     },
47227
47228     handleHeaderClick : function(g, index){
47229         if(this.headersDisabled){
47230             return;
47231         }
47232         var dm = g.dataSource, cm = g.colModel;
47233             if(!cm.isSortable(index)){
47234             return;
47235         }
47236             g.stopEditing();
47237         dm.sort(cm.getDataIndex(index));
47238     },
47239
47240
47241     destroy : function(){
47242         if(this.colMenu){
47243             this.colMenu.removeAll();
47244             Roo.menu.MenuMgr.unregister(this.colMenu);
47245             this.colMenu.getEl().remove();
47246             delete this.colMenu;
47247         }
47248         if(this.hmenu){
47249             this.hmenu.removeAll();
47250             Roo.menu.MenuMgr.unregister(this.hmenu);
47251             this.hmenu.getEl().remove();
47252             delete this.hmenu;
47253         }
47254         if(this.grid.enableColumnMove){
47255             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47256             if(dds){
47257                 for(var dd in dds){
47258                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47259                         var elid = dds[dd].dragElId;
47260                         dds[dd].unreg();
47261                         Roo.get(elid).remove();
47262                     } else if(dds[dd].config.isTarget){
47263                         dds[dd].proxyTop.remove();
47264                         dds[dd].proxyBottom.remove();
47265                         dds[dd].unreg();
47266                     }
47267                     if(Roo.dd.DDM.locationCache[dd]){
47268                         delete Roo.dd.DDM.locationCache[dd];
47269                     }
47270                 }
47271                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47272             }
47273         }
47274         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47275         this.bind(null, null);
47276         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47277     },
47278
47279     handleLockChange : function(){
47280         this.refresh(true);
47281     },
47282
47283     onDenyColumnLock : function(){
47284
47285     },
47286
47287     onDenyColumnHide : function(){
47288
47289     },
47290
47291     handleHdMenuClick : function(item){
47292         var index = this.hdCtxIndex;
47293         var cm = this.cm, ds = this.ds;
47294         switch(item.id){
47295             case "asc":
47296                 ds.sort(cm.getDataIndex(index), "ASC");
47297                 break;
47298             case "desc":
47299                 ds.sort(cm.getDataIndex(index), "DESC");
47300                 break;
47301             case "lock":
47302                 var lc = cm.getLockedCount();
47303                 if(cm.getColumnCount(true) <= lc+1){
47304                     this.onDenyColumnLock();
47305                     return;
47306                 }
47307                 if(lc != index){
47308                     cm.setLocked(index, true, true);
47309                     cm.moveColumn(index, lc);
47310                     this.grid.fireEvent("columnmove", index, lc);
47311                 }else{
47312                     cm.setLocked(index, true);
47313                 }
47314             break;
47315             case "unlock":
47316                 var lc = cm.getLockedCount();
47317                 if((lc-1) != index){
47318                     cm.setLocked(index, false, true);
47319                     cm.moveColumn(index, lc-1);
47320                     this.grid.fireEvent("columnmove", index, lc-1);
47321                 }else{
47322                     cm.setLocked(index, false);
47323                 }
47324             break;
47325             default:
47326                 index = cm.getIndexById(item.id.substr(4));
47327                 if(index != -1){
47328                     if(item.checked && cm.getColumnCount(true) <= 1){
47329                         this.onDenyColumnHide();
47330                         return false;
47331                     }
47332                     cm.setHidden(index, item.checked);
47333                 }
47334         }
47335         return true;
47336     },
47337
47338     beforeColMenuShow : function(){
47339         var cm = this.cm,  colCount = cm.getColumnCount();
47340         this.colMenu.removeAll();
47341         for(var i = 0; i < colCount; i++){
47342             this.colMenu.add(new Roo.menu.CheckItem({
47343                 id: "col-"+cm.getColumnId(i),
47344                 text: cm.getColumnHeader(i),
47345                 checked: !cm.isHidden(i),
47346                 hideOnClick:false
47347             }));
47348         }
47349     },
47350
47351     handleHdCtx : function(g, index, e){
47352         e.stopEvent();
47353         var hd = this.getHeaderCell(index);
47354         this.hdCtxIndex = index;
47355         var ms = this.hmenu.items, cm = this.cm;
47356         ms.get("asc").setDisabled(!cm.isSortable(index));
47357         ms.get("desc").setDisabled(!cm.isSortable(index));
47358         if(this.grid.enableColLock !== false){
47359             ms.get("lock").setDisabled(cm.isLocked(index));
47360             ms.get("unlock").setDisabled(!cm.isLocked(index));
47361         }
47362         this.hmenu.show(hd, "tl-bl");
47363     },
47364
47365     handleHdOver : function(e){
47366         var hd = this.findHeaderCell(e.getTarget());
47367         if(hd && !this.headersDisabled){
47368             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47369                this.fly(hd).addClass("x-grid-hd-over");
47370             }
47371         }
47372     },
47373
47374     handleHdOut : function(e){
47375         var hd = this.findHeaderCell(e.getTarget());
47376         if(hd){
47377             this.fly(hd).removeClass("x-grid-hd-over");
47378         }
47379     },
47380
47381     handleSplitDblClick : function(e, t){
47382         var i = this.getCellIndex(t);
47383         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47384             this.autoSizeColumn(i, true);
47385             this.layout();
47386         }
47387     },
47388
47389     render : function(){
47390
47391         var cm = this.cm;
47392         var colCount = cm.getColumnCount();
47393
47394         if(this.grid.monitorWindowResize === true){
47395             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47396         }
47397         var header = this.renderHeaders();
47398         var body = this.templates.body.apply({rows:""});
47399         var html = this.templates.master.apply({
47400             lockedBody: body,
47401             body: body,
47402             lockedHeader: header[0],
47403             header: header[1]
47404         });
47405
47406         //this.updateColumns();
47407
47408         this.grid.getGridEl().dom.innerHTML = html;
47409
47410         this.initElements();
47411
47412         this.scroller.on("scroll", this.handleScroll, this);
47413         this.lockedBody.on("mousewheel", this.handleWheel, this);
47414         this.mainBody.on("mousewheel", this.handleWheel, this);
47415
47416         this.mainHd.on("mouseover", this.handleHdOver, this);
47417         this.mainHd.on("mouseout", this.handleHdOut, this);
47418         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47419                 {delegate: "."+this.splitClass});
47420
47421         this.lockedHd.on("mouseover", this.handleHdOver, this);
47422         this.lockedHd.on("mouseout", this.handleHdOut, this);
47423         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47424                 {delegate: "."+this.splitClass});
47425
47426         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47427             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47428         }
47429
47430         this.updateSplitters();
47431
47432         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47433             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47434             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47435         }
47436
47437         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47438             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47439             this.hmenu.add(
47440                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47441                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47442             );
47443             if(this.grid.enableColLock !== false){
47444                 this.hmenu.add('-',
47445                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47446                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47447                 );
47448             }
47449             if(this.grid.enableColumnHide !== false){
47450
47451                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47452                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47453                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47454
47455                 this.hmenu.add('-',
47456                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47457                 );
47458             }
47459             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47460
47461             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47462         }
47463
47464         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47465             this.dd = new Roo.grid.GridDragZone(this.grid, {
47466                 ddGroup : this.grid.ddGroup || 'GridDD'
47467             });
47468         }
47469
47470         /*
47471         for(var i = 0; i < colCount; i++){
47472             if(cm.isHidden(i)){
47473                 this.hideColumn(i);
47474             }
47475             if(cm.config[i].align){
47476                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47477                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47478             }
47479         }*/
47480         
47481         this.updateHeaderSortState();
47482
47483         this.beforeInitialResize();
47484         this.layout(true);
47485
47486         // two part rendering gives faster view to the user
47487         this.renderPhase2.defer(1, this);
47488     },
47489
47490     renderPhase2 : function(){
47491         // render the rows now
47492         this.refresh();
47493         if(this.grid.autoSizeColumns){
47494             this.autoSizeColumns();
47495         }
47496     },
47497
47498     beforeInitialResize : function(){
47499
47500     },
47501
47502     onColumnSplitterMoved : function(i, w){
47503         this.userResized = true;
47504         var cm = this.grid.colModel;
47505         cm.setColumnWidth(i, w, true);
47506         var cid = cm.getColumnId(i);
47507         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47508         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47509         this.updateSplitters();
47510         this.layout();
47511         this.grid.fireEvent("columnresize", i, w);
47512     },
47513
47514     syncRowHeights : function(startIndex, endIndex){
47515         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47516             startIndex = startIndex || 0;
47517             var mrows = this.getBodyTable().rows;
47518             var lrows = this.getLockedTable().rows;
47519             var len = mrows.length-1;
47520             endIndex = Math.min(endIndex || len, len);
47521             for(var i = startIndex; i <= endIndex; i++){
47522                 var m = mrows[i], l = lrows[i];
47523                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47524                 m.style.height = l.style.height = h + "px";
47525             }
47526         }
47527     },
47528
47529     layout : function(initialRender, is2ndPass){
47530         var g = this.grid;
47531         var auto = g.autoHeight;
47532         var scrollOffset = 16;
47533         var c = g.getGridEl(), cm = this.cm,
47534                 expandCol = g.autoExpandColumn,
47535                 gv = this;
47536         //c.beginMeasure();
47537
47538         if(!c.dom.offsetWidth){ // display:none?
47539             if(initialRender){
47540                 this.lockedWrap.show();
47541                 this.mainWrap.show();
47542             }
47543             return;
47544         }
47545
47546         var hasLock = this.cm.isLocked(0);
47547
47548         var tbh = this.headerPanel.getHeight();
47549         var bbh = this.footerPanel.getHeight();
47550
47551         if(auto){
47552             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47553             var newHeight = ch + c.getBorderWidth("tb");
47554             if(g.maxHeight){
47555                 newHeight = Math.min(g.maxHeight, newHeight);
47556             }
47557             c.setHeight(newHeight);
47558         }
47559
47560         if(g.autoWidth){
47561             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47562         }
47563
47564         var s = this.scroller;
47565
47566         var csize = c.getSize(true);
47567
47568         this.el.setSize(csize.width, csize.height);
47569
47570         this.headerPanel.setWidth(csize.width);
47571         this.footerPanel.setWidth(csize.width);
47572
47573         var hdHeight = this.mainHd.getHeight();
47574         var vw = csize.width;
47575         var vh = csize.height - (tbh + bbh);
47576
47577         s.setSize(vw, vh);
47578
47579         var bt = this.getBodyTable();
47580         var ltWidth = hasLock ?
47581                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47582
47583         var scrollHeight = bt.offsetHeight;
47584         var scrollWidth = ltWidth + bt.offsetWidth;
47585         var vscroll = false, hscroll = false;
47586
47587         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47588
47589         var lw = this.lockedWrap, mw = this.mainWrap;
47590         var lb = this.lockedBody, mb = this.mainBody;
47591
47592         setTimeout(function(){
47593             var t = s.dom.offsetTop;
47594             var w = s.dom.clientWidth,
47595                 h = s.dom.clientHeight;
47596
47597             lw.setTop(t);
47598             lw.setSize(ltWidth, h);
47599
47600             mw.setLeftTop(ltWidth, t);
47601             mw.setSize(w-ltWidth, h);
47602
47603             lb.setHeight(h-hdHeight);
47604             mb.setHeight(h-hdHeight);
47605
47606             if(is2ndPass !== true && !gv.userResized && expandCol){
47607                 // high speed resize without full column calculation
47608                 
47609                 var ci = cm.getIndexById(expandCol);
47610                 if (ci < 0) {
47611                     ci = cm.findColumnIndex(expandCol);
47612                 }
47613                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47614                 var expandId = cm.getColumnId(ci);
47615                 var  tw = cm.getTotalWidth(false);
47616                 var currentWidth = cm.getColumnWidth(ci);
47617                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47618                 if(currentWidth != cw){
47619                     cm.setColumnWidth(ci, cw, true);
47620                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47621                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47622                     gv.updateSplitters();
47623                     gv.layout(false, true);
47624                 }
47625             }
47626
47627             if(initialRender){
47628                 lw.show();
47629                 mw.show();
47630             }
47631             //c.endMeasure();
47632         }, 10);
47633     },
47634
47635     onWindowResize : function(){
47636         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47637             return;
47638         }
47639         this.layout();
47640     },
47641
47642     appendFooter : function(parentEl){
47643         return null;
47644     },
47645
47646     sortAscText : "Sort Ascending",
47647     sortDescText : "Sort Descending",
47648     lockText : "Lock Column",
47649     unlockText : "Unlock Column",
47650     columnsText : "Columns"
47651 });
47652
47653
47654 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47655     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47656     this.proxy.el.addClass('x-grid3-col-dd');
47657 };
47658
47659 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47660     handleMouseDown : function(e){
47661
47662     },
47663
47664     callHandleMouseDown : function(e){
47665         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47666     }
47667 });
47668 /*
47669  * Based on:
47670  * Ext JS Library 1.1.1
47671  * Copyright(c) 2006-2007, Ext JS, LLC.
47672  *
47673  * Originally Released Under LGPL - original licence link has changed is not relivant.
47674  *
47675  * Fork - LGPL
47676  * <script type="text/javascript">
47677  */
47678  
47679 // private
47680 // This is a support class used internally by the Grid components
47681 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47682     this.grid = grid;
47683     this.view = grid.getView();
47684     this.proxy = this.view.resizeProxy;
47685     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47686         "gridSplitters" + this.grid.getGridEl().id, {
47687         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47688     });
47689     this.setHandleElId(Roo.id(hd));
47690     this.setOuterHandleElId(Roo.id(hd2));
47691     this.scroll = false;
47692 };
47693 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47694     fly: Roo.Element.fly,
47695
47696     b4StartDrag : function(x, y){
47697         this.view.headersDisabled = true;
47698         this.proxy.setHeight(this.view.mainWrap.getHeight());
47699         var w = this.cm.getColumnWidth(this.cellIndex);
47700         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47701         this.resetConstraints();
47702         this.setXConstraint(minw, 1000);
47703         this.setYConstraint(0, 0);
47704         this.minX = x - minw;
47705         this.maxX = x + 1000;
47706         this.startPos = x;
47707         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47708     },
47709
47710
47711     handleMouseDown : function(e){
47712         ev = Roo.EventObject.setEvent(e);
47713         var t = this.fly(ev.getTarget());
47714         if(t.hasClass("x-grid-split")){
47715             this.cellIndex = this.view.getCellIndex(t.dom);
47716             this.split = t.dom;
47717             this.cm = this.grid.colModel;
47718             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47719                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47720             }
47721         }
47722     },
47723
47724     endDrag : function(e){
47725         this.view.headersDisabled = false;
47726         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47727         var diff = endX - this.startPos;
47728         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47729     },
47730
47731     autoOffset : function(){
47732         this.setDelta(0,0);
47733     }
47734 });/*
47735  * Based on:
47736  * Ext JS Library 1.1.1
47737  * Copyright(c) 2006-2007, Ext JS, LLC.
47738  *
47739  * Originally Released Under LGPL - original licence link has changed is not relivant.
47740  *
47741  * Fork - LGPL
47742  * <script type="text/javascript">
47743  */
47744  
47745 // private
47746 // This is a support class used internally by the Grid components
47747 Roo.grid.GridDragZone = function(grid, config){
47748     this.view = grid.getView();
47749     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47750     if(this.view.lockedBody){
47751         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47752         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47753     }
47754     this.scroll = false;
47755     this.grid = grid;
47756     this.ddel = document.createElement('div');
47757     this.ddel.className = 'x-grid-dd-wrap';
47758 };
47759
47760 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47761     ddGroup : "GridDD",
47762
47763     getDragData : function(e){
47764         var t = Roo.lib.Event.getTarget(e);
47765         var rowIndex = this.view.findRowIndex(t);
47766         if(rowIndex !== false){
47767             var sm = this.grid.selModel;
47768             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47769               //  sm.mouseDown(e, t);
47770             //}
47771             if (e.hasModifier()){
47772                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47773             }
47774             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47775         }
47776         return false;
47777     },
47778
47779     onInitDrag : function(e){
47780         var data = this.dragData;
47781         this.ddel.innerHTML = this.grid.getDragDropText();
47782         this.proxy.update(this.ddel);
47783         // fire start drag?
47784     },
47785
47786     afterRepair : function(){
47787         this.dragging = false;
47788     },
47789
47790     getRepairXY : function(e, data){
47791         return false;
47792     },
47793
47794     onEndDrag : function(data, e){
47795         // fire end drag?
47796     },
47797
47798     onValidDrop : function(dd, e, id){
47799         // fire drag drop?
47800         this.hideProxy();
47801     },
47802
47803     beforeInvalidDrop : function(e, id){
47804
47805     }
47806 });/*
47807  * Based on:
47808  * Ext JS Library 1.1.1
47809  * Copyright(c) 2006-2007, Ext JS, LLC.
47810  *
47811  * Originally Released Under LGPL - original licence link has changed is not relivant.
47812  *
47813  * Fork - LGPL
47814  * <script type="text/javascript">
47815  */
47816  
47817
47818 /**
47819  * @class Roo.grid.ColumnModel
47820  * @extends Roo.util.Observable
47821  * This is the default implementation of a ColumnModel used by the Grid. It defines
47822  * the columns in the grid.
47823  * <br>Usage:<br>
47824  <pre><code>
47825  var colModel = new Roo.grid.ColumnModel([
47826         {header: "Ticker", width: 60, sortable: true, locked: true},
47827         {header: "Company Name", width: 150, sortable: true},
47828         {header: "Market Cap.", width: 100, sortable: true},
47829         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47830         {header: "Employees", width: 100, sortable: true, resizable: false}
47831  ]);
47832  </code></pre>
47833  * <p>
47834  
47835  * The config options listed for this class are options which may appear in each
47836  * individual column definition.
47837  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47838  * @constructor
47839  * @param {Object} config An Array of column config objects. See this class's
47840  * config objects for details.
47841 */
47842 Roo.grid.ColumnModel = function(config){
47843         /**
47844      * The config passed into the constructor
47845      */
47846     this.config = config;
47847     this.lookup = {};
47848
47849     // if no id, create one
47850     // if the column does not have a dataIndex mapping,
47851     // map it to the order it is in the config
47852     for(var i = 0, len = config.length; i < len; i++){
47853         var c = config[i];
47854         if(typeof c.dataIndex == "undefined"){
47855             c.dataIndex = i;
47856         }
47857         if(typeof c.renderer == "string"){
47858             c.renderer = Roo.util.Format[c.renderer];
47859         }
47860         if(typeof c.id == "undefined"){
47861             c.id = Roo.id();
47862         }
47863         if(c.editor && c.editor.xtype){
47864             c.editor  = Roo.factory(c.editor, Roo.grid);
47865         }
47866         if(c.editor && c.editor.isFormField){
47867             c.editor = new Roo.grid.GridEditor(c.editor);
47868         }
47869         this.lookup[c.id] = c;
47870     }
47871
47872     /**
47873      * The width of columns which have no width specified (defaults to 100)
47874      * @type Number
47875      */
47876     this.defaultWidth = 100;
47877
47878     /**
47879      * Default sortable of columns which have no sortable specified (defaults to false)
47880      * @type Boolean
47881      */
47882     this.defaultSortable = false;
47883
47884     this.addEvents({
47885         /**
47886              * @event widthchange
47887              * Fires when the width of a column changes.
47888              * @param {ColumnModel} this
47889              * @param {Number} columnIndex The column index
47890              * @param {Number} newWidth The new width
47891              */
47892             "widthchange": true,
47893         /**
47894              * @event headerchange
47895              * Fires when the text of a header changes.
47896              * @param {ColumnModel} this
47897              * @param {Number} columnIndex The column index
47898              * @param {Number} newText The new header text
47899              */
47900             "headerchange": true,
47901         /**
47902              * @event hiddenchange
47903              * Fires when a column is hidden or "unhidden".
47904              * @param {ColumnModel} this
47905              * @param {Number} columnIndex The column index
47906              * @param {Boolean} hidden true if hidden, false otherwise
47907              */
47908             "hiddenchange": true,
47909             /**
47910          * @event columnmoved
47911          * Fires when a column is moved.
47912          * @param {ColumnModel} this
47913          * @param {Number} oldIndex
47914          * @param {Number} newIndex
47915          */
47916         "columnmoved" : true,
47917         /**
47918          * @event columlockchange
47919          * Fires when a column's locked state is changed
47920          * @param {ColumnModel} this
47921          * @param {Number} colIndex
47922          * @param {Boolean} locked true if locked
47923          */
47924         "columnlockchange" : true
47925     });
47926     Roo.grid.ColumnModel.superclass.constructor.call(this);
47927 };
47928 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
47929     /**
47930      * @cfg {String} header The header text to display in the Grid view.
47931      */
47932     /**
47933      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
47934      * {@link Roo.data.Record} definition from which to draw the column's value. If not
47935      * specified, the column's index is used as an index into the Record's data Array.
47936      */
47937     /**
47938      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
47939      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
47940      */
47941     /**
47942      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
47943      * Defaults to the value of the {@link #defaultSortable} property.
47944      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
47945      */
47946     /**
47947      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
47948      */
47949     /**
47950      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
47951      */
47952     /**
47953      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
47954      */
47955     /**
47956      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
47957      */
47958     /**
47959      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
47960      * given the cell's data value. See {@link #setRenderer}. If not specified, the
47961      * default renderer uses the raw data value.
47962      */
47963        /**
47964      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
47965      */
47966     /**
47967      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
47968      */
47969
47970     /**
47971      * Returns the id of the column at the specified index.
47972      * @param {Number} index The column index
47973      * @return {String} the id
47974      */
47975     getColumnId : function(index){
47976         return this.config[index].id;
47977     },
47978
47979     /**
47980      * Returns the column for a specified id.
47981      * @param {String} id The column id
47982      * @return {Object} the column
47983      */
47984     getColumnById : function(id){
47985         return this.lookup[id];
47986     },
47987
47988     /**
47989      * Returns the index for a specified column id.
47990      * @param {String} id The column id
47991      * @return {Number} the index, or -1 if not found
47992      */
47993     getIndexById : function(id){
47994         for(var i = 0, len = this.config.length; i < len; i++){
47995             if(this.config[i].id == id){
47996                 return i;
47997             }
47998         }
47999         return -1;
48000     },
48001     /**
48002      * Returns the index for a specified column dataIndex.
48003      * @param {String} dataIndex The column dataIndex
48004      * @return {Number} the index, or -1 if not found
48005      */
48006     
48007     findColumnIndex : function(dataIndex){
48008         for(var i = 0, len = this.config.length; i < len; i++){
48009             if(this.config[i].dataIndex == dataIndex){
48010                 return i;
48011             }
48012         }
48013         return -1;
48014     },
48015     
48016     
48017     moveColumn : function(oldIndex, newIndex){
48018         var c = this.config[oldIndex];
48019         this.config.splice(oldIndex, 1);
48020         this.config.splice(newIndex, 0, c);
48021         this.dataMap = null;
48022         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48023     },
48024
48025     isLocked : function(colIndex){
48026         return this.config[colIndex].locked === true;
48027     },
48028
48029     setLocked : function(colIndex, value, suppressEvent){
48030         if(this.isLocked(colIndex) == value){
48031             return;
48032         }
48033         this.config[colIndex].locked = value;
48034         if(!suppressEvent){
48035             this.fireEvent("columnlockchange", this, colIndex, value);
48036         }
48037     },
48038
48039     getTotalLockedWidth : function(){
48040         var totalWidth = 0;
48041         for(var i = 0; i < this.config.length; i++){
48042             if(this.isLocked(i) && !this.isHidden(i)){
48043                 this.totalWidth += this.getColumnWidth(i);
48044             }
48045         }
48046         return totalWidth;
48047     },
48048
48049     getLockedCount : function(){
48050         for(var i = 0, len = this.config.length; i < len; i++){
48051             if(!this.isLocked(i)){
48052                 return i;
48053             }
48054         }
48055     },
48056
48057     /**
48058      * Returns the number of columns.
48059      * @return {Number}
48060      */
48061     getColumnCount : function(visibleOnly){
48062         if(visibleOnly === true){
48063             var c = 0;
48064             for(var i = 0, len = this.config.length; i < len; i++){
48065                 if(!this.isHidden(i)){
48066                     c++;
48067                 }
48068             }
48069             return c;
48070         }
48071         return this.config.length;
48072     },
48073
48074     /**
48075      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48076      * @param {Function} fn
48077      * @param {Object} scope (optional)
48078      * @return {Array} result
48079      */
48080     getColumnsBy : function(fn, scope){
48081         var r = [];
48082         for(var i = 0, len = this.config.length; i < len; i++){
48083             var c = this.config[i];
48084             if(fn.call(scope||this, c, i) === true){
48085                 r[r.length] = c;
48086             }
48087         }
48088         return r;
48089     },
48090
48091     /**
48092      * Returns true if the specified column is sortable.
48093      * @param {Number} col The column index
48094      * @return {Boolean}
48095      */
48096     isSortable : function(col){
48097         if(typeof this.config[col].sortable == "undefined"){
48098             return this.defaultSortable;
48099         }
48100         return this.config[col].sortable;
48101     },
48102
48103     /**
48104      * Returns the rendering (formatting) function defined for the column.
48105      * @param {Number} col The column index.
48106      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48107      */
48108     getRenderer : function(col){
48109         if(!this.config[col].renderer){
48110             return Roo.grid.ColumnModel.defaultRenderer;
48111         }
48112         return this.config[col].renderer;
48113     },
48114
48115     /**
48116      * Sets the rendering (formatting) function for a column.
48117      * @param {Number} col The column index
48118      * @param {Function} fn The function to use to process the cell's raw data
48119      * to return HTML markup for the grid view. The render function is called with
48120      * the following parameters:<ul>
48121      * <li>Data value.</li>
48122      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48123      * <li>css A CSS style string to apply to the table cell.</li>
48124      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48125      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48126      * <li>Row index</li>
48127      * <li>Column index</li>
48128      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48129      */
48130     setRenderer : function(col, fn){
48131         this.config[col].renderer = fn;
48132     },
48133
48134     /**
48135      * Returns the width for the specified column.
48136      * @param {Number} col The column index
48137      * @return {Number}
48138      */
48139     getColumnWidth : function(col){
48140         return this.config[col].width || this.defaultWidth;
48141     },
48142
48143     /**
48144      * Sets the width for a column.
48145      * @param {Number} col The column index
48146      * @param {Number} width The new width
48147      */
48148     setColumnWidth : function(col, width, suppressEvent){
48149         this.config[col].width = width;
48150         this.totalWidth = null;
48151         if(!suppressEvent){
48152              this.fireEvent("widthchange", this, col, width);
48153         }
48154     },
48155
48156     /**
48157      * Returns the total width of all columns.
48158      * @param {Boolean} includeHidden True to include hidden column widths
48159      * @return {Number}
48160      */
48161     getTotalWidth : function(includeHidden){
48162         if(!this.totalWidth){
48163             this.totalWidth = 0;
48164             for(var i = 0, len = this.config.length; i < len; i++){
48165                 if(includeHidden || !this.isHidden(i)){
48166                     this.totalWidth += this.getColumnWidth(i);
48167                 }
48168             }
48169         }
48170         return this.totalWidth;
48171     },
48172
48173     /**
48174      * Returns the header for the specified column.
48175      * @param {Number} col The column index
48176      * @return {String}
48177      */
48178     getColumnHeader : function(col){
48179         return this.config[col].header;
48180     },
48181
48182     /**
48183      * Sets the header for a column.
48184      * @param {Number} col The column index
48185      * @param {String} header The new header
48186      */
48187     setColumnHeader : function(col, header){
48188         this.config[col].header = header;
48189         this.fireEvent("headerchange", this, col, header);
48190     },
48191
48192     /**
48193      * Returns the tooltip for the specified column.
48194      * @param {Number} col The column index
48195      * @return {String}
48196      */
48197     getColumnTooltip : function(col){
48198             return this.config[col].tooltip;
48199     },
48200     /**
48201      * Sets the tooltip for a column.
48202      * @param {Number} col The column index
48203      * @param {String} tooltip The new tooltip
48204      */
48205     setColumnTooltip : function(col, tooltip){
48206             this.config[col].tooltip = tooltip;
48207     },
48208
48209     /**
48210      * Returns the dataIndex for the specified column.
48211      * @param {Number} col The column index
48212      * @return {Number}
48213      */
48214     getDataIndex : function(col){
48215         return this.config[col].dataIndex;
48216     },
48217
48218     /**
48219      * Sets the dataIndex for a column.
48220      * @param {Number} col The column index
48221      * @param {Number} dataIndex The new dataIndex
48222      */
48223     setDataIndex : function(col, dataIndex){
48224         this.config[col].dataIndex = dataIndex;
48225     },
48226
48227     
48228     
48229     /**
48230      * Returns true if the cell is editable.
48231      * @param {Number} colIndex The column index
48232      * @param {Number} rowIndex The row index
48233      * @return {Boolean}
48234      */
48235     isCellEditable : function(colIndex, rowIndex){
48236         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48237     },
48238
48239     /**
48240      * Returns the editor defined for the cell/column.
48241      * return false or null to disable editing.
48242      * @param {Number} colIndex The column index
48243      * @param {Number} rowIndex The row index
48244      * @return {Object}
48245      */
48246     getCellEditor : function(colIndex, rowIndex){
48247         return this.config[colIndex].editor;
48248     },
48249
48250     /**
48251      * Sets if a column is editable.
48252      * @param {Number} col The column index
48253      * @param {Boolean} editable True if the column is editable
48254      */
48255     setEditable : function(col, editable){
48256         this.config[col].editable = editable;
48257     },
48258
48259
48260     /**
48261      * Returns true if the column is hidden.
48262      * @param {Number} colIndex The column index
48263      * @return {Boolean}
48264      */
48265     isHidden : function(colIndex){
48266         return this.config[colIndex].hidden;
48267     },
48268
48269
48270     /**
48271      * Returns true if the column width cannot be changed
48272      */
48273     isFixed : function(colIndex){
48274         return this.config[colIndex].fixed;
48275     },
48276
48277     /**
48278      * Returns true if the column can be resized
48279      * @return {Boolean}
48280      */
48281     isResizable : function(colIndex){
48282         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48283     },
48284     /**
48285      * Sets if a column is hidden.
48286      * @param {Number} colIndex The column index
48287      * @param {Boolean} hidden True if the column is hidden
48288      */
48289     setHidden : function(colIndex, hidden){
48290         this.config[colIndex].hidden = hidden;
48291         this.totalWidth = null;
48292         this.fireEvent("hiddenchange", this, colIndex, hidden);
48293     },
48294
48295     /**
48296      * Sets the editor for a column.
48297      * @param {Number} col The column index
48298      * @param {Object} editor The editor object
48299      */
48300     setEditor : function(col, editor){
48301         this.config[col].editor = editor;
48302     }
48303 });
48304
48305 Roo.grid.ColumnModel.defaultRenderer = function(value){
48306         if(typeof value == "string" && value.length < 1){
48307             return "&#160;";
48308         }
48309         return value;
48310 };
48311
48312 // Alias for backwards compatibility
48313 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48314 /*
48315  * Based on:
48316  * Ext JS Library 1.1.1
48317  * Copyright(c) 2006-2007, Ext JS, LLC.
48318  *
48319  * Originally Released Under LGPL - original licence link has changed is not relivant.
48320  *
48321  * Fork - LGPL
48322  * <script type="text/javascript">
48323  */
48324
48325 /**
48326  * @class Roo.grid.AbstractSelectionModel
48327  * @extends Roo.util.Observable
48328  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48329  * implemented by descendant classes.  This class should not be directly instantiated.
48330  * @constructor
48331  */
48332 Roo.grid.AbstractSelectionModel = function(){
48333     this.locked = false;
48334     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48335 };
48336
48337 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48338     /** @ignore Called by the grid automatically. Do not call directly. */
48339     init : function(grid){
48340         this.grid = grid;
48341         this.initEvents();
48342     },
48343
48344     /**
48345      * Locks the selections.
48346      */
48347     lock : function(){
48348         this.locked = true;
48349     },
48350
48351     /**
48352      * Unlocks the selections.
48353      */
48354     unlock : function(){
48355         this.locked = false;
48356     },
48357
48358     /**
48359      * Returns true if the selections are locked.
48360      * @return {Boolean}
48361      */
48362     isLocked : function(){
48363         return this.locked;
48364     }
48365 });/*
48366  * Based on:
48367  * Ext JS Library 1.1.1
48368  * Copyright(c) 2006-2007, Ext JS, LLC.
48369  *
48370  * Originally Released Under LGPL - original licence link has changed is not relivant.
48371  *
48372  * Fork - LGPL
48373  * <script type="text/javascript">
48374  */
48375 /**
48376  * @extends Roo.grid.AbstractSelectionModel
48377  * @class Roo.grid.RowSelectionModel
48378  * The default SelectionModel used by {@link Roo.grid.Grid}.
48379  * It supports multiple selections and keyboard selection/navigation. 
48380  * @constructor
48381  * @param {Object} config
48382  */
48383 Roo.grid.RowSelectionModel = function(config){
48384     Roo.apply(this, config);
48385     this.selections = new Roo.util.MixedCollection(false, function(o){
48386         return o.id;
48387     });
48388
48389     this.last = false;
48390     this.lastActive = false;
48391
48392     this.addEvents({
48393         /**
48394              * @event selectionchange
48395              * Fires when the selection changes
48396              * @param {SelectionModel} this
48397              */
48398             "selectionchange" : true,
48399         /**
48400              * @event afterselectionchange
48401              * Fires after the selection changes (eg. by key press or clicking)
48402              * @param {SelectionModel} this
48403              */
48404             "afterselectionchange" : true,
48405         /**
48406              * @event beforerowselect
48407              * Fires when a row is selected being selected, return false to cancel.
48408              * @param {SelectionModel} this
48409              * @param {Number} rowIndex The selected index
48410              * @param {Boolean} keepExisting False if other selections will be cleared
48411              */
48412             "beforerowselect" : true,
48413         /**
48414              * @event rowselect
48415              * Fires when a row is selected.
48416              * @param {SelectionModel} this
48417              * @param {Number} rowIndex The selected index
48418              * @param {Roo.data.Record} r The record
48419              */
48420             "rowselect" : true,
48421         /**
48422              * @event rowdeselect
48423              * Fires when a row is deselected.
48424              * @param {SelectionModel} this
48425              * @param {Number} rowIndex The selected index
48426              */
48427         "rowdeselect" : true
48428     });
48429     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48430     this.locked = false;
48431 };
48432
48433 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48434     /**
48435      * @cfg {Boolean} singleSelect
48436      * True to allow selection of only one row at a time (defaults to false)
48437      */
48438     singleSelect : false,
48439
48440     // private
48441     initEvents : function(){
48442
48443         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48444             this.grid.on("mousedown", this.handleMouseDown, this);
48445         }else{ // allow click to work like normal
48446             this.grid.on("rowclick", this.handleDragableRowClick, this);
48447         }
48448
48449         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48450             "up" : function(e){
48451                 if(!e.shiftKey){
48452                     this.selectPrevious(e.shiftKey);
48453                 }else if(this.last !== false && this.lastActive !== false){
48454                     var last = this.last;
48455                     this.selectRange(this.last,  this.lastActive-1);
48456                     this.grid.getView().focusRow(this.lastActive);
48457                     if(last !== false){
48458                         this.last = last;
48459                     }
48460                 }else{
48461                     this.selectFirstRow();
48462                 }
48463                 this.fireEvent("afterselectionchange", this);
48464             },
48465             "down" : function(e){
48466                 if(!e.shiftKey){
48467                     this.selectNext(e.shiftKey);
48468                 }else if(this.last !== false && this.lastActive !== false){
48469                     var last = this.last;
48470                     this.selectRange(this.last,  this.lastActive+1);
48471                     this.grid.getView().focusRow(this.lastActive);
48472                     if(last !== false){
48473                         this.last = last;
48474                     }
48475                 }else{
48476                     this.selectFirstRow();
48477                 }
48478                 this.fireEvent("afterselectionchange", this);
48479             },
48480             scope: this
48481         });
48482
48483         var view = this.grid.view;
48484         view.on("refresh", this.onRefresh, this);
48485         view.on("rowupdated", this.onRowUpdated, this);
48486         view.on("rowremoved", this.onRemove, this);
48487     },
48488
48489     // private
48490     onRefresh : function(){
48491         var ds = this.grid.dataSource, i, v = this.grid.view;
48492         var s = this.selections;
48493         s.each(function(r){
48494             if((i = ds.indexOfId(r.id)) != -1){
48495                 v.onRowSelect(i);
48496             }else{
48497                 s.remove(r);
48498             }
48499         });
48500     },
48501
48502     // private
48503     onRemove : function(v, index, r){
48504         this.selections.remove(r);
48505     },
48506
48507     // private
48508     onRowUpdated : function(v, index, r){
48509         if(this.isSelected(r)){
48510             v.onRowSelect(index);
48511         }
48512     },
48513
48514     /**
48515      * Select records.
48516      * @param {Array} records The records to select
48517      * @param {Boolean} keepExisting (optional) True to keep existing selections
48518      */
48519     selectRecords : function(records, keepExisting){
48520         if(!keepExisting){
48521             this.clearSelections();
48522         }
48523         var ds = this.grid.dataSource;
48524         for(var i = 0, len = records.length; i < len; i++){
48525             this.selectRow(ds.indexOf(records[i]), true);
48526         }
48527     },
48528
48529     /**
48530      * Gets the number of selected rows.
48531      * @return {Number}
48532      */
48533     getCount : function(){
48534         return this.selections.length;
48535     },
48536
48537     /**
48538      * Selects the first row in the grid.
48539      */
48540     selectFirstRow : function(){
48541         this.selectRow(0);
48542     },
48543
48544     /**
48545      * Select the last row.
48546      * @param {Boolean} keepExisting (optional) True to keep existing selections
48547      */
48548     selectLastRow : function(keepExisting){
48549         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48550     },
48551
48552     /**
48553      * Selects the row immediately following the last selected row.
48554      * @param {Boolean} keepExisting (optional) True to keep existing selections
48555      */
48556     selectNext : function(keepExisting){
48557         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48558             this.selectRow(this.last+1, keepExisting);
48559             this.grid.getView().focusRow(this.last);
48560         }
48561     },
48562
48563     /**
48564      * Selects the row that precedes the last selected row.
48565      * @param {Boolean} keepExisting (optional) True to keep existing selections
48566      */
48567     selectPrevious : function(keepExisting){
48568         if(this.last){
48569             this.selectRow(this.last-1, keepExisting);
48570             this.grid.getView().focusRow(this.last);
48571         }
48572     },
48573
48574     /**
48575      * Returns the selected records
48576      * @return {Array} Array of selected records
48577      */
48578     getSelections : function(){
48579         return [].concat(this.selections.items);
48580     },
48581
48582     /**
48583      * Returns the first selected record.
48584      * @return {Record}
48585      */
48586     getSelected : function(){
48587         return this.selections.itemAt(0);
48588     },
48589
48590
48591     /**
48592      * Clears all selections.
48593      */
48594     clearSelections : function(fast){
48595         if(this.locked) return;
48596         if(fast !== true){
48597             var ds = this.grid.dataSource;
48598             var s = this.selections;
48599             s.each(function(r){
48600                 this.deselectRow(ds.indexOfId(r.id));
48601             }, this);
48602             s.clear();
48603         }else{
48604             this.selections.clear();
48605         }
48606         this.last = false;
48607     },
48608
48609
48610     /**
48611      * Selects all rows.
48612      */
48613     selectAll : function(){
48614         if(this.locked) return;
48615         this.selections.clear();
48616         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48617             this.selectRow(i, true);
48618         }
48619     },
48620
48621     /**
48622      * Returns True if there is a selection.
48623      * @return {Boolean}
48624      */
48625     hasSelection : function(){
48626         return this.selections.length > 0;
48627     },
48628
48629     /**
48630      * Returns True if the specified row is selected.
48631      * @param {Number/Record} record The record or index of the record to check
48632      * @return {Boolean}
48633      */
48634     isSelected : function(index){
48635         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48636         return (r && this.selections.key(r.id) ? true : false);
48637     },
48638
48639     /**
48640      * Returns True if the specified record id is selected.
48641      * @param {String} id The id of record to check
48642      * @return {Boolean}
48643      */
48644     isIdSelected : function(id){
48645         return (this.selections.key(id) ? true : false);
48646     },
48647
48648     // private
48649     handleMouseDown : function(e, t){
48650         var view = this.grid.getView(), rowIndex;
48651         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48652             return;
48653         };
48654         if(e.shiftKey && this.last !== false){
48655             var last = this.last;
48656             this.selectRange(last, rowIndex, e.ctrlKey);
48657             this.last = last; // reset the last
48658             view.focusRow(rowIndex);
48659         }else{
48660             var isSelected = this.isSelected(rowIndex);
48661             if(e.button !== 0 && isSelected){
48662                 view.focusRow(rowIndex);
48663             }else if(e.ctrlKey && isSelected){
48664                 this.deselectRow(rowIndex);
48665             }else if(!isSelected){
48666                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48667                 view.focusRow(rowIndex);
48668             }
48669         }
48670         this.fireEvent("afterselectionchange", this);
48671     },
48672     // private
48673     handleDragableRowClick :  function(grid, rowIndex, e) 
48674     {
48675         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48676             this.selectRow(rowIndex, false);
48677             grid.view.focusRow(rowIndex);
48678              this.fireEvent("afterselectionchange", this);
48679         }
48680     },
48681     
48682     /**
48683      * Selects multiple rows.
48684      * @param {Array} rows Array of the indexes of the row to select
48685      * @param {Boolean} keepExisting (optional) True to keep existing selections
48686      */
48687     selectRows : function(rows, keepExisting){
48688         if(!keepExisting){
48689             this.clearSelections();
48690         }
48691         for(var i = 0, len = rows.length; i < len; i++){
48692             this.selectRow(rows[i], true);
48693         }
48694     },
48695
48696     /**
48697      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48698      * @param {Number} startRow The index of the first row in the range
48699      * @param {Number} endRow The index of the last row in the range
48700      * @param {Boolean} keepExisting (optional) True to retain existing selections
48701      */
48702     selectRange : function(startRow, endRow, keepExisting){
48703         if(this.locked) return;
48704         if(!keepExisting){
48705             this.clearSelections();
48706         }
48707         if(startRow <= endRow){
48708             for(var i = startRow; i <= endRow; i++){
48709                 this.selectRow(i, true);
48710             }
48711         }else{
48712             for(var i = startRow; i >= endRow; i--){
48713                 this.selectRow(i, true);
48714             }
48715         }
48716     },
48717
48718     /**
48719      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48720      * @param {Number} startRow The index of the first row in the range
48721      * @param {Number} endRow The index of the last row in the range
48722      */
48723     deselectRange : function(startRow, endRow, preventViewNotify){
48724         if(this.locked) return;
48725         for(var i = startRow; i <= endRow; i++){
48726             this.deselectRow(i, preventViewNotify);
48727         }
48728     },
48729
48730     /**
48731      * Selects a row.
48732      * @param {Number} row The index of the row to select
48733      * @param {Boolean} keepExisting (optional) True to keep existing selections
48734      */
48735     selectRow : function(index, keepExisting, preventViewNotify){
48736         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48737         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48738             if(!keepExisting || this.singleSelect){
48739                 this.clearSelections();
48740             }
48741             var r = this.grid.dataSource.getAt(index);
48742             this.selections.add(r);
48743             this.last = this.lastActive = index;
48744             if(!preventViewNotify){
48745                 this.grid.getView().onRowSelect(index);
48746             }
48747             this.fireEvent("rowselect", this, index, r);
48748             this.fireEvent("selectionchange", this);
48749         }
48750     },
48751
48752     /**
48753      * Deselects a row.
48754      * @param {Number} row The index of the row to deselect
48755      */
48756     deselectRow : function(index, preventViewNotify){
48757         if(this.locked) return;
48758         if(this.last == index){
48759             this.last = false;
48760         }
48761         if(this.lastActive == index){
48762             this.lastActive = false;
48763         }
48764         var r = this.grid.dataSource.getAt(index);
48765         this.selections.remove(r);
48766         if(!preventViewNotify){
48767             this.grid.getView().onRowDeselect(index);
48768         }
48769         this.fireEvent("rowdeselect", this, index);
48770         this.fireEvent("selectionchange", this);
48771     },
48772
48773     // private
48774     restoreLast : function(){
48775         if(this._last){
48776             this.last = this._last;
48777         }
48778     },
48779
48780     // private
48781     acceptsNav : function(row, col, cm){
48782         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48783     },
48784
48785     // private
48786     onEditorKey : function(field, e){
48787         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48788         if(k == e.TAB){
48789             e.stopEvent();
48790             ed.completeEdit();
48791             if(e.shiftKey){
48792                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48793             }else{
48794                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48795             }
48796         }else if(k == e.ENTER && !e.ctrlKey){
48797             e.stopEvent();
48798             ed.completeEdit();
48799             if(e.shiftKey){
48800                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48801             }else{
48802                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48803             }
48804         }else if(k == e.ESC){
48805             ed.cancelEdit();
48806         }
48807         if(newCell){
48808             g.startEditing(newCell[0], newCell[1]);
48809         }
48810     }
48811 });/*
48812  * Based on:
48813  * Ext JS Library 1.1.1
48814  * Copyright(c) 2006-2007, Ext JS, LLC.
48815  *
48816  * Originally Released Under LGPL - original licence link has changed is not relivant.
48817  *
48818  * Fork - LGPL
48819  * <script type="text/javascript">
48820  */
48821 /**
48822  * @class Roo.grid.CellSelectionModel
48823  * @extends Roo.grid.AbstractSelectionModel
48824  * This class provides the basic implementation for cell selection in a grid.
48825  * @constructor
48826  * @param {Object} config The object containing the configuration of this model.
48827  */
48828 Roo.grid.CellSelectionModel = function(config){
48829     Roo.apply(this, config);
48830
48831     this.selection = null;
48832
48833     this.addEvents({
48834         /**
48835              * @event beforerowselect
48836              * Fires before a cell is selected.
48837              * @param {SelectionModel} this
48838              * @param {Number} rowIndex The selected row index
48839              * @param {Number} colIndex The selected cell index
48840              */
48841             "beforecellselect" : true,
48842         /**
48843              * @event cellselect
48844              * Fires when a cell is selected.
48845              * @param {SelectionModel} this
48846              * @param {Number} rowIndex The selected row index
48847              * @param {Number} colIndex The selected cell index
48848              */
48849             "cellselect" : true,
48850         /**
48851              * @event selectionchange
48852              * Fires when the active selection changes.
48853              * @param {SelectionModel} this
48854              * @param {Object} selection null for no selection or an object (o) with two properties
48855                 <ul>
48856                 <li>o.record: the record object for the row the selection is in</li>
48857                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
48858                 </ul>
48859              */
48860             "selectionchange" : true
48861     });
48862     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
48863 };
48864
48865 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
48866
48867     /** @ignore */
48868     initEvents : function(){
48869         this.grid.on("mousedown", this.handleMouseDown, this);
48870         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
48871         var view = this.grid.view;
48872         view.on("refresh", this.onViewChange, this);
48873         view.on("rowupdated", this.onRowUpdated, this);
48874         view.on("beforerowremoved", this.clearSelections, this);
48875         view.on("beforerowsinserted", this.clearSelections, this);
48876         if(this.grid.isEditor){
48877             this.grid.on("beforeedit", this.beforeEdit,  this);
48878         }
48879     },
48880
48881         //private
48882     beforeEdit : function(e){
48883         this.select(e.row, e.column, false, true, e.record);
48884     },
48885
48886         //private
48887     onRowUpdated : function(v, index, r){
48888         if(this.selection && this.selection.record == r){
48889             v.onCellSelect(index, this.selection.cell[1]);
48890         }
48891     },
48892
48893         //private
48894     onViewChange : function(){
48895         this.clearSelections(true);
48896     },
48897
48898         /**
48899          * Returns the currently selected cell,.
48900          * @return {Array} The selected cell (row, column) or null if none selected.
48901          */
48902     getSelectedCell : function(){
48903         return this.selection ? this.selection.cell : null;
48904     },
48905
48906     /**
48907      * Clears all selections.
48908      * @param {Boolean} true to prevent the gridview from being notified about the change.
48909      */
48910     clearSelections : function(preventNotify){
48911         var s = this.selection;
48912         if(s){
48913             if(preventNotify !== true){
48914                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
48915             }
48916             this.selection = null;
48917             this.fireEvent("selectionchange", this, null);
48918         }
48919     },
48920
48921     /**
48922      * Returns true if there is a selection.
48923      * @return {Boolean}
48924      */
48925     hasSelection : function(){
48926         return this.selection ? true : false;
48927     },
48928
48929     /** @ignore */
48930     handleMouseDown : function(e, t){
48931         var v = this.grid.getView();
48932         if(this.isLocked()){
48933             return;
48934         };
48935         var row = v.findRowIndex(t);
48936         var cell = v.findCellIndex(t);
48937         if(row !== false && cell !== false){
48938             this.select(row, cell);
48939         }
48940     },
48941
48942     /**
48943      * Selects a cell.
48944      * @param {Number} rowIndex
48945      * @param {Number} collIndex
48946      */
48947     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
48948         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
48949             this.clearSelections();
48950             r = r || this.grid.dataSource.getAt(rowIndex);
48951             this.selection = {
48952                 record : r,
48953                 cell : [rowIndex, colIndex]
48954             };
48955             if(!preventViewNotify){
48956                 var v = this.grid.getView();
48957                 v.onCellSelect(rowIndex, colIndex);
48958                 if(preventFocus !== true){
48959                     v.focusCell(rowIndex, colIndex);
48960                 }
48961             }
48962             this.fireEvent("cellselect", this, rowIndex, colIndex);
48963             this.fireEvent("selectionchange", this, this.selection);
48964         }
48965     },
48966
48967         //private
48968     isSelectable : function(rowIndex, colIndex, cm){
48969         return !cm.isHidden(colIndex);
48970     },
48971
48972     /** @ignore */
48973     handleKeyDown : function(e){
48974         if(!e.isNavKeyPress()){
48975             return;
48976         }
48977         var g = this.grid, s = this.selection;
48978         if(!s){
48979             e.stopEvent();
48980             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
48981             if(cell){
48982                 this.select(cell[0], cell[1]);
48983             }
48984             return;
48985         }
48986         var sm = this;
48987         var walk = function(row, col, step){
48988             return g.walkCells(row, col, step, sm.isSelectable,  sm);
48989         };
48990         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
48991         var newCell;
48992
48993         switch(k){
48994              case e.TAB:
48995                  if(e.shiftKey){
48996                      newCell = walk(r, c-1, -1);
48997                  }else{
48998                      newCell = walk(r, c+1, 1);
48999                  }
49000              break;
49001              case e.DOWN:
49002                  newCell = walk(r+1, c, 1);
49003              break;
49004              case e.UP:
49005                  newCell = walk(r-1, c, -1);
49006              break;
49007              case e.RIGHT:
49008                  newCell = walk(r, c+1, 1);
49009              break;
49010              case e.LEFT:
49011                  newCell = walk(r, c-1, -1);
49012              break;
49013              case e.ENTER:
49014                  if(g.isEditor && !g.editing){
49015                     g.startEditing(r, c);
49016                     e.stopEvent();
49017                     return;
49018                 }
49019              break;
49020         };
49021         if(newCell){
49022             this.select(newCell[0], newCell[1]);
49023             e.stopEvent();
49024         }
49025     },
49026
49027     acceptsNav : function(row, col, cm){
49028         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49029     },
49030
49031     onEditorKey : function(field, e){
49032         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49033         if(k == e.TAB){
49034             if(e.shiftKey){
49035                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49036             }else{
49037                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49038             }
49039             e.stopEvent();
49040         }else if(k == e.ENTER && !e.ctrlKey){
49041             ed.completeEdit();
49042             e.stopEvent();
49043         }else if(k == e.ESC){
49044             ed.cancelEdit();
49045         }
49046         if(newCell){
49047             g.startEditing(newCell[0], newCell[1]);
49048         }
49049     }
49050 });/*
49051  * Based on:
49052  * Ext JS Library 1.1.1
49053  * Copyright(c) 2006-2007, Ext JS, LLC.
49054  *
49055  * Originally Released Under LGPL - original licence link has changed is not relivant.
49056  *
49057  * Fork - LGPL
49058  * <script type="text/javascript">
49059  */
49060  
49061 /**
49062  * @class Roo.grid.EditorGrid
49063  * @extends Roo.grid.Grid
49064  * Class for creating and editable grid.
49065  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49066  * The container MUST have some type of size defined for the grid to fill. The container will be 
49067  * automatically set to position relative if it isn't already.
49068  * @param {Object} dataSource The data model to bind to
49069  * @param {Object} colModel The column model with info about this grid's columns
49070  */
49071 Roo.grid.EditorGrid = function(container, config){
49072     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49073     this.getGridEl().addClass("xedit-grid");
49074
49075     if(!this.selModel){
49076         this.selModel = new Roo.grid.CellSelectionModel();
49077     }
49078
49079     this.activeEditor = null;
49080
49081         this.addEvents({
49082             /**
49083              * @event beforeedit
49084              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49085              * <ul style="padding:5px;padding-left:16px;">
49086              * <li>grid - This grid</li>
49087              * <li>record - The record being edited</li>
49088              * <li>field - The field name being edited</li>
49089              * <li>value - The value for the field being edited.</li>
49090              * <li>row - The grid row index</li>
49091              * <li>column - The grid column index</li>
49092              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49093              * </ul>
49094              * @param {Object} e An edit event (see above for description)
49095              */
49096             "beforeedit" : true,
49097             /**
49098              * @event afteredit
49099              * Fires after a cell is edited. <br />
49100              * <ul style="padding:5px;padding-left:16px;">
49101              * <li>grid - This grid</li>
49102              * <li>record - The record being edited</li>
49103              * <li>field - The field name being edited</li>
49104              * <li>value - The value being set</li>
49105              * <li>originalValue - The original value for the field, before the edit.</li>
49106              * <li>row - The grid row index</li>
49107              * <li>column - The grid column index</li>
49108              * </ul>
49109              * @param {Object} e An edit event (see above for description)
49110              */
49111             "afteredit" : true,
49112             /**
49113              * @event validateedit
49114              * Fires after a cell is edited, but before the value is set in the record. 
49115          * You can use this to modify the value being set in the field, Return false
49116              * to cancel the change. The edit event object has the following properties <br />
49117              * <ul style="padding:5px;padding-left:16px;">
49118          * <li>editor - This editor</li>
49119              * <li>grid - This grid</li>
49120              * <li>record - The record being edited</li>
49121              * <li>field - The field name being edited</li>
49122              * <li>value - The value being set</li>
49123              * <li>originalValue - The original value for the field, before the edit.</li>
49124              * <li>row - The grid row index</li>
49125              * <li>column - The grid column index</li>
49126              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49127              * </ul>
49128              * @param {Object} e An edit event (see above for description)
49129              */
49130             "validateedit" : true
49131         });
49132     this.on("bodyscroll", this.stopEditing,  this);
49133     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49134 };
49135
49136 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49137     /**
49138      * @cfg {Number} clicksToEdit
49139      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49140      */
49141     clicksToEdit: 2,
49142
49143     // private
49144     isEditor : true,
49145     // private
49146     trackMouseOver: false, // causes very odd FF errors
49147
49148     onCellDblClick : function(g, row, col){
49149         this.startEditing(row, col);
49150     },
49151
49152     onEditComplete : function(ed, value, startValue){
49153         this.editing = false;
49154         this.activeEditor = null;
49155         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49156         var r = ed.record;
49157         var field = this.colModel.getDataIndex(ed.col);
49158         var e = {
49159             grid: this,
49160             record: r,
49161             field: field,
49162             originalValue: startValue,
49163             value: value,
49164             row: ed.row,
49165             column: ed.col,
49166             cancel:false,
49167             editor: ed
49168         };
49169         if(String(value) !== String(startValue)){
49170             
49171             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49172                 r.set(field, e.value);
49173                 delete e.cancel; //?? why!!!
49174                 this.fireEvent("afteredit", e);
49175             }
49176         } else {
49177             this.fireEvent("afteredit", e); // always fir it!
49178         }
49179         this.view.focusCell(ed.row, ed.col);
49180     },
49181
49182     /**
49183      * Starts editing the specified for the specified row/column
49184      * @param {Number} rowIndex
49185      * @param {Number} colIndex
49186      */
49187     startEditing : function(row, col){
49188         this.stopEditing();
49189         if(this.colModel.isCellEditable(col, row)){
49190             this.view.ensureVisible(row, col, true);
49191             var r = this.dataSource.getAt(row);
49192             var field = this.colModel.getDataIndex(col);
49193             var e = {
49194                 grid: this,
49195                 record: r,
49196                 field: field,
49197                 value: r.data[field],
49198                 row: row,
49199                 column: col,
49200                 cancel:false
49201             };
49202             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49203                 this.editing = true;
49204                 var ed = this.colModel.getCellEditor(col, row);
49205                 
49206                 if (!ed) {
49207                     return;
49208                 }
49209                 if(!ed.rendered){
49210                     ed.render(ed.parentEl || document.body);
49211                 }
49212                 ed.field.reset();
49213                 (function(){ // complex but required for focus issues in safari, ie and opera
49214                     ed.row = row;
49215                     ed.col = col;
49216                     ed.record = r;
49217                     ed.on("complete", this.onEditComplete, this, {single: true});
49218                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49219                     this.activeEditor = ed;
49220                     var v = r.data[field];
49221                     ed.startEdit(this.view.getCell(row, col), v);
49222                 }).defer(50, this);
49223             }
49224         }
49225     },
49226         
49227     /**
49228      * Stops any active editing
49229      */
49230     stopEditing : function(){
49231         if(this.activeEditor){
49232             this.activeEditor.completeEdit();
49233         }
49234         this.activeEditor = null;
49235     }
49236 });/*
49237  * Based on:
49238  * Ext JS Library 1.1.1
49239  * Copyright(c) 2006-2007, Ext JS, LLC.
49240  *
49241  * Originally Released Under LGPL - original licence link has changed is not relivant.
49242  *
49243  * Fork - LGPL
49244  * <script type="text/javascript">
49245  */
49246
49247 // private - not really -- you end up using it !
49248 // This is a support class used internally by the Grid components
49249
49250 /**
49251  * @class Roo.grid.GridEditor
49252  * @extends Roo.Editor
49253  * Class for creating and editable grid elements.
49254  * @param {Object} config any settings (must include field)
49255  */
49256 Roo.grid.GridEditor = function(field, config){
49257     if (!config && field.field) {
49258         config = field;
49259         field = Roo.factory(config.field, Roo.form);
49260     }
49261     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49262     field.monitorTab = false;
49263 };
49264
49265 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49266     
49267     /**
49268      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49269      */
49270     
49271     alignment: "tl-tl",
49272     autoSize: "width",
49273     hideEl : false,
49274     cls: "x-small-editor x-grid-editor",
49275     shim:false,
49276     shadow:"frame"
49277 });/*
49278  * Based on:
49279  * Ext JS Library 1.1.1
49280  * Copyright(c) 2006-2007, Ext JS, LLC.
49281  *
49282  * Originally Released Under LGPL - original licence link has changed is not relivant.
49283  *
49284  * Fork - LGPL
49285  * <script type="text/javascript">
49286  */
49287   
49288
49289   
49290 Roo.grid.PropertyRecord = Roo.data.Record.create([
49291     {name:'name',type:'string'},  'value'
49292 ]);
49293
49294
49295 Roo.grid.PropertyStore = function(grid, source){
49296     this.grid = grid;
49297     this.store = new Roo.data.Store({
49298         recordType : Roo.grid.PropertyRecord
49299     });
49300     this.store.on('update', this.onUpdate,  this);
49301     if(source){
49302         this.setSource(source);
49303     }
49304     Roo.grid.PropertyStore.superclass.constructor.call(this);
49305 };
49306
49307
49308
49309 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49310     setSource : function(o){
49311         this.source = o;
49312         this.store.removeAll();
49313         var data = [];
49314         for(var k in o){
49315             if(this.isEditableValue(o[k])){
49316                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49317             }
49318         }
49319         this.store.loadRecords({records: data}, {}, true);
49320     },
49321
49322     onUpdate : function(ds, record, type){
49323         if(type == Roo.data.Record.EDIT){
49324             var v = record.data['value'];
49325             var oldValue = record.modified['value'];
49326             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49327                 this.source[record.id] = v;
49328                 record.commit();
49329                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49330             }else{
49331                 record.reject();
49332             }
49333         }
49334     },
49335
49336     getProperty : function(row){
49337        return this.store.getAt(row);
49338     },
49339
49340     isEditableValue: function(val){
49341         if(val && val instanceof Date){
49342             return true;
49343         }else if(typeof val == 'object' || typeof val == 'function'){
49344             return false;
49345         }
49346         return true;
49347     },
49348
49349     setValue : function(prop, value){
49350         this.source[prop] = value;
49351         this.store.getById(prop).set('value', value);
49352     },
49353
49354     getSource : function(){
49355         return this.source;
49356     }
49357 });
49358
49359 Roo.grid.PropertyColumnModel = function(grid, store){
49360     this.grid = grid;
49361     var g = Roo.grid;
49362     g.PropertyColumnModel.superclass.constructor.call(this, [
49363         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49364         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49365     ]);
49366     this.store = store;
49367     this.bselect = Roo.DomHelper.append(document.body, {
49368         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49369             {tag: 'option', value: 'true', html: 'true'},
49370             {tag: 'option', value: 'false', html: 'false'}
49371         ]
49372     });
49373     Roo.id(this.bselect);
49374     var f = Roo.form;
49375     this.editors = {
49376         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49377         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49378         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49379         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49380         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49381     };
49382     this.renderCellDelegate = this.renderCell.createDelegate(this);
49383     this.renderPropDelegate = this.renderProp.createDelegate(this);
49384 };
49385
49386 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49387     
49388     
49389     nameText : 'Name',
49390     valueText : 'Value',
49391     
49392     dateFormat : 'm/j/Y',
49393     
49394     
49395     renderDate : function(dateVal){
49396         return dateVal.dateFormat(this.dateFormat);
49397     },
49398
49399     renderBool : function(bVal){
49400         return bVal ? 'true' : 'false';
49401     },
49402
49403     isCellEditable : function(colIndex, rowIndex){
49404         return colIndex == 1;
49405     },
49406
49407     getRenderer : function(col){
49408         return col == 1 ?
49409             this.renderCellDelegate : this.renderPropDelegate;
49410     },
49411
49412     renderProp : function(v){
49413         return this.getPropertyName(v);
49414     },
49415
49416     renderCell : function(val){
49417         var rv = val;
49418         if(val instanceof Date){
49419             rv = this.renderDate(val);
49420         }else if(typeof val == 'boolean'){
49421             rv = this.renderBool(val);
49422         }
49423         return Roo.util.Format.htmlEncode(rv);
49424     },
49425
49426     getPropertyName : function(name){
49427         var pn = this.grid.propertyNames;
49428         return pn && pn[name] ? pn[name] : name;
49429     },
49430
49431     getCellEditor : function(colIndex, rowIndex){
49432         var p = this.store.getProperty(rowIndex);
49433         var n = p.data['name'], val = p.data['value'];
49434         
49435         if(typeof(this.grid.customEditors[n]) == 'string'){
49436             return this.editors[this.grid.customEditors[n]];
49437         }
49438         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49439             return this.grid.customEditors[n];
49440         }
49441         if(val instanceof Date){
49442             return this.editors['date'];
49443         }else if(typeof val == 'number'){
49444             return this.editors['number'];
49445         }else if(typeof val == 'boolean'){
49446             return this.editors['boolean'];
49447         }else{
49448             return this.editors['string'];
49449         }
49450     }
49451 });
49452
49453 /**
49454  * @class Roo.grid.PropertyGrid
49455  * @extends Roo.grid.EditorGrid
49456  * This class represents the  interface of a component based property grid control.
49457  * <br><br>Usage:<pre><code>
49458  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49459       
49460  });
49461  // set any options
49462  grid.render();
49463  * </code></pre>
49464   
49465  * @constructor
49466  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49467  * The container MUST have some type of size defined for the grid to fill. The container will be
49468  * automatically set to position relative if it isn't already.
49469  * @param {Object} config A config object that sets properties on this grid.
49470  */
49471 Roo.grid.PropertyGrid = function(container, config){
49472     config = config || {};
49473     var store = new Roo.grid.PropertyStore(this);
49474     this.store = store;
49475     var cm = new Roo.grid.PropertyColumnModel(this, store);
49476     store.store.sort('name', 'ASC');
49477     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49478         ds: store.store,
49479         cm: cm,
49480         enableColLock:false,
49481         enableColumnMove:false,
49482         stripeRows:false,
49483         trackMouseOver: false,
49484         clicksToEdit:1
49485     }, config));
49486     this.getGridEl().addClass('x-props-grid');
49487     this.lastEditRow = null;
49488     this.on('columnresize', this.onColumnResize, this);
49489     this.addEvents({
49490          /**
49491              * @event beforepropertychange
49492              * Fires before a property changes (return false to stop?)
49493              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49494              * @param {String} id Record Id
49495              * @param {String} newval New Value
49496          * @param {String} oldval Old Value
49497              */
49498         "beforepropertychange": true,
49499         /**
49500              * @event propertychange
49501              * Fires after a property changes
49502              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49503              * @param {String} id Record Id
49504              * @param {String} newval New Value
49505          * @param {String} oldval Old Value
49506              */
49507         "propertychange": true
49508     });
49509     this.customEditors = this.customEditors || {};
49510 };
49511 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49512     
49513      /**
49514      * @cfg {Object} customEditors map of colnames=> custom editors.
49515      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49516      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49517      * false disables editing of the field.
49518          */
49519     
49520       /**
49521      * @cfg {Object} propertyNames map of property Names to their displayed value
49522          */
49523     
49524     render : function(){
49525         Roo.grid.PropertyGrid.superclass.render.call(this);
49526         this.autoSize.defer(100, this);
49527     },
49528
49529     autoSize : function(){
49530         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49531         if(this.view){
49532             this.view.fitColumns();
49533         }
49534     },
49535
49536     onColumnResize : function(){
49537         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49538         this.autoSize();
49539     },
49540     /**
49541      * Sets the data for the Grid
49542      * accepts a Key => Value object of all the elements avaiable.
49543      * @param {Object} data  to appear in grid.
49544      */
49545     setSource : function(source){
49546         this.store.setSource(source);
49547         //this.autoSize();
49548     },
49549     /**
49550      * Gets all the data from the grid.
49551      * @return {Object} data  data stored in grid
49552      */
49553     getSource : function(){
49554         return this.store.getSource();
49555     }
49556 });/*
49557  * Based on:
49558  * Ext JS Library 1.1.1
49559  * Copyright(c) 2006-2007, Ext JS, LLC.
49560  *
49561  * Originally Released Under LGPL - original licence link has changed is not relivant.
49562  *
49563  * Fork - LGPL
49564  * <script type="text/javascript">
49565  */
49566  
49567 /**
49568  * @class Roo.LoadMask
49569  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49570  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49571  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49572  * element's UpdateManager load indicator and will be destroyed after the initial load.
49573  * @constructor
49574  * Create a new LoadMask
49575  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49576  * @param {Object} config The config object
49577  */
49578 Roo.LoadMask = function(el, config){
49579     this.el = Roo.get(el);
49580     Roo.apply(this, config);
49581     if(this.store){
49582         this.store.on('beforeload', this.onBeforeLoad, this);
49583         this.store.on('load', this.onLoad, this);
49584         this.store.on('loadexception', this.onLoad, this);
49585         this.removeMask = false;
49586     }else{
49587         var um = this.el.getUpdateManager();
49588         um.showLoadIndicator = false; // disable the default indicator
49589         um.on('beforeupdate', this.onBeforeLoad, this);
49590         um.on('update', this.onLoad, this);
49591         um.on('failure', this.onLoad, this);
49592         this.removeMask = true;
49593     }
49594 };
49595
49596 Roo.LoadMask.prototype = {
49597     /**
49598      * @cfg {Boolean} removeMask
49599      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49600      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49601      */
49602     /**
49603      * @cfg {String} msg
49604      * The text to display in a centered loading message box (defaults to 'Loading...')
49605      */
49606     msg : 'Loading...',
49607     /**
49608      * @cfg {String} msgCls
49609      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49610      */
49611     msgCls : 'x-mask-loading',
49612
49613     /**
49614      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49615      * @type Boolean
49616      */
49617     disabled: false,
49618
49619     /**
49620      * Disables the mask to prevent it from being displayed
49621      */
49622     disable : function(){
49623        this.disabled = true;
49624     },
49625
49626     /**
49627      * Enables the mask so that it can be displayed
49628      */
49629     enable : function(){
49630         this.disabled = false;
49631     },
49632
49633     // private
49634     onLoad : function(){
49635         this.el.unmask(this.removeMask);
49636     },
49637
49638     // private
49639     onBeforeLoad : function(){
49640         if(!this.disabled){
49641             this.el.mask(this.msg, this.msgCls);
49642         }
49643     },
49644
49645     // private
49646     destroy : function(){
49647         if(this.store){
49648             this.store.un('beforeload', this.onBeforeLoad, this);
49649             this.store.un('load', this.onLoad, this);
49650             this.store.un('loadexception', this.onLoad, this);
49651         }else{
49652             var um = this.el.getUpdateManager();
49653             um.un('beforeupdate', this.onBeforeLoad, this);
49654             um.un('update', this.onLoad, this);
49655             um.un('failure', this.onLoad, this);
49656         }
49657     }
49658 };/*
49659  * Based on:
49660  * Ext JS Library 1.1.1
49661  * Copyright(c) 2006-2007, Ext JS, LLC.
49662  *
49663  * Originally Released Under LGPL - original licence link has changed is not relivant.
49664  *
49665  * Fork - LGPL
49666  * <script type="text/javascript">
49667  */
49668 Roo.XTemplate = function(){
49669     Roo.XTemplate.superclass.constructor.apply(this, arguments);
49670     var s = this.html;
49671
49672     s = ['<tpl>', s, '</tpl>'].join('');
49673
49674     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
49675
49676     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
49677     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
49678     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
49679     var m, id = 0;
49680     var tpls = [];
49681
49682     while(m = s.match(re)){
49683        var m2 = m[0].match(nameRe);
49684        var m3 = m[0].match(ifRe);
49685        var m4 = m[0].match(execRe);
49686        var exp = null, fn = null, exec = null;
49687        var name = m2 && m2[1] ? m2[1] : '';
49688        if(m3){
49689            exp = m3 && m3[1] ? m3[1] : null;
49690            if(exp){
49691                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
49692            }
49693        }
49694        if(m4){
49695            exp = m4 && m4[1] ? m4[1] : null;
49696            if(exp){
49697                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
49698            }
49699        }
49700        if(name){
49701            switch(name){
49702                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
49703                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
49704                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
49705            }
49706        }
49707        tpls.push({
49708             id: id,
49709             target: name,
49710             exec: exec,
49711             test: fn,
49712             body: m[1]||''
49713         });
49714        s = s.replace(m[0], '{xtpl'+ id + '}');
49715        ++id;
49716     }
49717     for(var i = tpls.length-1; i >= 0; --i){
49718         this.compileTpl(tpls[i]);
49719     }
49720     this.master = tpls[tpls.length-1];
49721     this.tpls = tpls;
49722 };
49723 Roo.extend(Roo.XTemplate, Roo.Template, {
49724
49725     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49726
49727     applySubTemplate : function(id, values, parent){
49728         var t = this.tpls[id];
49729         if(t.test && !t.test.call(this, values, parent)){
49730             return '';
49731         }
49732         if(t.exec && t.exec.call(this, values, parent)){
49733             return '';
49734         }
49735         var vs = t.target ? t.target.call(this, values, parent) : values;
49736         parent = t.target ? values : parent;
49737         if(t.target && vs instanceof Array){
49738             var buf = [];
49739             for(var i = 0, len = vs.length; i < len; i++){
49740                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
49741             }
49742             return buf.join('');
49743         }
49744         return t.compiled.call(this, vs, parent);
49745     },
49746
49747     compileTpl : function(tpl){
49748         var fm = Roo.util.Format;
49749         var useF = this.disableFormats !== true;
49750         var sep = Roo.isGecko ? "+" : ",";
49751         var fn = function(m, name, format, args){
49752             if(name.substr(0, 4) == 'xtpl'){
49753                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
49754             }
49755             var v;
49756             if(name.indexOf('.') != -1){
49757                 v = name;
49758             }else{
49759                 v = "values['" + name + "']";
49760             }
49761             if(format && useF){
49762                 args = args ? ',' + args : "";
49763                 if(format.substr(0, 5) != "this."){
49764                     format = "fm." + format + '(';
49765                 }else{
49766                     format = 'this.call("'+ format.substr(5) + '", ';
49767                     args = ", values";
49768                 }
49769             }else{
49770                 args= ''; format = "("+v+" === undefined ? '' : ";
49771             }
49772             return "'"+ sep + format + v + args + ")"+sep+"'";
49773         };
49774         var body;
49775         // branched to use + in gecko and [].join() in others
49776         if(Roo.isGecko){
49777             body = "tpl.compiled = function(values, parent){ return '" +
49778                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
49779                     "';};";
49780         }else{
49781             body = ["tpl.compiled = function(values, parent){ return ['"];
49782             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
49783             body.push("'].join('');};");
49784             body = body.join('');
49785         }
49786         /** eval:var:zzzzzzz */
49787         eval(body);
49788         return this;
49789     },
49790
49791     applyTemplate : function(values){
49792         return this.master.compiled.call(this, values, {});
49793         var s = this.subs;
49794     },
49795
49796     apply : function(){
49797         return this.applyTemplate.apply(this, arguments);
49798     },
49799
49800     compile : function(){return this;}
49801 });
49802
49803 Roo.XTemplate.from = function(el){
49804     el = Roo.getDom(el);
49805     return new Roo.XTemplate(el.value || el.innerHTML);
49806 };/*
49807  * Original code for Roojs - LGPL
49808  * <script type="text/javascript">
49809  */
49810  
49811 /**
49812  * @class Roo.XComponent
49813  * A delayed Element creator...
49814  * 
49815  * Mypart.xyx = new Roo.XComponent({
49816
49817     parent : 'Mypart.xyz', // empty == document.element.!!
49818     order : '001',
49819     name : 'xxxx'
49820     region : 'xxxx'
49821     disabled : function() {} 
49822      
49823     tree : function() { // return an tree of xtype declared components
49824         var MODULE = this;
49825         return 
49826         {
49827             xtype : 'NestedLayoutPanel',
49828             // technicall
49829         }
49830      ]
49831  *})
49832  * @extends Roo.util.Observable
49833  * @constructor
49834  * @param cfg {Object} configuration of component
49835  * 
49836  */
49837 Roo.XComponent = function(cfg) {
49838     Roo.apply(this, cfg);
49839     this.addEvents({ 
49840         /**
49841              * @event built
49842              * Fires when this the componnt is built
49843              * @param {Roo.XComponent} c the component
49844              */
49845         'built' : true,
49846         /**
49847              * @event buildcomplete
49848              * Fires on the top level element when all elements have been built
49849              * @param {Roo.XComponent} c the top level component.
49850          */
49851         'buildcomplete' : true,
49852         
49853     });
49854     
49855     Roo.XComponent.register(this);
49856     this.modules = false;
49857     this.el = false; // where the layout goes..
49858     
49859     
49860 }
49861 Roo.extend(Roo.XComponent, Roo.util.Observable, {
49862     /**
49863      * @property el
49864      * The created element (with Roo.factory())
49865      * @type {Roo.Layout}
49866      */
49867     el  : false,
49868     
49869     /**
49870      * @property el
49871      * for BC  - use el in new code
49872      * @type {Roo.Layout}
49873      */
49874     panel : false,
49875     
49876     /**
49877      * @property layout
49878      * for BC  - use el in new code
49879      * @type {Roo.Layout}
49880      */
49881     layout : false,
49882     
49883      /**
49884      * @cfg {Function|boolean} disabled
49885      * If this module is disabled by some rule, return true from the funtion
49886      */
49887     disabled : false,
49888     
49889     /**
49890      * @cfg {String} parent 
49891      * Name of parent element which it get xtype added to..
49892      */
49893     parent: false,
49894     
49895     /**
49896      * @cfg {String} order
49897      * Used to set the order in which elements are created (usefull for multiple tabs)
49898      */
49899     
49900     order : false,
49901     /**
49902      * @cfg {String} name
49903      * String to display while loading.
49904      */
49905     name : false,
49906     /**
49907      * @cfg {Array} items
49908      * A single item array - the first element is the root of the tree..
49909      * It's done this way to stay compatible with the Xtype system...
49910      */
49911     items : false,
49912      
49913      
49914     
49915 });
49916
49917 Roo.apply(Roo.XComponent, {
49918     
49919     /**
49920      * @property  buildCompleted
49921      * True when the builder has completed building the interface.
49922      * @type Boolean
49923      */
49924     buildCompleted : false,
49925      
49926     /**
49927      * @property  topModule
49928      * the upper most module - uses document.element as it's constructor.
49929      * @type Object
49930      */
49931      
49932     topModule  : false,
49933       
49934     /**
49935      * @property  modules
49936      * array of modules to be created by registration system.
49937      * @type Roo.XComponent
49938      */
49939     
49940     modules : [],
49941       
49942     
49943     /**
49944      * Register components to be built later.
49945      *
49946      * This solves the following issues
49947      * - Building is not done on page load, but after an authentication process has occured.
49948      * - Interface elements are registered on page load
49949      * - Parent Interface elements may not be loaded before child, so this handles that..
49950      * 
49951      *
49952      * example:
49953      * 
49954      * MyApp.register({
49955           order : '000001',
49956           module : 'Pman.Tab.projectMgr',
49957           region : 'center',
49958           parent : 'Pman.layout',
49959           disabled : false,  // or use a function..
49960         })
49961      
49962      * * @param {Object} details about module
49963      */
49964     register : function(obj) {
49965         this.modules.push(obj);
49966          
49967     },
49968     /**
49969      * convert a string to an object..
49970      * 
49971      */
49972     
49973     toObject : function(str)
49974     {
49975         if (!str || typeof(str) == 'object') {
49976             return str;
49977         }
49978         var ar = str.split('.');
49979         var rt, o;
49980         rt = ar.shift();
49981             /** eval:var:o */
49982         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
49983         if (o === false) {
49984             throw "Module not found : " + str;
49985         }
49986         Roo.each(ar, function(e) {
49987             if (typeof(o[e]) == 'undefined') {
49988                 throw "Module not found : " + str;
49989             }
49990             o = o[e];
49991         });
49992         return o;
49993         
49994     },
49995     
49996     
49997     /**
49998      * move modules into their correct place in the tree..
49999      * 
50000      */
50001     preBuild : function ()
50002     {
50003         
50004         Roo.each(this.modules , function (obj)
50005         {
50006             obj.parent = this.toObject(obj.parent);
50007             
50008             if (!obj.parent) {
50009                 this.topModule = obj;
50010                 return;
50011             }
50012             
50013             if (!obj.parent.modules) {
50014                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50015                     function(o) { return o.order + '' }
50016                 );
50017             }
50018             
50019             obj.parent.modules.add(obj);
50020         }, this);
50021     },
50022     
50023      /**
50024      * make a list of modules to build.
50025      * @return {Array} list of modules. 
50026      */ 
50027     
50028     buildOrder : function()
50029     {
50030         var _this = this;
50031         var cmp = function(a,b) {   
50032             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50033         };
50034         
50035         if (!this.topModule || !this.topModule.modules) {
50036             throw "No top level modules to build";
50037         }
50038        
50039         // make a flat list in order of modules to build.
50040         var mods = [ this.topModule ];
50041         
50042         
50043         // add modules to their parents..
50044         var addMod = function(m) {
50045            // console.log(m.modKey);
50046             
50047             mods.push(m);
50048             if (m.modules) {
50049                 m.modules.keySort('ASC',  cmp );
50050                 m.modules.each(addMod);
50051             }
50052             // not sure if this is used any more..
50053             if (m.finalize) {
50054                 m.finalize.name = m.name + " (clean up) ";
50055                 mods.push(m.finalize);
50056             }
50057             
50058         }
50059         this.topModule.modules.keySort('ASC',  cmp );
50060         this.topModule.modules.each(addMod);
50061         return mods;
50062     },
50063     
50064      /**
50065      * Build the registered modules.
50066      * @param {Object} parent element.
50067      * @param {Function} optional method to call after module has been added.
50068      * 
50069      */ 
50070    
50071     build : function() 
50072     {
50073         
50074         this.preBuild();
50075         var mods = this.buildOrder();
50076       
50077         //this.allmods = mods;
50078         //console.log(mods);
50079         //return;
50080         if (!mods.length) { // should not happen
50081             throw "NO modules!!!";
50082         }
50083         
50084         
50085         
50086         // flash it up as modal - so we store the mask!?
50087         Roo.MessageBox.show({ title: 'loading' });
50088         Roo.MessageBox.show({
50089            title: "Please wait...",
50090            msg: "Building Interface...",
50091            width:450,
50092            progress:true,
50093            closable:false,
50094            modal: false
50095           
50096         });
50097         var total = mods.length;
50098         
50099         var _this = this;
50100         var progressRun = function() {
50101             if (!mods.length) {
50102                 console.log('hide?');
50103                 Roo.MessageBox.hide();
50104                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50105                 return;    
50106             }
50107             
50108             var m = mods.shift();
50109             console.log(m);
50110             if (typeof(m) == 'function') { // not sure if this is supported any more..
50111                 m.call(this);
50112                 return progressRun.defer(10, _this);
50113             } 
50114             
50115             Roo.MessageBox.updateProgress(
50116                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50117                     " of " + total + 
50118                     (m.name ? (' - ' + m.name) : '')
50119                     );
50120             
50121          
50122             
50123             var disabled = (typeof(m.disabled) == 'function') ?
50124                 m.disabled.call(m.module.disabled) : m.disabled;    
50125             
50126             
50127             if (disabled) {
50128                 return progressRun(); // we do not update the display!
50129             }
50130             
50131             if (!m.parent) {
50132                 // it's a top level one..
50133                 var layoutbase = new Ext.BorderLayout(document.body, {
50134                
50135                     center: {
50136                          titlebar: false,
50137                          autoScroll:false,
50138                          closeOnTab: true,
50139                          tabPosition: 'top',
50140                          //resizeTabs: true,
50141                          alwaysShowTabs: true,
50142                          minTabWidth: 140
50143                     }
50144                 });
50145                 var tree = m.tree();
50146                 tree.region = 'center';
50147                 m.el = layoutbase.addxtype(tree);
50148                 m.panel = m.el;
50149                 m.layout = m.panel.layout;    
50150                 return progressRun.defer(10, _this);
50151             }
50152             
50153             var tree = m.tree();
50154             tree.region = tree.region || m.region;
50155             m.el = m.parent.el.addxtype(tree);
50156             m.fireEvent('built', m);
50157             m.panel = m.el;
50158             m.layout = m.panel.layout;    
50159             progressRun.defer(10, _this); 
50160             
50161         }
50162         progressRun.defer(1, _this);
50163      
50164         
50165         
50166     }
50167      
50168    
50169     
50170     
50171 });
50172  //<script type="text/javascript">
50173
50174
50175 /**
50176  * @class Roo.Login
50177  * @extends Roo.LayoutDialog
50178  * A generic Login Dialog..... - only one needed in theory!?!?
50179  *
50180  * Fires XComponent builder on success...
50181  * 
50182  * Sends 
50183  *    username,password, lang = for login actions.
50184  *    check = 1 for periodic checking that sesion is valid.
50185  *    passwordRequest = email request password
50186  *    logout = 1 = to logout
50187  * 
50188  * Affects: (this id="????" elements)
50189  *   loading  (removed) (used to indicate application is loading)
50190  *   loading-mask (hides) (used to hide application when it's building loading)
50191  *   
50192  * 
50193  * Usage: 
50194  *    
50195  * 
50196  * Myapp.login = Roo.Login({
50197      url: xxxx,
50198    
50199      realm : 'Myapp', 
50200      
50201      
50202      method : 'POST',
50203      
50204      
50205      * 
50206  })
50207  * 
50208  * 
50209  * 
50210  **/
50211  
50212 Roo.Login = function(cfg)
50213 {
50214     this.addEvents({
50215         'refreshed' : true,
50216     });
50217     
50218     Roo.apply(this,cfg);
50219     
50220     Roo.onReady(function() {
50221         this.onLoad();
50222     }, this);
50223     // call parent..
50224     
50225    
50226     Roo.Login.superclass.constructor.call(this, this);
50227     //this.addxtype(this.items[0]);
50228     
50229     
50230 }
50231
50232
50233 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50234     
50235     /**
50236      * @cfg {String} method
50237      * Method used to query for login details.
50238      */
50239     
50240     method : 'POST',
50241     /**
50242      * @cfg {String} url
50243      * URL to query login data. - eg. baseURL + '/Login.php'
50244      */
50245     url : '',
50246     
50247     /**
50248      * @property user
50249      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50250      * @type {Object} 
50251      */
50252     user : false,
50253     /**
50254      * @property checkFails
50255      * Number of times we have attempted to get authentication check, and failed.
50256      * @type {Number} 
50257      */
50258     checkFails : 0,
50259       /**
50260      * @property intervalID
50261      * The window interval that does the constant login checking.
50262      * @type {Number} 
50263      */
50264     intervalID : 0,
50265     
50266     
50267     onLoad : function() // called on page load...
50268     {
50269         // load 
50270          
50271         if (Roo.get('loading')) { // clear any loading indicator..
50272             Roo.get('loading').remove();
50273         }
50274         
50275         //this.switchLang('en'); // set the language to english..
50276        
50277         this.check({
50278             success:  function(response, opts)  {  // check successfull...
50279             
50280                 var res = this.processResponse(response);
50281                 this.checkFails =0;
50282                 if (!res.success) { // error!
50283                     this.checkFails = 5;
50284                     //console.log('call failure');
50285                     return this.failure(response,opts);
50286                 }
50287                 
50288                 if (!res.data.id) { // id=0 == login failure.
50289                     return this.show();
50290                 }
50291                 
50292                               
50293                         //console.log(success);
50294                 this.fillAuth(res.data);   
50295                 this.checkFails =0;
50296                 Roo.XComponent.build();
50297             },
50298             failure : this.show
50299         });
50300         
50301     }, 
50302     
50303     
50304     check: function(cfg) // called every so often to refresh cookie etc..
50305     {
50306         if (cfg.again) { // could be undefined..
50307             this.checkFails++;
50308         } else {
50309             this.checkFails = 0;
50310         }
50311         var _this = this;
50312         if (this.sending) {
50313             if ( this.checkFails > 4) {
50314                 Roo.MessageBox.alert("Error",  
50315                     "Error getting authentication status. - try reloading, or wait a while", function() {
50316                         _this.sending = false;
50317                     }); 
50318                 return;
50319             }
50320             cfg.again = true;
50321             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50322             return;
50323         }
50324         this.sending = true;
50325         
50326         Roo.Ajax.request({  
50327             url: this.url,
50328             params: {
50329                 getAuthUser: true
50330             },  
50331             method: this.method,
50332             success:  cfg.success || this.success,
50333             failure : cfg.failure || this.failure,
50334             scope : this,
50335             callCfg : cfg
50336               
50337         });  
50338     }, 
50339     
50340     
50341     logout: function()
50342     {
50343         window.onbeforeunload = function() { }; // false does not work for IE..
50344         this.user = false;
50345         var _this = this;
50346         
50347         Roo.Ajax.request({  
50348             url: this.url,
50349             params: {
50350                 logout: 1
50351             },  
50352             method: 'GET',
50353             failure : function() {
50354                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50355                     document.location = document.location.toString() + '?ts=' + Math.random();
50356                 });
50357                 
50358             },
50359             success : function() {
50360                 _this.user = false;
50361                 this.checkFails =0;
50362                 // fixme..
50363                 document.location = document.location.toString() + '?ts=' + Math.random();
50364             }
50365               
50366               
50367         }); 
50368     },
50369     
50370     processResponse : function (response)
50371     {
50372         var res = '';
50373         try {
50374             res = Roo.decode(response.responseText);
50375             // oops...
50376             if (typeof(res) != 'object') {
50377                 res = { success : false, errorMsg : res, errors : true };
50378             }
50379             if (typeof(res.success) == 'undefined') {
50380                 res.success = false;
50381             }
50382             
50383         } catch(e) {
50384             res = { success : false,  errorMsg : response.responseText, errors : true };
50385         }
50386         return res;
50387     },
50388     
50389     success : function(response, opts)  // check successfull...
50390     {  
50391         this.sending = false;
50392         var res = this.processResponse(response);
50393         if (!res.success) {
50394             return this.failure(response, opts);
50395         }
50396         if (!res.data || !res.data.id) {
50397             return this.failure(response,opts);
50398         }
50399         //console.log(res);
50400         this.fillAuth(res.data);
50401         
50402         this.checkFails =0;
50403         
50404     },
50405     
50406     
50407     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50408     {
50409         this.authUser = -1;
50410         this.sending = false;
50411         var res = this.processResponse(response);
50412         //console.log(res);
50413         if ( this.checkFails > 2) {
50414         
50415             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50416                 "Error getting authentication status. - try reloading"); 
50417             return;
50418         }
50419         opts.callCfg.again = true;
50420         this.check.defer(1000, this, [ opts.callCfg ]);
50421         return;  
50422     },
50423     
50424     
50425     
50426     fillAuth: function(au) {
50427         this.startAuthCheck();
50428         this.authUserId = au.id;
50429         this.authUser = au;
50430         this.lastChecked = new Date();
50431         this.fireEvent('refreshed', au);
50432         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50433         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50434         au.lang = au.lang || 'en';
50435         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50436         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50437         this.switchLang(au.lang );
50438         
50439      
50440         // open system... - -on setyp..
50441         if (this.authUserId  < 0) {
50442             Roo.MessageBox.alert("Warning", 
50443                 "This is an open system - please set up a admin user with a password.");  
50444         }
50445          
50446         //Pman.onload(); // which should do nothing if it's a re-auth result...
50447         
50448              
50449     },
50450     
50451     startAuthCheck : function() // starter for timeout checking..
50452     {
50453         if (this.intervalID) { // timer already in place...
50454             return false;
50455         }
50456         var _this = this;
50457         this.intervalID =  window.setInterval(function() {
50458               _this.check(false);
50459             }, 120000); // every 120 secs = 2mins..
50460         
50461         
50462     },
50463          
50464     
50465     switchLang : function (lang) 
50466     {
50467         _T = typeof(_T) == 'undefined' ? false : _T;
50468           if (!_T || !lang.length) {
50469             return;
50470         }
50471         
50472         if (!_T && lang != 'en') {
50473             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50474             return;
50475         }
50476         
50477         if (typeof(_T.en) == 'undefined') {
50478             _T.en = {};
50479             Roo.apply(_T.en, _T);
50480         }
50481         
50482         if (typeof(_T[lang]) == 'undefined') {
50483             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50484             return;
50485         }
50486         
50487         
50488         Roo.apply(_T, _T[lang]);
50489         // just need to set the text values for everything...
50490         var _this = this;
50491         /* this will not work ...
50492         if (this.form) { 
50493             
50494                
50495             function formLabel(name, val) {
50496                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50497             }
50498             
50499             formLabel('password', "Password"+':');
50500             formLabel('username', "Email Address"+':');
50501             formLabel('lang', "Language"+':');
50502             this.dialog.setTitle("Login");
50503             this.dialog.buttons[0].setText("Forgot Password");
50504             this.dialog.buttons[1].setText("Login");
50505         }
50506         */
50507         
50508         
50509     },
50510     
50511     
50512     title: "Login",
50513     modal: true,
50514     width:  350,
50515     //height: 230,
50516     height: 180,
50517     shadow: true,
50518     minWidth:200,
50519     minHeight:180,
50520     //proxyDrag: true,
50521     closable: false,
50522     draggable: false,
50523     collapsible: false,
50524     resizable: false,
50525     center: {  // needed??
50526         autoScroll:false,
50527         titlebar: false,
50528        // tabPosition: 'top',
50529         hideTabs: true,
50530         closeOnTab: true,
50531         alwaysShowTabs: false
50532     } ,
50533     listeners : {
50534         
50535         show  : function(dlg)
50536         {
50537             //console.log(this);
50538             this.form = this.layout.getRegion('center').activePanel.form;
50539             this.form.dialog = dlg;
50540             this.buttons[0].form = this.form;
50541             this.buttons[0].dialog = dlg
50542             this.buttons[1].form = this.form;
50543             this.buttons[1].dialog = dlg;
50544            
50545            //this.resizeToLogo.defer(1000,this);
50546             // this is all related to resizing for logos..
50547             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50548            //// if (!sz) {
50549              //   this.resizeToLogo.defer(1000,this);
50550              //   return;
50551            // }
50552             //var w = Ext.lib.Dom.getViewWidth() - 100;
50553             //var h = Ext.lib.Dom.getViewHeight() - 100;
50554             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50555             //this.center();
50556             if (this.disabled) {
50557                 this.hide();
50558                 return;
50559             }
50560             
50561             if (this.user.id < 0) { // used for inital setup situations.
50562                 return;
50563             }
50564             
50565             if (this.intervalID) {
50566                 // remove the timer
50567                 window.clearInterval(this.intervalID);
50568                 this.intervalID = false;
50569             }
50570             
50571             
50572             if (Roo.get('loading')) {
50573                 Roo.get('loading').remove();
50574             }
50575             if (Roo.get('loading-mask')) {
50576                 Roo.get('loading-mask').hide();
50577             }
50578             
50579             //incomming._node = tnode;
50580             this.form.reset();
50581             //this.dialog.modal = !modal;
50582             //this.dialog.show();
50583             this.el.unmask(); 
50584             
50585             
50586             this.form.setValues({
50587                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50588                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50589             });
50590             
50591             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50592             if (this.form.findField('username').getValue().length > 0 ){
50593                 this.form.findField('password').focus();
50594             } else {
50595                this.form.findField('username').focus();
50596             }
50597     
50598         }
50599     },
50600     items : [
50601          {
50602        
50603             xtype : 'ContentPanel',
50604             xns : Roo,
50605             region: 'center',
50606             fitToFrame : true,
50607             
50608             items : [
50609     
50610                 {
50611                
50612                     xtype : 'Form',
50613                     xns : Roo.form,
50614                     labelWidth: 100,
50615                     style : 'margin: 10px;',
50616                     
50617                     listeners : {
50618                         actionfailed : function(f, act) {
50619                             // form can return { errors: .... }
50620                                 
50621                             //act.result.errors // invalid form element list...
50622                             //act.result.errorMsg// invalid form element list...
50623                             
50624                             this.dialog.el.unmask();
50625                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50626                                         "Login failed - communication error - try again.");
50627                                       
50628                         },
50629                         actioncomplete: function(re, act) {
50630                              
50631                             Roo.state.Manager.set(
50632                                 this.dialog.realm + '.username',  
50633                                     this.findField('username').getValue()
50634                             );
50635                             Roo.state.Manager.set(
50636                                 this.dialog.realm + '.lang',  
50637                                 this.findField('lang').getValue() 
50638                             );
50639                             
50640                             this.dialog.fillAuth(act.result.data);
50641                               
50642                             this.dialog.hide();
50643                             
50644                             if (Roo.get('loading-mask')) {
50645                                 Roo.get('loading-mask').show();
50646                             }
50647                             Roo.XComponent.build();
50648                             
50649                              
50650                             
50651                         }
50652                     },
50653                     items : [
50654                         {
50655                             xtype : 'TextField',
50656                             xns : Roo.form,
50657                             fieldLabel: "Email Address",
50658                             name: 'username',
50659                             width:200,
50660                             autoCreate : {tag: "input", type: "text", size: "20"}
50661                         },
50662                         {
50663                             xtype : 'TextField',
50664                             xns : Roo.form,
50665                             fieldLabel: "Password",
50666                             inputType: 'password',
50667                             name: 'password',
50668                             width:200,
50669                             autoCreate : {tag: "input", type: "text", size: "20"},
50670                             listeners : {
50671                                 specialkey : function(e,ev) {
50672                                     if (ev.keyCode == 13) {
50673                                         this.form.dialog.el.mask("Logging in");
50674                                         this.form.doAction('submit', {
50675                                             url: this.form.dialog.url,
50676                                             method: this.form.dialog.method,
50677                                         });
50678                                     }
50679                                 }
50680                             }  
50681                         },
50682                         {
50683                             xtype : 'ComboBox',
50684                             xns : Roo.form,
50685                             fieldLabel: "Language",
50686                             name : 'langdisp',
50687                             store: {
50688                                 xtype : 'SimpleStore',
50689                                 fields: ['lang', 'ldisp'],
50690                                 data : [
50691                                     [ 'en', 'English' ],
50692                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
50693                                     [ 'zh_CN', '\u7C21\u4E2D' ]
50694                                 ]
50695                             },
50696                             
50697                             valueField : 'lang',
50698                             hiddenName:  'lang',
50699                             width: 200,
50700                             displayField:'ldisp',
50701                             typeAhead: false,
50702                             editable: false,
50703                             mode: 'local',
50704                             triggerAction: 'all',
50705                             emptyText:'Select a Language...',
50706                             selectOnFocus:true,
50707                             listeners : {
50708                                 select :  function(cb, rec, ix) {
50709                                     this.form.switchLang(rec.data.lang);
50710                                 }
50711                             }
50712                         
50713                         }
50714                     ]
50715                 }
50716                   
50717                 
50718             ]
50719         }
50720     ],
50721     buttons : [
50722         {
50723             xtype : 'Button',
50724             xns : 'Roo',
50725             text : "Forgot Password",
50726             listeners : {
50727                 click : function() {
50728                     //console.log(this);
50729                     var n = this.form.findField('username').getValue();
50730                     if (!n.length) {
50731                         Roo.MessageBox.alert("Error", "Fill in your email address");
50732                         return;
50733                     }
50734                     Roo.Ajax.request({
50735                         url: this.dialog.url,
50736                         params: {
50737                             passwordRequest: n
50738                         },
50739                         method: this.dialog.method,
50740                         success:  function(response, opts)  {  // check successfull...
50741                         
50742                             var res = this.dialog.processResponse(response);
50743                             if (!res.success) { // error!
50744                                Roo.MessageBox.alert("Error" ,
50745                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
50746                                return;
50747                             }
50748                             Roo.MessageBox.alert("Notice" ,
50749                                 "Please check you email for the Password Reset message");
50750                         },
50751                         failure : function() {
50752                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
50753                         }
50754                         
50755                     });
50756                 }
50757             }
50758         },
50759         {
50760             xtype : 'Button',
50761             xns : 'Roo',
50762             text : "Login",
50763             listeners : {
50764                 
50765                 click : function () {
50766                         
50767                     this.dialog.el.mask("Logging in");
50768                     this.form.doAction('submit', {
50769                             url: this.dialog.url,
50770                             method: this.dialog.method
50771                     });
50772                 }
50773             }
50774         }
50775     ]
50776   
50777   
50778 })
50779  
50780
50781
50782