Fix #6630 - making upload work with card uploader
[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         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * 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.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * 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]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     },
1019     /**
1020      * equals
1021      * @param {Array} o The array to compare to
1022      * @returns {Boolean} true if the same
1023      */
1024     equals : function(b)
1025     {
1026         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1027         if (this === b) {
1028             return true;
1029          }
1030         if (b == null) {
1031             return false;
1032         }
1033         if (this.length !== b.length) {
1034             return false;
1035         }
1036       
1037         // sort?? a.sort().equals(b.sort());
1038       
1039         for (var i = 0; i < this.length; ++i) {
1040             if (this[i] !== b[i]) {
1041                 return false;
1042             }
1043         }
1044         return true;
1045     }
1046 });
1047
1048
1049  
1050 /*
1051  * Based on:
1052  * Ext JS Library 1.1.1
1053  * Copyright(c) 2006-2007, Ext JS, LLC.
1054  *
1055  * Originally Released Under LGPL - original licence link has changed is not relivant.
1056  *
1057  * Fork - LGPL
1058  * <script type="text/javascript">
1059  */
1060
1061 /**
1062  * @class Date
1063  *
1064  * The date parsing and format syntax is a subset of
1065  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1066  * supported will provide results equivalent to their PHP versions.
1067  *
1068  * Following is the list of all currently supported formats:
1069  *<pre>
1070 Sample date:
1071 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1072
1073 Format  Output      Description
1074 ------  ----------  --------------------------------------------------------------
1075   d      10         Day of the month, 2 digits with leading zeros
1076   D      Wed        A textual representation of a day, three letters
1077   j      10         Day of the month without leading zeros
1078   l      Wednesday  A full textual representation of the day of the week
1079   S      th         English ordinal day of month suffix, 2 chars (use with j)
1080   w      3          Numeric representation of the day of the week
1081   z      9          The julian date, or day of the year (0-365)
1082   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1083   F      January    A full textual representation of the month
1084   m      01         Numeric representation of a month, with leading zeros
1085   M      Jan        Month name abbreviation, three letters
1086   n      1          Numeric representation of a month, without leading zeros
1087   t      31         Number of days in the given month
1088   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1089   Y      2007       A full numeric representation of a year, 4 digits
1090   y      07         A two digit representation of a year
1091   a      pm         Lowercase Ante meridiem and Post meridiem
1092   A      PM         Uppercase Ante meridiem and Post meridiem
1093   g      3          12-hour format of an hour without leading zeros
1094   G      15         24-hour format of an hour without leading zeros
1095   h      03         12-hour format of an hour with leading zeros
1096   H      15         24-hour format of an hour with leading zeros
1097   i      05         Minutes with leading zeros
1098   s      01         Seconds, with leading zeros
1099   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1100   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1101   T      CST        Timezone setting of the machine running the code
1102   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1103 </pre>
1104  *
1105  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1106  * <pre><code>
1107 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1108 document.write(dt.format('Y-m-d'));                         //2007-01-10
1109 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1110 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
1111  </code></pre>
1112  *
1113  * Here are some standard date/time patterns that you might find helpful.  They
1114  * are not part of the source of Date.js, but to use them you can simply copy this
1115  * block of code into any script that is included after Date.js and they will also become
1116  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1117  * <pre><code>
1118 Date.patterns = {
1119     ISO8601Long:"Y-m-d H:i:s",
1120     ISO8601Short:"Y-m-d",
1121     ShortDate: "n/j/Y",
1122     LongDate: "l, F d, Y",
1123     FullDateTime: "l, F d, Y g:i:s A",
1124     MonthDay: "F d",
1125     ShortTime: "g:i A",
1126     LongTime: "g:i:s A",
1127     SortableDateTime: "Y-m-d\\TH:i:s",
1128     UniversalSortableDateTime: "Y-m-d H:i:sO",
1129     YearMonth: "F, Y"
1130 };
1131 </code></pre>
1132  *
1133  * Example usage:
1134  * <pre><code>
1135 var dt = new Date();
1136 document.write(dt.format(Date.patterns.ShortDate));
1137  </code></pre>
1138  */
1139
1140 /*
1141  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1142  * They generate precompiled functions from date formats instead of parsing and
1143  * processing the pattern every time you format a date.  These functions are available
1144  * on every Date object (any javascript function).
1145  *
1146  * The original article and download are here:
1147  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1148  *
1149  */
1150  
1151  
1152  // was in core
1153 /**
1154  Returns the number of milliseconds between this date and date
1155  @param {Date} date (optional) Defaults to now
1156  @return {Number} The diff in milliseconds
1157  @member Date getElapsed
1158  */
1159 Date.prototype.getElapsed = function(date) {
1160         return Math.abs((date || new Date()).getTime()-this.getTime());
1161 };
1162 // was in date file..
1163
1164
1165 // private
1166 Date.parseFunctions = {count:0};
1167 // private
1168 Date.parseRegexes = [];
1169 // private
1170 Date.formatFunctions = {count:0};
1171
1172 // private
1173 Date.prototype.dateFormat = function(format) {
1174     if (Date.formatFunctions[format] == null) {
1175         Date.createNewFormat(format);
1176     }
1177     var func = Date.formatFunctions[format];
1178     return this[func]();
1179 };
1180
1181
1182 /**
1183  * Formats a date given the supplied format string
1184  * @param {String} format The format string
1185  * @return {String} The formatted date
1186  * @method
1187  */
1188 Date.prototype.format = Date.prototype.dateFormat;
1189
1190 // private
1191 Date.createNewFormat = function(format) {
1192     var funcName = "format" + Date.formatFunctions.count++;
1193     Date.formatFunctions[format] = funcName;
1194     var code = "Date.prototype." + funcName + " = function(){return ";
1195     var special = false;
1196     var ch = '';
1197     for (var i = 0; i < format.length; ++i) {
1198         ch = format.charAt(i);
1199         if (!special && ch == "\\") {
1200             special = true;
1201         }
1202         else if (special) {
1203             special = false;
1204             code += "'" + String.escape(ch) + "' + ";
1205         }
1206         else {
1207             code += Date.getFormatCode(ch);
1208         }
1209     }
1210     /** eval:var:zzzzzzzzzzzzz */
1211     eval(code.substring(0, code.length - 3) + ";}");
1212 };
1213
1214 // private
1215 Date.getFormatCode = function(character) {
1216     switch (character) {
1217     case "d":
1218         return "String.leftPad(this.getDate(), 2, '0') + ";
1219     case "D":
1220         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1221     case "j":
1222         return "this.getDate() + ";
1223     case "l":
1224         return "Date.dayNames[this.getDay()] + ";
1225     case "S":
1226         return "this.getSuffix() + ";
1227     case "w":
1228         return "this.getDay() + ";
1229     case "z":
1230         return "this.getDayOfYear() + ";
1231     case "W":
1232         return "this.getWeekOfYear() + ";
1233     case "F":
1234         return "Date.monthNames[this.getMonth()] + ";
1235     case "m":
1236         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1237     case "M":
1238         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1239     case "n":
1240         return "(this.getMonth() + 1) + ";
1241     case "t":
1242         return "this.getDaysInMonth() + ";
1243     case "L":
1244         return "(this.isLeapYear() ? 1 : 0) + ";
1245     case "Y":
1246         return "this.getFullYear() + ";
1247     case "y":
1248         return "('' + this.getFullYear()).substring(2, 4) + ";
1249     case "a":
1250         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1251     case "A":
1252         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1253     case "g":
1254         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1255     case "G":
1256         return "this.getHours() + ";
1257     case "h":
1258         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1259     case "H":
1260         return "String.leftPad(this.getHours(), 2, '0') + ";
1261     case "i":
1262         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1263     case "s":
1264         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1265     case "O":
1266         return "this.getGMTOffset() + ";
1267     case "P":
1268         return "this.getGMTColonOffset() + ";
1269     case "T":
1270         return "this.getTimezone() + ";
1271     case "Z":
1272         return "(this.getTimezoneOffset() * -60) + ";
1273     default:
1274         return "'" + String.escape(character) + "' + ";
1275     }
1276 };
1277
1278 /**
1279  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1280  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1281  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1282  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1283  * string or the parse operation will fail.
1284  * Example Usage:
1285 <pre><code>
1286 //dt = Fri May 25 2007 (current date)
1287 var dt = new Date();
1288
1289 //dt = Thu May 25 2006 (today's month/day in 2006)
1290 dt = Date.parseDate("2006", "Y");
1291
1292 //dt = Sun Jan 15 2006 (all date parts specified)
1293 dt = Date.parseDate("2006-1-15", "Y-m-d");
1294
1295 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1296 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1297 </code></pre>
1298  * @param {String} input The unparsed date as a string
1299  * @param {String} format The format the date is in
1300  * @return {Date} The parsed date
1301  * @static
1302  */
1303 Date.parseDate = function(input, format) {
1304     if (Date.parseFunctions[format] == null) {
1305         Date.createParser(format);
1306     }
1307     var func = Date.parseFunctions[format];
1308     return Date[func](input);
1309 };
1310 /**
1311  * @private
1312  */
1313
1314 Date.createParser = function(format) {
1315     var funcName = "parse" + Date.parseFunctions.count++;
1316     var regexNum = Date.parseRegexes.length;
1317     var currentGroup = 1;
1318     Date.parseFunctions[format] = funcName;
1319
1320     var code = "Date." + funcName + " = function(input){\n"
1321         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1322         + "var d = new Date();\n"
1323         + "y = d.getFullYear();\n"
1324         + "m = d.getMonth();\n"
1325         + "d = d.getDate();\n"
1326         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1327         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1328         + "if (results && results.length > 0) {";
1329     var regex = "";
1330
1331     var special = false;
1332     var ch = '';
1333     for (var i = 0; i < format.length; ++i) {
1334         ch = format.charAt(i);
1335         if (!special && ch == "\\") {
1336             special = true;
1337         }
1338         else if (special) {
1339             special = false;
1340             regex += String.escape(ch);
1341         }
1342         else {
1343             var obj = Date.formatCodeToRegex(ch, currentGroup);
1344             currentGroup += obj.g;
1345             regex += obj.s;
1346             if (obj.g && obj.c) {
1347                 code += obj.c;
1348             }
1349         }
1350     }
1351
1352     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1353         + "{v = new Date(y, m, d, h, i, s);}\n"
1354         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1355         + "{v = new Date(y, m, d, h, i);}\n"
1356         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1357         + "{v = new Date(y, m, d, h);}\n"
1358         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1359         + "{v = new Date(y, m, d);}\n"
1360         + "else if (y >= 0 && m >= 0)\n"
1361         + "{v = new Date(y, m);}\n"
1362         + "else if (y >= 0)\n"
1363         + "{v = new Date(y);}\n"
1364         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1365         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1366         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1367         + ";}";
1368
1369     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1370     /** eval:var:zzzzzzzzzzzzz */
1371     eval(code);
1372 };
1373
1374 // private
1375 Date.formatCodeToRegex = function(character, currentGroup) {
1376     switch (character) {
1377     case "D":
1378         return {g:0,
1379         c:null,
1380         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1381     case "j":
1382         return {g:1,
1383             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // day of month without leading zeroes
1385     case "d":
1386         return {g:1,
1387             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{2})"}; // day of month with leading zeroes
1389     case "l":
1390         return {g:0,
1391             c:null,
1392             s:"(?:" + Date.dayNames.join("|") + ")"};
1393     case "S":
1394         return {g:0,
1395             c:null,
1396             s:"(?:st|nd|rd|th)"};
1397     case "w":
1398         return {g:0,
1399             c:null,
1400             s:"\\d"};
1401     case "z":
1402         return {g:0,
1403             c:null,
1404             s:"(?:\\d{1,3})"};
1405     case "W":
1406         return {g:0,
1407             c:null,
1408             s:"(?:\\d{2})"};
1409     case "F":
1410         return {g:1,
1411             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1412             s:"(" + Date.monthNames.join("|") + ")"};
1413     case "M":
1414         return {g:1,
1415             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1416             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1417     case "n":
1418         return {g:1,
1419             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1420             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1421     case "m":
1422         return {g:1,
1423             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1424             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1425     case "t":
1426         return {g:0,
1427             c:null,
1428             s:"\\d{1,2}"};
1429     case "L":
1430         return {g:0,
1431             c:null,
1432             s:"(?:1|0)"};
1433     case "Y":
1434         return {g:1,
1435             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{4})"};
1437     case "y":
1438         return {g:1,
1439             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1440                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1441             s:"(\\d{1,2})"};
1442     case "a":
1443         return {g:1,
1444             c:"if (results[" + currentGroup + "] == 'am') {\n"
1445                 + "if (h == 12) { h = 0; }\n"
1446                 + "} else { if (h < 12) { h += 12; }}",
1447             s:"(am|pm)"};
1448     case "A":
1449         return {g:1,
1450             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1451                 + "if (h == 12) { h = 0; }\n"
1452                 + "} else { if (h < 12) { h += 12; }}",
1453             s:"(AM|PM)"};
1454     case "g":
1455     case "G":
1456         return {g:1,
1457             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1458             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1459     case "h":
1460     case "H":
1461         return {g:1,
1462             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1464     case "i":
1465         return {g:1,
1466             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1467             s:"(\\d{2})"};
1468     case "s":
1469         return {g:1,
1470             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1471             s:"(\\d{2})"};
1472     case "O":
1473         return {g:1,
1474             c:[
1475                 "o = results[", currentGroup, "];\n",
1476                 "var sn = o.substring(0,1);\n", // get + / - sign
1477                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1478                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1479                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1480                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1481             ].join(""),
1482             s:"([+\-]\\d{2,4})"};
1483     
1484     
1485     case "P":
1486         return {g:1,
1487                 c:[
1488                    "o = results[", currentGroup, "];\n",
1489                    "var sn = o.substring(0,1);\n",
1490                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1491                    "var mn = o.substring(4,6) % 60;\n",
1492                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1493                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1494             ].join(""),
1495             s:"([+\-]\\d{4})"};
1496     case "T":
1497         return {g:0,
1498             c:null,
1499             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1500     case "Z":
1501         return {g:1,
1502             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1503                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1504             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1505     default:
1506         return {g:0,
1507             c:null,
1508             s:String.escape(character)};
1509     }
1510 };
1511
1512 /**
1513  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1514  * @return {String} The abbreviated timezone name (e.g. 'CST')
1515  */
1516 Date.prototype.getTimezone = function() {
1517     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1518 };
1519
1520 /**
1521  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1522  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1523  */
1524 Date.prototype.getGMTOffset = function() {
1525     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1526         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1527         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1528 };
1529
1530 /**
1531  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1532  * @return {String} 2-characters representing hours and 2-characters representing minutes
1533  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1534  */
1535 Date.prototype.getGMTColonOffset = function() {
1536         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538                 + ":"
1539                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1540 }
1541
1542 /**
1543  * Get the numeric day number of the year, adjusted for leap year.
1544  * @return {Number} 0 through 364 (365 in leap years)
1545  */
1546 Date.prototype.getDayOfYear = function() {
1547     var num = 0;
1548     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1549     for (var i = 0; i < this.getMonth(); ++i) {
1550         num += Date.daysInMonth[i];
1551     }
1552     return num + this.getDate() - 1;
1553 };
1554
1555 /**
1556  * Get the string representation of the numeric week number of the year
1557  * (equivalent to the format specifier 'W').
1558  * @return {String} '00' through '52'
1559  */
1560 Date.prototype.getWeekOfYear = function() {
1561     // Skip to Thursday of this week
1562     var now = this.getDayOfYear() + (4 - this.getDay());
1563     // Find the first Thursday of the year
1564     var jan1 = new Date(this.getFullYear(), 0, 1);
1565     var then = (7 - jan1.getDay() + 4);
1566     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1567 };
1568
1569 /**
1570  * Whether or not the current date is in a leap year.
1571  * @return {Boolean} True if the current date is in a leap year, else false
1572  */
1573 Date.prototype.isLeapYear = function() {
1574     var year = this.getFullYear();
1575     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1576 };
1577
1578 /**
1579  * Get the first day of the current month, adjusted for leap year.  The returned value
1580  * is the numeric day index within the week (0-6) which can be used in conjunction with
1581  * the {@link #monthNames} array to retrieve the textual day name.
1582  * Example:
1583  *<pre><code>
1584 var dt = new Date('1/10/2007');
1585 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1586 </code></pre>
1587  * @return {Number} The day number (0-6)
1588  */
1589 Date.prototype.getFirstDayOfMonth = function() {
1590     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1591     return (day < 0) ? (day + 7) : day;
1592 };
1593
1594 /**
1595  * Get the last day of the current month, adjusted for leap year.  The returned value
1596  * is the numeric day index within the week (0-6) which can be used in conjunction with
1597  * the {@link #monthNames} array to retrieve the textual day name.
1598  * Example:
1599  *<pre><code>
1600 var dt = new Date('1/10/2007');
1601 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1602 </code></pre>
1603  * @return {Number} The day number (0-6)
1604  */
1605 Date.prototype.getLastDayOfMonth = function() {
1606     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1607     return (day < 0) ? (day + 7) : day;
1608 };
1609
1610
1611 /**
1612  * Get the first date of this date's month
1613  * @return {Date}
1614  */
1615 Date.prototype.getFirstDateOfMonth = function() {
1616     return new Date(this.getFullYear(), this.getMonth(), 1);
1617 };
1618
1619 /**
1620  * Get the last date of this date's month
1621  * @return {Date}
1622  */
1623 Date.prototype.getLastDateOfMonth = function() {
1624     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1625 };
1626 /**
1627  * Get the number of days in the current month, adjusted for leap year.
1628  * @return {Number} The number of days in the month
1629  */
1630 Date.prototype.getDaysInMonth = function() {
1631     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1632     return Date.daysInMonth[this.getMonth()];
1633 };
1634
1635 /**
1636  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1637  * @return {String} 'st, 'nd', 'rd' or 'th'
1638  */
1639 Date.prototype.getSuffix = function() {
1640     switch (this.getDate()) {
1641         case 1:
1642         case 21:
1643         case 31:
1644             return "st";
1645         case 2:
1646         case 22:
1647             return "nd";
1648         case 3:
1649         case 23:
1650             return "rd";
1651         default:
1652             return "th";
1653     }
1654 };
1655
1656 // private
1657 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1658
1659 /**
1660  * An array of textual month names.
1661  * Override these values for international dates, for example...
1662  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1663  * @type Array
1664  * @static
1665  */
1666 Date.monthNames =
1667    ["January",
1668     "February",
1669     "March",
1670     "April",
1671     "May",
1672     "June",
1673     "July",
1674     "August",
1675     "September",
1676     "October",
1677     "November",
1678     "December"];
1679
1680 /**
1681  * An array of textual day names.
1682  * Override these values for international dates, for example...
1683  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1684  * @type Array
1685  * @static
1686  */
1687 Date.dayNames =
1688    ["Sunday",
1689     "Monday",
1690     "Tuesday",
1691     "Wednesday",
1692     "Thursday",
1693     "Friday",
1694     "Saturday"];
1695
1696 // private
1697 Date.y2kYear = 50;
1698 // private
1699 Date.monthNumbers = {
1700     Jan:0,
1701     Feb:1,
1702     Mar:2,
1703     Apr:3,
1704     May:4,
1705     Jun:5,
1706     Jul:6,
1707     Aug:7,
1708     Sep:8,
1709     Oct:9,
1710     Nov:10,
1711     Dec:11};
1712
1713 /**
1714  * Creates and returns a new Date instance with the exact same date value as the called instance.
1715  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1716  * variable will also be changed.  When the intention is to create a new variable that will not
1717  * modify the original instance, you should create a clone.
1718  *
1719  * Example of correctly cloning a date:
1720  * <pre><code>
1721 //wrong way:
1722 var orig = new Date('10/1/2006');
1723 var copy = orig;
1724 copy.setDate(5);
1725 document.write(orig);  //returns 'Thu Oct 05 2006'!
1726
1727 //correct way:
1728 var orig = new Date('10/1/2006');
1729 var copy = orig.clone();
1730 copy.setDate(5);
1731 document.write(orig);  //returns 'Thu Oct 01 2006'
1732 </code></pre>
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.clone = function() {
1736         return new Date(this.getTime());
1737 };
1738
1739 /**
1740  * Clears any time information from this date
1741  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1742  @return {Date} this or the clone
1743  */
1744 Date.prototype.clearTime = function(clone){
1745     if(clone){
1746         return this.clone().clearTime();
1747     }
1748     this.setHours(0);
1749     this.setMinutes(0);
1750     this.setSeconds(0);
1751     this.setMilliseconds(0);
1752     return this;
1753 };
1754
1755 // private
1756 // safari setMonth is broken -- check that this is only donw once...
1757 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1758     Date.brokenSetMonth = Date.prototype.setMonth;
1759         Date.prototype.setMonth = function(num){
1760                 if(num <= -1){
1761                         var n = Math.ceil(-num);
1762                         var back_year = Math.ceil(n/12);
1763                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1764                         this.setFullYear(this.getFullYear() - back_year);
1765                         return Date.brokenSetMonth.call(this, month);
1766                 } else {
1767                         return Date.brokenSetMonth.apply(this, arguments);
1768                 }
1769         };
1770 }
1771
1772 /** Date interval constant 
1773 * @static 
1774 * @type String */
1775 Date.MILLI = "ms";
1776 /** Date interval constant 
1777 * @static 
1778 * @type String */
1779 Date.SECOND = "s";
1780 /** Date interval constant 
1781 * @static 
1782 * @type String */
1783 Date.MINUTE = "mi";
1784 /** Date interval constant 
1785 * @static 
1786 * @type String */
1787 Date.HOUR = "h";
1788 /** Date interval constant 
1789 * @static 
1790 * @type String */
1791 Date.DAY = "d";
1792 /** Date interval constant 
1793 * @static 
1794 * @type String */
1795 Date.MONTH = "mo";
1796 /** Date interval constant 
1797 * @static 
1798 * @type String */
1799 Date.YEAR = "y";
1800
1801 /**
1802  * Provides a convenient method of performing basic date arithmetic.  This method
1803  * does not modify the Date instance being called - it creates and returns
1804  * a new Date instance containing the resulting date value.
1805  *
1806  * Examples:
1807  * <pre><code>
1808 //Basic usage:
1809 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1810 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1811
1812 //Negative values will subtract correctly:
1813 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1814 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1815
1816 //You can even chain several calls together in one line!
1817 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1818 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1819  </code></pre>
1820  *
1821  * @param {String} interval   A valid date interval enum value
1822  * @param {Number} value      The amount to add to the current date
1823  * @return {Date} The new Date instance
1824  */
1825 Date.prototype.add = function(interval, value){
1826   var d = this.clone();
1827   if (!interval || value === 0) { return d; }
1828   switch(interval.toLowerCase()){
1829     case Date.MILLI:
1830       d.setMilliseconds(this.getMilliseconds() + value);
1831       break;
1832     case Date.SECOND:
1833       d.setSeconds(this.getSeconds() + value);
1834       break;
1835     case Date.MINUTE:
1836       d.setMinutes(this.getMinutes() + value);
1837       break;
1838     case Date.HOUR:
1839       d.setHours(this.getHours() + value);
1840       break;
1841     case Date.DAY:
1842       d.setDate(this.getDate() + value);
1843       break;
1844     case Date.MONTH:
1845       var day = this.getDate();
1846       if(day > 28){
1847           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1848       }
1849       d.setDate(day);
1850       d.setMonth(this.getMonth() + value);
1851       break;
1852     case Date.YEAR:
1853       d.setFullYear(this.getFullYear() + value);
1854       break;
1855   }
1856   return d;
1857 };
1858 /*
1859  * Based on:
1860  * Ext JS Library 1.1.1
1861  * Copyright(c) 2006-2007, Ext JS, LLC.
1862  *
1863  * Originally Released Under LGPL - original licence link has changed is not relivant.
1864  *
1865  * Fork - LGPL
1866  * <script type="text/javascript">
1867  */
1868
1869 /**
1870  * @class Roo.lib.Dom
1871  * @static
1872  * 
1873  * Dom utils (from YIU afaik)
1874  * 
1875  **/
1876 Roo.lib.Dom = {
1877     /**
1878      * Get the view width
1879      * @param {Boolean} full True will get the full document, otherwise it's the view width
1880      * @return {Number} The width
1881      */
1882      
1883     getViewWidth : function(full) {
1884         return full ? this.getDocumentWidth() : this.getViewportWidth();
1885     },
1886     /**
1887      * Get the view height
1888      * @param {Boolean} full True will get the full document, otherwise it's the view height
1889      * @return {Number} The height
1890      */
1891     getViewHeight : function(full) {
1892         return full ? this.getDocumentHeight() : this.getViewportHeight();
1893     },
1894
1895     getDocumentHeight: function() {
1896         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1897         return Math.max(scrollHeight, this.getViewportHeight());
1898     },
1899
1900     getDocumentWidth: function() {
1901         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1902         return Math.max(scrollWidth, this.getViewportWidth());
1903     },
1904
1905     getViewportHeight: function() {
1906         var height = self.innerHeight;
1907         var mode = document.compatMode;
1908
1909         if ((mode || Roo.isIE) && !Roo.isOpera) {
1910             height = (mode == "CSS1Compat") ?
1911                      document.documentElement.clientHeight :
1912                      document.body.clientHeight;
1913         }
1914
1915         return height;
1916     },
1917
1918     getViewportWidth: function() {
1919         var width = self.innerWidth;
1920         var mode = document.compatMode;
1921
1922         if (mode || Roo.isIE) {
1923             width = (mode == "CSS1Compat") ?
1924                     document.documentElement.clientWidth :
1925                     document.body.clientWidth;
1926         }
1927         return width;
1928     },
1929
1930     isAncestor : function(p, c) {
1931         p = Roo.getDom(p);
1932         c = Roo.getDom(c);
1933         if (!p || !c) {
1934             return false;
1935         }
1936
1937         if (p.contains && !Roo.isSafari) {
1938             return p.contains(c);
1939         } else if (p.compareDocumentPosition) {
1940             return !!(p.compareDocumentPosition(c) & 16);
1941         } else {
1942             var parent = c.parentNode;
1943             while (parent) {
1944                 if (parent == p) {
1945                     return true;
1946                 }
1947                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1948                     return false;
1949                 }
1950                 parent = parent.parentNode;
1951             }
1952             return false;
1953         }
1954     },
1955
1956     getRegion : function(el) {
1957         return Roo.lib.Region.getRegion(el);
1958     },
1959
1960     getY : function(el) {
1961         return this.getXY(el)[1];
1962     },
1963
1964     getX : function(el) {
1965         return this.getXY(el)[0];
1966     },
1967
1968     getXY : function(el) {
1969         var p, pe, b, scroll, bd = document.body;
1970         el = Roo.getDom(el);
1971         var fly = Roo.lib.AnimBase.fly;
1972         if (el.getBoundingClientRect) {
1973             b = el.getBoundingClientRect();
1974             scroll = fly(document).getScroll();
1975             return [b.left + scroll.left, b.top + scroll.top];
1976         }
1977         var x = 0, y = 0;
1978
1979         p = el;
1980
1981         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1982
1983         while (p) {
1984
1985             x += p.offsetLeft;
1986             y += p.offsetTop;
1987
1988             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1989                 hasAbsolute = true;
1990             }
1991
1992             if (Roo.isGecko) {
1993                 pe = fly(p);
1994
1995                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1996                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1997
1998
1999                 x += bl;
2000                 y += bt;
2001
2002
2003                 if (p != el && pe.getStyle('overflow') != 'visible') {
2004                     x += bl;
2005                     y += bt;
2006                 }
2007             }
2008             p = p.offsetParent;
2009         }
2010
2011         if (Roo.isSafari && hasAbsolute) {
2012             x -= bd.offsetLeft;
2013             y -= bd.offsetTop;
2014         }
2015
2016         if (Roo.isGecko && !hasAbsolute) {
2017             var dbd = fly(bd);
2018             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2019             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2020         }
2021
2022         p = el.parentNode;
2023         while (p && p != bd) {
2024             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2025                 x -= p.scrollLeft;
2026                 y -= p.scrollTop;
2027             }
2028             p = p.parentNode;
2029         }
2030         return [x, y];
2031     },
2032  
2033   
2034
2035
2036     setXY : function(el, xy) {
2037         el = Roo.fly(el, '_setXY');
2038         el.position();
2039         var pts = el.translatePoints(xy);
2040         if (xy[0] !== false) {
2041             el.dom.style.left = pts.left + "px";
2042         }
2043         if (xy[1] !== false) {
2044             el.dom.style.top = pts.top + "px";
2045         }
2046     },
2047
2048     setX : function(el, x) {
2049         this.setXY(el, [x, false]);
2050     },
2051
2052     setY : function(el, y) {
2053         this.setXY(el, [false, y]);
2054     }
2055 };
2056 /*
2057  * Portions of this file are based on pieces of Yahoo User Interface Library
2058  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2059  * YUI licensed under the BSD License:
2060  * http://developer.yahoo.net/yui/license.txt
2061  * <script type="text/javascript">
2062  *
2063  */
2064
2065 Roo.lib.Event = function() {
2066     var loadComplete = false;
2067     var listeners = [];
2068     var unloadListeners = [];
2069     var retryCount = 0;
2070     var onAvailStack = [];
2071     var counter = 0;
2072     var lastError = null;
2073
2074     return {
2075         POLL_RETRYS: 200,
2076         POLL_INTERVAL: 20,
2077         EL: 0,
2078         TYPE: 1,
2079         FN: 2,
2080         WFN: 3,
2081         OBJ: 3,
2082         ADJ_SCOPE: 4,
2083         _interval: null,
2084
2085         startInterval: function() {
2086             if (!this._interval) {
2087                 var self = this;
2088                 var callback = function() {
2089                     self._tryPreloadAttach();
2090                 };
2091                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2092
2093             }
2094         },
2095
2096         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2097             onAvailStack.push({ id:         p_id,
2098                 fn:         p_fn,
2099                 obj:        p_obj,
2100                 override:   p_override,
2101                 checkReady: false    });
2102
2103             retryCount = this.POLL_RETRYS;
2104             this.startInterval();
2105         },
2106
2107
2108         addListener: function(el, eventName, fn) {
2109             el = Roo.getDom(el);
2110             if (!el || !fn) {
2111                 return false;
2112             }
2113
2114             if ("unload" == eventName) {
2115                 unloadListeners[unloadListeners.length] =
2116                 [el, eventName, fn];
2117                 return true;
2118             }
2119
2120             var wrappedFn = function(e) {
2121                 return fn(Roo.lib.Event.getEvent(e));
2122             };
2123
2124             var li = [el, eventName, fn, wrappedFn];
2125
2126             var index = listeners.length;
2127             listeners[index] = li;
2128
2129             this.doAdd(el, eventName, wrappedFn, false);
2130             return true;
2131
2132         },
2133
2134
2135         removeListener: function(el, eventName, fn) {
2136             var i, len;
2137
2138             el = Roo.getDom(el);
2139
2140             if(!fn) {
2141                 return this.purgeElement(el, false, eventName);
2142             }
2143
2144
2145             if ("unload" == eventName) {
2146
2147                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2148                     var li = unloadListeners[i];
2149                     if (li &&
2150                         li[0] == el &&
2151                         li[1] == eventName &&
2152                         li[2] == fn) {
2153                         unloadListeners.splice(i, 1);
2154                         return true;
2155                     }
2156                 }
2157
2158                 return false;
2159             }
2160
2161             var cacheItem = null;
2162
2163
2164             var index = arguments[3];
2165
2166             if ("undefined" == typeof index) {
2167                 index = this._getCacheIndex(el, eventName, fn);
2168             }
2169
2170             if (index >= 0) {
2171                 cacheItem = listeners[index];
2172             }
2173
2174             if (!el || !cacheItem) {
2175                 return false;
2176             }
2177
2178             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2179
2180             delete listeners[index][this.WFN];
2181             delete listeners[index][this.FN];
2182             listeners.splice(index, 1);
2183
2184             return true;
2185
2186         },
2187
2188
2189         getTarget: function(ev, resolveTextNode) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             var t = ev.target || ev.srcElement;
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         resolveTextNode: function(node) {
2198             if (Roo.isSafari && node && 3 == node.nodeType) {
2199                 return node.parentNode;
2200             } else {
2201                 return node;
2202             }
2203         },
2204
2205
2206         getPageX: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2209             var x = ev.pageX;
2210             if (!x && 0 !== x) {
2211                 x = ev.clientX || 0;
2212
2213                 if (Roo.isIE) {
2214                     x += this.getScroll()[1];
2215                 }
2216             }
2217
2218             return x;
2219         },
2220
2221
2222         getPageY: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2225             var y = ev.pageY;
2226             if (!y && 0 !== y) {
2227                 y = ev.clientY || 0;
2228
2229                 if (Roo.isIE) {
2230                     y += this.getScroll()[0];
2231                 }
2232             }
2233
2234
2235             return y;
2236         },
2237
2238
2239         getXY: function(ev) {
2240             ev = ev.browserEvent || ev;
2241             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2242             return [this.getPageX(ev), this.getPageY(ev)];
2243         },
2244
2245
2246         getRelatedTarget: function(ev) {
2247             ev = ev.browserEvent || ev;
2248             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2249             var t = ev.relatedTarget;
2250             if (!t) {
2251                 if (ev.type == "mouseout") {
2252                     t = ev.toElement;
2253                 } else if (ev.type == "mouseover") {
2254                     t = ev.fromElement;
2255                 }
2256             }
2257
2258             return this.resolveTextNode(t);
2259         },
2260
2261
2262         getTime: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             if (!ev.time) {
2266                 var t = new Date().getTime();
2267                 try {
2268                     ev.time = t;
2269                 } catch(ex) {
2270                     this.lastError = ex;
2271                     return t;
2272                 }
2273             }
2274
2275             return ev.time;
2276         },
2277
2278
2279         stopEvent: function(ev) {
2280             this.stopPropagation(ev);
2281             this.preventDefault(ev);
2282         },
2283
2284
2285         stopPropagation: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (ev.stopPropagation) {
2288                 ev.stopPropagation();
2289             } else {
2290                 ev.cancelBubble = true;
2291             }
2292         },
2293
2294
2295         preventDefault: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             if(ev.preventDefault) {
2298                 ev.preventDefault();
2299             } else {
2300                 ev.returnValue = false;
2301             }
2302         },
2303
2304
2305         getEvent: function(e) {
2306             var ev = e || window.event;
2307             if (!ev) {
2308                 var c = this.getEvent.caller;
2309                 while (c) {
2310                     ev = c.arguments[0];
2311                     if (ev && Event == ev.constructor) {
2312                         break;
2313                     }
2314                     c = c.caller;
2315                 }
2316             }
2317             return ev;
2318         },
2319
2320
2321         getCharCode: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             return ev.charCode || ev.keyCode || 0;
2324         },
2325
2326
2327         _getCacheIndex: function(el, eventName, fn) {
2328             for (var i = 0,len = listeners.length; i < len; ++i) {
2329                 var li = listeners[i];
2330                 if (li &&
2331                     li[this.FN] == fn &&
2332                     li[this.EL] == el &&
2333                     li[this.TYPE] == eventName) {
2334                     return i;
2335                 }
2336             }
2337
2338             return -1;
2339         },
2340
2341
2342         elCache: {},
2343
2344
2345         getEl: function(id) {
2346             return document.getElementById(id);
2347         },
2348
2349
2350         clearCache: function() {
2351         },
2352
2353
2354         _load: function(e) {
2355             loadComplete = true;
2356             var EU = Roo.lib.Event;
2357
2358
2359             if (Roo.isIE) {
2360                 EU.doRemove(window, "load", EU._load);
2361             }
2362         },
2363
2364
2365         _tryPreloadAttach: function() {
2366
2367             if (this.locked) {
2368                 return false;
2369             }
2370
2371             this.locked = true;
2372
2373
2374             var tryAgain = !loadComplete;
2375             if (!tryAgain) {
2376                 tryAgain = (retryCount > 0);
2377             }
2378
2379
2380             var notAvail = [];
2381             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2382                 var item = onAvailStack[i];
2383                 if (item) {
2384                     var el = this.getEl(item.id);
2385
2386                     if (el) {
2387                         if (!item.checkReady ||
2388                             loadComplete ||
2389                             el.nextSibling ||
2390                             (document && document.body)) {
2391
2392                             var scope = el;
2393                             if (item.override) {
2394                                 if (item.override === true) {
2395                                     scope = item.obj;
2396                                 } else {
2397                                     scope = item.override;
2398                                 }
2399                             }
2400                             item.fn.call(scope, item.obj);
2401                             onAvailStack[i] = null;
2402                         }
2403                     } else {
2404                         notAvail.push(item);
2405                     }
2406                 }
2407             }
2408
2409             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2410
2411             if (tryAgain) {
2412
2413                 this.startInterval();
2414             } else {
2415                 clearInterval(this._interval);
2416                 this._interval = null;
2417             }
2418
2419             this.locked = false;
2420
2421             return true;
2422
2423         },
2424
2425
2426         purgeElement: function(el, recurse, eventName) {
2427             var elListeners = this.getListeners(el, eventName);
2428             if (elListeners) {
2429                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2430                     var l = elListeners[i];
2431                     this.removeListener(el, l.type, l.fn);
2432                 }
2433             }
2434
2435             if (recurse && el && el.childNodes) {
2436                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2437                     this.purgeElement(el.childNodes[i], recurse, eventName);
2438                 }
2439             }
2440         },
2441
2442
2443         getListeners: function(el, eventName) {
2444             var results = [], searchLists;
2445             if (!eventName) {
2446                 searchLists = [listeners, unloadListeners];
2447             } else if (eventName == "unload") {
2448                 searchLists = [unloadListeners];
2449             } else {
2450                 searchLists = [listeners];
2451             }
2452
2453             for (var j = 0; j < searchLists.length; ++j) {
2454                 var searchList = searchLists[j];
2455                 if (searchList && searchList.length > 0) {
2456                     for (var i = 0,len = searchList.length; i < len; ++i) {
2457                         var l = searchList[i];
2458                         if (l && l[this.EL] === el &&
2459                             (!eventName || eventName === l[this.TYPE])) {
2460                             results.push({
2461                                 type:   l[this.TYPE],
2462                                 fn:     l[this.FN],
2463                                 obj:    l[this.OBJ],
2464                                 adjust: l[this.ADJ_SCOPE],
2465                                 index:  i
2466                             });
2467                         }
2468                     }
2469                 }
2470             }
2471
2472             return (results.length) ? results : null;
2473         },
2474
2475
2476         _unload: function(e) {
2477
2478             var EU = Roo.lib.Event, i, j, l, len, index;
2479
2480             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2481                 l = unloadListeners[i];
2482                 if (l) {
2483                     var scope = window;
2484                     if (l[EU.ADJ_SCOPE]) {
2485                         if (l[EU.ADJ_SCOPE] === true) {
2486                             scope = l[EU.OBJ];
2487                         } else {
2488                             scope = l[EU.ADJ_SCOPE];
2489                         }
2490                     }
2491                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2492                     unloadListeners[i] = null;
2493                     l = null;
2494                     scope = null;
2495                 }
2496             }
2497
2498             unloadListeners = null;
2499
2500             if (listeners && listeners.length > 0) {
2501                 j = listeners.length;
2502                 while (j) {
2503                     index = j - 1;
2504                     l = listeners[index];
2505                     if (l) {
2506                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2507                                 l[EU.FN], index);
2508                     }
2509                     j = j - 1;
2510                 }
2511                 l = null;
2512
2513                 EU.clearCache();
2514             }
2515
2516             EU.doRemove(window, "unload", EU._unload);
2517
2518         },
2519
2520
2521         getScroll: function() {
2522             var dd = document.documentElement, db = document.body;
2523             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2524                 return [dd.scrollTop, dd.scrollLeft];
2525             } else if (db) {
2526                 return [db.scrollTop, db.scrollLeft];
2527             } else {
2528                 return [0, 0];
2529             }
2530         },
2531
2532
2533         doAdd: function () {
2534             if (window.addEventListener) {
2535                 return function(el, eventName, fn, capture) {
2536                     el.addEventListener(eventName, fn, (capture));
2537                 };
2538             } else if (window.attachEvent) {
2539                 return function(el, eventName, fn, capture) {
2540                     el.attachEvent("on" + eventName, fn);
2541                 };
2542             } else {
2543                 return function() {
2544                 };
2545             }
2546         }(),
2547
2548
2549         doRemove: function() {
2550             if (window.removeEventListener) {
2551                 return function (el, eventName, fn, capture) {
2552                     el.removeEventListener(eventName, fn, (capture));
2553                 };
2554             } else if (window.detachEvent) {
2555                 return function (el, eventName, fn) {
2556                     el.detachEvent("on" + eventName, fn);
2557                 };
2558             } else {
2559                 return function() {
2560                 };
2561             }
2562         }()
2563     };
2564     
2565 }();
2566 (function() {     
2567    
2568     var E = Roo.lib.Event;
2569     E.on = E.addListener;
2570     E.un = E.removeListener;
2571
2572     if (document && document.body) {
2573         E._load();
2574     } else {
2575         E.doAdd(window, "load", E._load);
2576     }
2577     E.doAdd(window, "unload", E._unload);
2578     E._tryPreloadAttach();
2579 })();
2580
2581 /*
2582  * Portions of this file are based on pieces of Yahoo User Interface Library
2583  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2584  * YUI licensed under the BSD License:
2585  * http://developer.yahoo.net/yui/license.txt
2586  * <script type="text/javascript">
2587  *
2588  */
2589
2590 (function() {
2591     /**
2592      * @class Roo.lib.Ajax
2593      *
2594      */
2595     Roo.lib.Ajax = {
2596         /**
2597          * @static 
2598          */
2599         request : function(method, uri, cb, data, options) {
2600             if(options){
2601                 var hs = options.headers;
2602                 if(hs){
2603                     for(var h in hs){
2604                         if(hs.hasOwnProperty(h)){
2605                             this.initHeader(h, hs[h], false);
2606                         }
2607                     }
2608                 }
2609                 if(options.xmlData){
2610                     this.initHeader('Content-Type', 'text/xml', false);
2611                     method = 'POST';
2612                     data = options.xmlData;
2613                 }
2614             }
2615
2616             return this.asyncRequest(method, uri, cb, data);
2617         },
2618
2619         serializeForm : function(form) {
2620             if(typeof form == 'string') {
2621                 form = (document.getElementById(form) || document.forms[form]);
2622             }
2623
2624             var el, name, val, disabled, data = '', hasSubmit = false;
2625             for (var i = 0; i < form.elements.length; i++) {
2626                 el = form.elements[i];
2627                 disabled = form.elements[i].disabled;
2628                 name = form.elements[i].name;
2629                 val = form.elements[i].value;
2630
2631                 if (!disabled && name){
2632                     switch (el.type)
2633                             {
2634                         case 'select-one':
2635                         case 'select-multiple':
2636                             for (var j = 0; j < el.options.length; j++) {
2637                                 if (el.options[j].selected) {
2638                                     if (Roo.isIE) {
2639                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2640                                     }
2641                                     else {
2642                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2643                                     }
2644                                 }
2645                             }
2646                             break;
2647                         case 'radio':
2648                         case 'checkbox':
2649                             if (el.checked) {
2650                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2651                             }
2652                             break;
2653                         case 'file':
2654
2655                         case undefined:
2656
2657                         case 'reset':
2658
2659                         case 'button':
2660
2661                             break;
2662                         case 'submit':
2663                             if(hasSubmit == false) {
2664                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2665                                 hasSubmit = true;
2666                             }
2667                             break;
2668                         default:
2669                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2670                             break;
2671                     }
2672                 }
2673             }
2674             data = data.substr(0, data.length - 1);
2675             return data;
2676         },
2677
2678         headers:{},
2679
2680         hasHeaders:false,
2681
2682         useDefaultHeader:true,
2683
2684         defaultPostHeader:'application/x-www-form-urlencoded',
2685
2686         useDefaultXhrHeader:true,
2687
2688         defaultXhrHeader:'XMLHttpRequest',
2689
2690         hasDefaultHeaders:true,
2691
2692         defaultHeaders:{},
2693
2694         poll:{},
2695
2696         timeout:{},
2697
2698         pollInterval:50,
2699
2700         transactionId:0,
2701
2702         setProgId:function(id)
2703         {
2704             this.activeX.unshift(id);
2705         },
2706
2707         setDefaultPostHeader:function(b)
2708         {
2709             this.useDefaultHeader = b;
2710         },
2711
2712         setDefaultXhrHeader:function(b)
2713         {
2714             this.useDefaultXhrHeader = b;
2715         },
2716
2717         setPollingInterval:function(i)
2718         {
2719             if (typeof i == 'number' && isFinite(i)) {
2720                 this.pollInterval = i;
2721             }
2722         },
2723
2724         createXhrObject:function(transactionId)
2725         {
2726             var obj,http;
2727             try
2728             {
2729
2730                 http = new XMLHttpRequest();
2731
2732                 obj = { conn:http, tId:transactionId };
2733             }
2734             catch(e)
2735             {
2736                 for (var i = 0; i < this.activeX.length; ++i) {
2737                     try
2738                     {
2739
2740                         http = new ActiveXObject(this.activeX[i]);
2741
2742                         obj = { conn:http, tId:transactionId };
2743                         break;
2744                     }
2745                     catch(e) {
2746                     }
2747                 }
2748             }
2749             finally
2750             {
2751                 return obj;
2752             }
2753         },
2754
2755         getConnectionObject:function()
2756         {
2757             var o;
2758             var tId = this.transactionId;
2759
2760             try
2761             {
2762                 o = this.createXhrObject(tId);
2763                 if (o) {
2764                     this.transactionId++;
2765                 }
2766             }
2767             catch(e) {
2768             }
2769             finally
2770             {
2771                 return o;
2772             }
2773         },
2774
2775         asyncRequest:function(method, uri, callback, postData)
2776         {
2777             var o = this.getConnectionObject();
2778
2779             if (!o) {
2780                 return null;
2781             }
2782             else {
2783                 o.conn.open(method, uri, true);
2784
2785                 if (this.useDefaultXhrHeader) {
2786                     if (!this.defaultHeaders['X-Requested-With']) {
2787                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2788                     }
2789                 }
2790
2791                 if(postData && this.useDefaultHeader){
2792                     this.initHeader('Content-Type', this.defaultPostHeader);
2793                 }
2794
2795                  if (this.hasDefaultHeaders || this.hasHeaders) {
2796                     this.setHeader(o);
2797                 }
2798
2799                 this.handleReadyState(o, callback);
2800                 o.conn.send(postData || null);
2801
2802                 return o;
2803             }
2804         },
2805
2806         handleReadyState:function(o, callback)
2807         {
2808             var oConn = this;
2809
2810             if (callback && callback.timeout) {
2811                 
2812                 this.timeout[o.tId] = window.setTimeout(function() {
2813                     oConn.abort(o, callback, true);
2814                 }, callback.timeout);
2815             }
2816
2817             this.poll[o.tId] = window.setInterval(
2818                     function() {
2819                         if (o.conn && o.conn.readyState == 4) {
2820                             window.clearInterval(oConn.poll[o.tId]);
2821                             delete oConn.poll[o.tId];
2822
2823                             if(callback && callback.timeout) {
2824                                 window.clearTimeout(oConn.timeout[o.tId]);
2825                                 delete oConn.timeout[o.tId];
2826                             }
2827
2828                             oConn.handleTransactionResponse(o, callback);
2829                         }
2830                     }
2831                     , this.pollInterval);
2832         },
2833
2834         handleTransactionResponse:function(o, callback, isAbort)
2835         {
2836
2837             if (!callback) {
2838                 this.releaseObject(o);
2839                 return;
2840             }
2841
2842             var httpStatus, responseObject;
2843
2844             try
2845             {
2846                 if (o.conn.status !== undefined && o.conn.status != 0) {
2847                     httpStatus = o.conn.status;
2848                 }
2849                 else {
2850                     httpStatus = 13030;
2851                 }
2852             }
2853             catch(e) {
2854
2855
2856                 httpStatus = 13030;
2857             }
2858
2859             if (httpStatus >= 200 && httpStatus < 300) {
2860                 responseObject = this.createResponseObject(o, callback.argument);
2861                 if (callback.success) {
2862                     if (!callback.scope) {
2863                         callback.success(responseObject);
2864                     }
2865                     else {
2866
2867
2868                         callback.success.apply(callback.scope, [responseObject]);
2869                     }
2870                 }
2871             }
2872             else {
2873                 switch (httpStatus) {
2874
2875                     case 12002:
2876                     case 12029:
2877                     case 12030:
2878                     case 12031:
2879                     case 12152:
2880                     case 13030:
2881                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2882                         if (callback.failure) {
2883                             if (!callback.scope) {
2884                                 callback.failure(responseObject);
2885                             }
2886                             else {
2887                                 callback.failure.apply(callback.scope, [responseObject]);
2888                             }
2889                         }
2890                         break;
2891                     default:
2892                         responseObject = this.createResponseObject(o, callback.argument);
2893                         if (callback.failure) {
2894                             if (!callback.scope) {
2895                                 callback.failure(responseObject);
2896                             }
2897                             else {
2898                                 callback.failure.apply(callback.scope, [responseObject]);
2899                             }
2900                         }
2901                 }
2902             }
2903
2904             this.releaseObject(o);
2905             responseObject = null;
2906         },
2907
2908         createResponseObject:function(o, callbackArg)
2909         {
2910             var obj = {};
2911             var headerObj = {};
2912
2913             try
2914             {
2915                 var headerStr = o.conn.getAllResponseHeaders();
2916                 var header = headerStr.split('\n');
2917                 for (var i = 0; i < header.length; i++) {
2918                     var delimitPos = header[i].indexOf(':');
2919                     if (delimitPos != -1) {
2920                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2921                     }
2922                 }
2923             }
2924             catch(e) {
2925             }
2926
2927             obj.tId = o.tId;
2928             obj.status = o.conn.status;
2929             obj.statusText = o.conn.statusText;
2930             obj.getResponseHeader = headerObj;
2931             obj.getAllResponseHeaders = headerStr;
2932             obj.responseText = o.conn.responseText;
2933             obj.responseXML = o.conn.responseXML;
2934
2935             if (typeof callbackArg !== undefined) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         createExceptionObject:function(tId, callbackArg, isAbort)
2943         {
2944             var COMM_CODE = 0;
2945             var COMM_ERROR = 'communication failure';
2946             var ABORT_CODE = -1;
2947             var ABORT_ERROR = 'transaction aborted';
2948
2949             var obj = {};
2950
2951             obj.tId = tId;
2952             if (isAbort) {
2953                 obj.status = ABORT_CODE;
2954                 obj.statusText = ABORT_ERROR;
2955             }
2956             else {
2957                 obj.status = COMM_CODE;
2958                 obj.statusText = COMM_ERROR;
2959             }
2960
2961             if (callbackArg) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         initHeader:function(label, value, isDefault)
2969         {
2970             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2971
2972             if (headerObj[label] === undefined) {
2973                 headerObj[label] = value;
2974             }
2975             else {
2976
2977
2978                 headerObj[label] = value + "," + headerObj[label];
2979             }
2980
2981             if (isDefault) {
2982                 this.hasDefaultHeaders = true;
2983             }
2984             else {
2985                 this.hasHeaders = true;
2986             }
2987         },
2988
2989
2990         setHeader:function(o)
2991         {
2992             if (this.hasDefaultHeaders) {
2993                 for (var prop in this.defaultHeaders) {
2994                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2995                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2996                     }
2997                 }
2998             }
2999
3000             if (this.hasHeaders) {
3001                 for (var prop in this.headers) {
3002                     if (this.headers.hasOwnProperty(prop)) {
3003                         o.conn.setRequestHeader(prop, this.headers[prop]);
3004                     }
3005                 }
3006                 this.headers = {};
3007                 this.hasHeaders = false;
3008             }
3009         },
3010
3011         resetDefaultHeaders:function() {
3012             delete this.defaultHeaders;
3013             this.defaultHeaders = {};
3014             this.hasDefaultHeaders = false;
3015         },
3016
3017         abort:function(o, callback, isTimeout)
3018         {
3019             if(this.isCallInProgress(o)) {
3020                 o.conn.abort();
3021                 window.clearInterval(this.poll[o.tId]);
3022                 delete this.poll[o.tId];
3023                 if (isTimeout) {
3024                     delete this.timeout[o.tId];
3025                 }
3026
3027                 this.handleTransactionResponse(o, callback, true);
3028
3029                 return true;
3030             }
3031             else {
3032                 return false;
3033             }
3034         },
3035
3036
3037         isCallInProgress:function(o)
3038         {
3039             if (o && o.conn) {
3040                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3041             }
3042             else {
3043
3044                 return false;
3045             }
3046         },
3047
3048
3049         releaseObject:function(o)
3050         {
3051
3052             o.conn = null;
3053
3054             o = null;
3055         },
3056
3057         activeX:[
3058         'MSXML2.XMLHTTP.3.0',
3059         'MSXML2.XMLHTTP',
3060         'Microsoft.XMLHTTP'
3061         ]
3062
3063
3064     };
3065 })();/*
3066  * Portions of this file are based on pieces of Yahoo User Interface Library
3067  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3068  * YUI licensed under the BSD License:
3069  * http://developer.yahoo.net/yui/license.txt
3070  * <script type="text/javascript">
3071  *
3072  */
3073
3074 Roo.lib.Region = function(t, r, b, l) {
3075     this.top = t;
3076     this[1] = t;
3077     this.right = r;
3078     this.bottom = b;
3079     this.left = l;
3080     this[0] = l;
3081 };
3082
3083
3084 Roo.lib.Region.prototype = {
3085     contains : function(region) {
3086         return ( region.left >= this.left &&
3087                  region.right <= this.right &&
3088                  region.top >= this.top &&
3089                  region.bottom <= this.bottom    );
3090
3091     },
3092
3093     getArea : function() {
3094         return ( (this.bottom - this.top) * (this.right - this.left) );
3095     },
3096
3097     intersect : function(region) {
3098         var t = Math.max(this.top, region.top);
3099         var r = Math.min(this.right, region.right);
3100         var b = Math.min(this.bottom, region.bottom);
3101         var l = Math.max(this.left, region.left);
3102
3103         if (b >= t && r >= l) {
3104             return new Roo.lib.Region(t, r, b, l);
3105         } else {
3106             return null;
3107         }
3108     },
3109     union : function(region) {
3110         var t = Math.min(this.top, region.top);
3111         var r = Math.max(this.right, region.right);
3112         var b = Math.max(this.bottom, region.bottom);
3113         var l = Math.min(this.left, region.left);
3114
3115         return new Roo.lib.Region(t, r, b, l);
3116     },
3117
3118     adjust : function(t, l, b, r) {
3119         this.top += t;
3120         this.left += l;
3121         this.right += r;
3122         this.bottom += b;
3123         return this;
3124     }
3125 };
3126
3127 Roo.lib.Region.getRegion = function(el) {
3128     var p = Roo.lib.Dom.getXY(el);
3129
3130     var t = p[1];
3131     var r = p[0] + el.offsetWidth;
3132     var b = p[1] + el.offsetHeight;
3133     var l = p[0];
3134
3135     return new Roo.lib.Region(t, r, b, l);
3136 };
3137 /*
3138  * Portions of this file are based on pieces of Yahoo User Interface Library
3139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3140  * YUI licensed under the BSD License:
3141  * http://developer.yahoo.net/yui/license.txt
3142  * <script type="text/javascript">
3143  *
3144  */
3145 //@@dep Roo.lib.Region
3146
3147
3148 Roo.lib.Point = function(x, y) {
3149     if (x instanceof Array) {
3150         y = x[1];
3151         x = x[0];
3152     }
3153     this.x = this.right = this.left = this[0] = x;
3154     this.y = this.top = this.bottom = this[1] = y;
3155 };
3156
3157 Roo.lib.Point.prototype = new Roo.lib.Region();
3158 /*
3159  * Portions of this file are based on pieces of Yahoo User Interface Library
3160  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3161  * YUI licensed under the BSD License:
3162  * http://developer.yahoo.net/yui/license.txt
3163  * <script type="text/javascript">
3164  *
3165  */
3166  
3167 (function() {   
3168
3169     Roo.lib.Anim = {
3170         scroll : function(el, args, duration, easing, cb, scope) {
3171             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3172         },
3173
3174         motion : function(el, args, duration, easing, cb, scope) {
3175             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3176         },
3177
3178         color : function(el, args, duration, easing, cb, scope) {
3179             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3180         },
3181
3182         run : function(el, args, duration, easing, cb, scope, type) {
3183             type = type || Roo.lib.AnimBase;
3184             if (typeof easing == "string") {
3185                 easing = Roo.lib.Easing[easing];
3186             }
3187             var anim = new type(el, args, duration, easing);
3188             anim.animateX(function() {
3189                 Roo.callback(cb, scope);
3190             });
3191             return anim;
3192         }
3193     };
3194 })();/*
3195  * Portions of this file are based on pieces of Yahoo User Interface Library
3196  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3197  * YUI licensed under the BSD License:
3198  * http://developer.yahoo.net/yui/license.txt
3199  * <script type="text/javascript">
3200  *
3201  */
3202
3203 (function() {    
3204     var libFlyweight;
3205     
3206     function fly(el) {
3207         if (!libFlyweight) {
3208             libFlyweight = new Roo.Element.Flyweight();
3209         }
3210         libFlyweight.dom = el;
3211         return libFlyweight;
3212     }
3213
3214     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3215     
3216    
3217     
3218     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3219         if (el) {
3220             this.init(el, attributes, duration, method);
3221         }
3222     };
3223
3224     Roo.lib.AnimBase.fly = fly;
3225     
3226     
3227     
3228     Roo.lib.AnimBase.prototype = {
3229
3230         toString: function() {
3231             var el = this.getEl();
3232             var id = el.id || el.tagName;
3233             return ("Anim " + id);
3234         },
3235
3236         patterns: {
3237             noNegatives:        /width|height|opacity|padding/i,
3238             offsetAttribute:  /^((width|height)|(top|left))$/,
3239             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3240             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3241         },
3242
3243
3244         doMethod: function(attr, start, end) {
3245             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3246         },
3247
3248
3249         setAttribute: function(attr, val, unit) {
3250             if (this.patterns.noNegatives.test(attr)) {
3251                 val = (val > 0) ? val : 0;
3252             }
3253
3254             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3255         },
3256
3257
3258         getAttribute: function(attr) {
3259             var el = this.getEl();
3260             var val = fly(el).getStyle(attr);
3261
3262             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3263                 return parseFloat(val);
3264             }
3265
3266             var a = this.patterns.offsetAttribute.exec(attr) || [];
3267             var pos = !!( a[3] );
3268             var box = !!( a[2] );
3269
3270
3271             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3272                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3273             } else {
3274                 val = 0;
3275             }
3276
3277             return val;
3278         },
3279
3280
3281         getDefaultUnit: function(attr) {
3282             if (this.patterns.defaultUnit.test(attr)) {
3283                 return 'px';
3284             }
3285
3286             return '';
3287         },
3288
3289         animateX : function(callback, scope) {
3290             var f = function() {
3291                 this.onComplete.removeListener(f);
3292                 if (typeof callback == "function") {
3293                     callback.call(scope || this, this);
3294                 }
3295             };
3296             this.onComplete.addListener(f, this);
3297             this.animate();
3298         },
3299
3300
3301         setRuntimeAttribute: function(attr) {
3302             var start;
3303             var end;
3304             var attributes = this.attributes;
3305
3306             this.runtimeAttributes[attr] = {};
3307
3308             var isset = function(prop) {
3309                 return (typeof prop !== 'undefined');
3310             };
3311
3312             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3313                 return false;
3314             }
3315
3316             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3317
3318
3319             if (isset(attributes[attr]['to'])) {
3320                 end = attributes[attr]['to'];
3321             } else if (isset(attributes[attr]['by'])) {
3322                 if (start.constructor == Array) {
3323                     end = [];
3324                     for (var i = 0, len = start.length; i < len; ++i) {
3325                         end[i] = start[i] + attributes[attr]['by'][i];
3326                     }
3327                 } else {
3328                     end = start + attributes[attr]['by'];
3329                 }
3330             }
3331
3332             this.runtimeAttributes[attr].start = start;
3333             this.runtimeAttributes[attr].end = end;
3334
3335
3336             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3337         },
3338
3339
3340         init: function(el, attributes, duration, method) {
3341
3342             var isAnimated = false;
3343
3344
3345             var startTime = null;
3346
3347
3348             var actualFrames = 0;
3349
3350
3351             el = Roo.getDom(el);
3352
3353
3354             this.attributes = attributes || {};
3355
3356
3357             this.duration = duration || 1;
3358
3359
3360             this.method = method || Roo.lib.Easing.easeNone;
3361
3362
3363             this.useSeconds = true;
3364
3365
3366             this.currentFrame = 0;
3367
3368
3369             this.totalFrames = Roo.lib.AnimMgr.fps;
3370
3371
3372             this.getEl = function() {
3373                 return el;
3374             };
3375
3376
3377             this.isAnimated = function() {
3378                 return isAnimated;
3379             };
3380
3381
3382             this.getStartTime = function() {
3383                 return startTime;
3384             };
3385
3386             this.runtimeAttributes = {};
3387
3388
3389             this.animate = function() {
3390                 if (this.isAnimated()) {
3391                     return false;
3392                 }
3393
3394                 this.currentFrame = 0;
3395
3396                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3397
3398                 Roo.lib.AnimMgr.registerElement(this);
3399             };
3400
3401
3402             this.stop = function(finish) {
3403                 if (finish) {
3404                     this.currentFrame = this.totalFrames;
3405                     this._onTween.fire();
3406                 }
3407                 Roo.lib.AnimMgr.stop(this);
3408             };
3409
3410             var onStart = function() {
3411                 this.onStart.fire();
3412
3413                 this.runtimeAttributes = {};
3414                 for (var attr in this.attributes) {
3415                     this.setRuntimeAttribute(attr);
3416                 }
3417
3418                 isAnimated = true;
3419                 actualFrames = 0;
3420                 startTime = new Date();
3421             };
3422
3423
3424             var onTween = function() {
3425                 var data = {
3426                     duration: new Date() - this.getStartTime(),
3427                     currentFrame: this.currentFrame
3428                 };
3429
3430                 data.toString = function() {
3431                     return (
3432                             'duration: ' + data.duration +
3433                             ', currentFrame: ' + data.currentFrame
3434                             );
3435                 };
3436
3437                 this.onTween.fire(data);
3438
3439                 var runtimeAttributes = this.runtimeAttributes;
3440
3441                 for (var attr in runtimeAttributes) {
3442                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3443                 }
3444
3445                 actualFrames += 1;
3446             };
3447
3448             var onComplete = function() {
3449                 var actual_duration = (new Date() - startTime) / 1000 ;
3450
3451                 var data = {
3452                     duration: actual_duration,
3453                     frames: actualFrames,
3454                     fps: actualFrames / actual_duration
3455                 };
3456
3457                 data.toString = function() {
3458                     return (
3459                             'duration: ' + data.duration +
3460                             ', frames: ' + data.frames +
3461                             ', fps: ' + data.fps
3462                             );
3463                 };
3464
3465                 isAnimated = false;
3466                 actualFrames = 0;
3467                 this.onComplete.fire(data);
3468             };
3469
3470
3471             this._onStart = new Roo.util.Event(this);
3472             this.onStart = new Roo.util.Event(this);
3473             this.onTween = new Roo.util.Event(this);
3474             this._onTween = new Roo.util.Event(this);
3475             this.onComplete = new Roo.util.Event(this);
3476             this._onComplete = new Roo.util.Event(this);
3477             this._onStart.addListener(onStart);
3478             this._onTween.addListener(onTween);
3479             this._onComplete.addListener(onComplete);
3480         }
3481     };
3482 })();
3483 /*
3484  * Portions of this file are based on pieces of Yahoo User Interface Library
3485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3486  * YUI licensed under the BSD License:
3487  * http://developer.yahoo.net/yui/license.txt
3488  * <script type="text/javascript">
3489  *
3490  */
3491
3492 Roo.lib.AnimMgr = new function() {
3493
3494     var thread = null;
3495
3496
3497     var queue = [];
3498
3499
3500     var tweenCount = 0;
3501
3502
3503     this.fps = 1000;
3504
3505
3506     this.delay = 1;
3507
3508
3509     this.registerElement = function(tween) {
3510         queue[queue.length] = tween;
3511         tweenCount += 1;
3512         tween._onStart.fire();
3513         this.start();
3514     };
3515
3516
3517     this.unRegister = function(tween, index) {
3518         tween._onComplete.fire();
3519         index = index || getIndex(tween);
3520         if (index != -1) {
3521             queue.splice(index, 1);
3522         }
3523
3524         tweenCount -= 1;
3525         if (tweenCount <= 0) {
3526             this.stop();
3527         }
3528     };
3529
3530
3531     this.start = function() {
3532         if (thread === null) {
3533             thread = setInterval(this.run, this.delay);
3534         }
3535     };
3536
3537
3538     this.stop = function(tween) {
3539         if (!tween) {
3540             clearInterval(thread);
3541
3542             for (var i = 0, len = queue.length; i < len; ++i) {
3543                 if (queue[0].isAnimated()) {
3544                     this.unRegister(queue[0], 0);
3545                 }
3546             }
3547
3548             queue = [];
3549             thread = null;
3550             tweenCount = 0;
3551         }
3552         else {
3553             this.unRegister(tween);
3554         }
3555     };
3556
3557
3558     this.run = function() {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             var tween = queue[i];
3561             if (!tween || !tween.isAnimated()) {
3562                 continue;
3563             }
3564
3565             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3566             {
3567                 tween.currentFrame += 1;
3568
3569                 if (tween.useSeconds) {
3570                     correctFrame(tween);
3571                 }
3572                 tween._onTween.fire();
3573             }
3574             else {
3575                 Roo.lib.AnimMgr.stop(tween, i);
3576             }
3577         }
3578     };
3579
3580     var getIndex = function(anim) {
3581         for (var i = 0, len = queue.length; i < len; ++i) {
3582             if (queue[i] == anim) {
3583                 return i;
3584             }
3585         }
3586         return -1;
3587     };
3588
3589
3590     var correctFrame = function(tween) {
3591         var frames = tween.totalFrames;
3592         var frame = tween.currentFrame;
3593         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3594         var elapsed = (new Date() - tween.getStartTime());
3595         var tweak = 0;
3596
3597         if (elapsed < tween.duration * 1000) {
3598             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3599         } else {
3600             tweak = frames - (frame + 1);
3601         }
3602         if (tweak > 0 && isFinite(tweak)) {
3603             if (tween.currentFrame + tweak >= frames) {
3604                 tweak = frames - (frame + 1);
3605             }
3606
3607             tween.currentFrame += tweak;
3608         }
3609     };
3610 };
3611
3612     /*
3613  * Portions of this file are based on pieces of Yahoo User Interface Library
3614  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3615  * YUI licensed under the BSD License:
3616  * http://developer.yahoo.net/yui/license.txt
3617  * <script type="text/javascript">
3618  *
3619  */
3620 Roo.lib.Bezier = new function() {
3621
3622         this.getPosition = function(points, t) {
3623             var n = points.length;
3624             var tmp = [];
3625
3626             for (var i = 0; i < n; ++i) {
3627                 tmp[i] = [points[i][0], points[i][1]];
3628             }
3629
3630             for (var j = 1; j < n; ++j) {
3631                 for (i = 0; i < n - j; ++i) {
3632                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3633                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3634                 }
3635             }
3636
3637             return [ tmp[0][0], tmp[0][1] ];
3638
3639         };
3640     };/*
3641  * Portions of this file are based on pieces of Yahoo User Interface Library
3642  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3643  * YUI licensed under the BSD License:
3644  * http://developer.yahoo.net/yui/license.txt
3645  * <script type="text/javascript">
3646  *
3647  */
3648 (function() {
3649
3650     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3651         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3652     };
3653
3654     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3655
3656     var fly = Roo.lib.AnimBase.fly;
3657     var Y = Roo.lib;
3658     var superclass = Y.ColorAnim.superclass;
3659     var proto = Y.ColorAnim.prototype;
3660
3661     proto.toString = function() {
3662         var el = this.getEl();
3663         var id = el.id || el.tagName;
3664         return ("ColorAnim " + id);
3665     };
3666
3667     proto.patterns.color = /color$/i;
3668     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3669     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3670     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3671     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3672
3673
3674     proto.parseColor = function(s) {
3675         if (s.length == 3) {
3676             return s;
3677         }
3678
3679         var c = this.patterns.hex.exec(s);
3680         if (c && c.length == 4) {
3681             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3682         }
3683
3684         c = this.patterns.rgb.exec(s);
3685         if (c && c.length == 4) {
3686             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3687         }
3688
3689         c = this.patterns.hex3.exec(s);
3690         if (c && c.length == 4) {
3691             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3692         }
3693
3694         return null;
3695     };
3696     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3697     proto.getAttribute = function(attr) {
3698         var el = this.getEl();
3699         if (this.patterns.color.test(attr)) {
3700             var val = fly(el).getStyle(attr);
3701
3702             if (this.patterns.transparent.test(val)) {
3703                 var parent = el.parentNode;
3704                 val = fly(parent).getStyle(attr);
3705
3706                 while (parent && this.patterns.transparent.test(val)) {
3707                     parent = parent.parentNode;
3708                     val = fly(parent).getStyle(attr);
3709                     if (parent.tagName.toUpperCase() == 'HTML') {
3710                         val = '#fff';
3711                     }
3712                 }
3713             }
3714         } else {
3715             val = superclass.getAttribute.call(this, attr);
3716         }
3717
3718         return val;
3719     };
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743
3744     proto.doMethod = function(attr, start, end) {
3745         var val;
3746
3747         if (this.patterns.color.test(attr)) {
3748             val = [];
3749             for (var i = 0, len = start.length; i < len; ++i) {
3750                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3751             }
3752
3753             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3754         }
3755         else {
3756             val = superclass.doMethod.call(this, attr, start, end);
3757         }
3758
3759         return val;
3760     };
3761
3762     proto.setRuntimeAttribute = function(attr) {
3763         superclass.setRuntimeAttribute.call(this, attr);
3764
3765         if (this.patterns.color.test(attr)) {
3766             var attributes = this.attributes;
3767             var start = this.parseColor(this.runtimeAttributes[attr].start);
3768             var end = this.parseColor(this.runtimeAttributes[attr].end);
3769
3770             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3771                 end = this.parseColor(attributes[attr].by);
3772
3773                 for (var i = 0, len = start.length; i < len; ++i) {
3774                     end[i] = start[i] + end[i];
3775                 }
3776             }
3777
3778             this.runtimeAttributes[attr].start = start;
3779             this.runtimeAttributes[attr].end = end;
3780         }
3781     };
3782 })();
3783
3784 /*
3785  * Portions of this file are based on pieces of Yahoo User Interface Library
3786  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3787  * YUI licensed under the BSD License:
3788  * http://developer.yahoo.net/yui/license.txt
3789  * <script type="text/javascript">
3790  *
3791  */
3792 Roo.lib.Easing = {
3793
3794
3795     easeNone: function (t, b, c, d) {
3796         return c * t / d + b;
3797     },
3798
3799
3800     easeIn: function (t, b, c, d) {
3801         return c * (t /= d) * t + b;
3802     },
3803
3804
3805     easeOut: function (t, b, c, d) {
3806         return -c * (t /= d) * (t - 2) + b;
3807     },
3808
3809
3810     easeBoth: function (t, b, c, d) {
3811         if ((t /= d / 2) < 1) {
3812             return c / 2 * t * t + b;
3813         }
3814
3815         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3816     },
3817
3818
3819     easeInStrong: function (t, b, c, d) {
3820         return c * (t /= d) * t * t * t + b;
3821     },
3822
3823
3824     easeOutStrong: function (t, b, c, d) {
3825         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3826     },
3827
3828
3829     easeBothStrong: function (t, b, c, d) {
3830         if ((t /= d / 2) < 1) {
3831             return c / 2 * t * t * t * t + b;
3832         }
3833
3834         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3835     },
3836
3837
3838
3839     elasticIn: function (t, b, c, d, a, p) {
3840         if (t == 0) {
3841             return b;
3842         }
3843         if ((t /= d) == 1) {
3844             return b + c;
3845         }
3846         if (!p) {
3847             p = d * .3;
3848         }
3849
3850         if (!a || a < Math.abs(c)) {
3851             a = c;
3852             var s = p / 4;
3853         }
3854         else {
3855             var s = p / (2 * Math.PI) * Math.asin(c / a);
3856         }
3857
3858         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3859     },
3860
3861
3862     elasticOut: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3882     },
3883
3884
3885     elasticBoth: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889
3890         if ((t /= d / 2) == 2) {
3891             return b + c;
3892         }
3893
3894         if (!p) {
3895             p = d * (.3 * 1.5);
3896         }
3897
3898         if (!a || a < Math.abs(c)) {
3899             a = c;
3900             var s = p / 4;
3901         }
3902         else {
3903             var s = p / (2 * Math.PI) * Math.asin(c / a);
3904         }
3905
3906         if (t < 1) {
3907             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3908                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3909         }
3910         return a * Math.pow(2, -10 * (t -= 1)) *
3911                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3912     },
3913
3914
3915
3916     backIn: function (t, b, c, d, s) {
3917         if (typeof s == 'undefined') {
3918             s = 1.70158;
3919         }
3920         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3921     },
3922
3923
3924     backOut: function (t, b, c, d, s) {
3925         if (typeof s == 'undefined') {
3926             s = 1.70158;
3927         }
3928         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3929     },
3930
3931
3932     backBoth: function (t, b, c, d, s) {
3933         if (typeof s == 'undefined') {
3934             s = 1.70158;
3935         }
3936
3937         if ((t /= d / 2 ) < 1) {
3938             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3939         }
3940         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3941     },
3942
3943
3944     bounceIn: function (t, b, c, d) {
3945         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3946     },
3947
3948
3949     bounceOut: function (t, b, c, d) {
3950         if ((t /= d) < (1 / 2.75)) {
3951             return c * (7.5625 * t * t) + b;
3952         } else if (t < (2 / 2.75)) {
3953             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3954         } else if (t < (2.5 / 2.75)) {
3955             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3956         }
3957         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3958     },
3959
3960
3961     bounceBoth: function (t, b, c, d) {
3962         if (t < d / 2) {
3963             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3964         }
3965         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3966     }
3967 };/*
3968  * Portions of this file are based on pieces of Yahoo User Interface Library
3969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3970  * YUI licensed under the BSD License:
3971  * http://developer.yahoo.net/yui/license.txt
3972  * <script type="text/javascript">
3973  *
3974  */
3975     (function() {
3976         Roo.lib.Motion = function(el, attributes, duration, method) {
3977             if (el) {
3978                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3979             }
3980         };
3981
3982         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3983
3984
3985         var Y = Roo.lib;
3986         var superclass = Y.Motion.superclass;
3987         var proto = Y.Motion.prototype;
3988
3989         proto.toString = function() {
3990             var el = this.getEl();
3991             var id = el.id || el.tagName;
3992             return ("Motion " + id);
3993         };
3994
3995         proto.patterns.points = /^points$/i;
3996
3997         proto.setAttribute = function(attr, val, unit) {
3998             if (this.patterns.points.test(attr)) {
3999                 unit = unit || 'px';
4000                 superclass.setAttribute.call(this, 'left', val[0], unit);
4001                 superclass.setAttribute.call(this, 'top', val[1], unit);
4002             } else {
4003                 superclass.setAttribute.call(this, attr, val, unit);
4004             }
4005         };
4006
4007         proto.getAttribute = function(attr) {
4008             if (this.patterns.points.test(attr)) {
4009                 var val = [
4010                         superclass.getAttribute.call(this, 'left'),
4011                         superclass.getAttribute.call(this, 'top')
4012                         ];
4013             } else {
4014                 val = superclass.getAttribute.call(this, attr);
4015             }
4016
4017             return val;
4018         };
4019
4020         proto.doMethod = function(attr, start, end) {
4021             var val = null;
4022
4023             if (this.patterns.points.test(attr)) {
4024                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4025                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4026             } else {
4027                 val = superclass.doMethod.call(this, attr, start, end);
4028             }
4029             return val;
4030         };
4031
4032         proto.setRuntimeAttribute = function(attr) {
4033             if (this.patterns.points.test(attr)) {
4034                 var el = this.getEl();
4035                 var attributes = this.attributes;
4036                 var start;
4037                 var control = attributes['points']['control'] || [];
4038                 var end;
4039                 var i, len;
4040
4041                 if (control.length > 0 && !(control[0] instanceof Array)) {
4042                     control = [control];
4043                 } else {
4044                     var tmp = [];
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         tmp[i] = control[i];
4047                     }
4048                     control = tmp;
4049                 }
4050
4051                 Roo.fly(el).position();
4052
4053                 if (isset(attributes['points']['from'])) {
4054                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4055                 }
4056                 else {
4057                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4058                 }
4059
4060                 start = this.getAttribute('points');
4061
4062
4063                 if (isset(attributes['points']['to'])) {
4064                     end = translateValues.call(this, attributes['points']['to'], start);
4065
4066                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4067                     for (i = 0,len = control.length; i < len; ++i) {
4068                         control[i] = translateValues.call(this, control[i], start);
4069                     }
4070
4071
4072                 } else if (isset(attributes['points']['by'])) {
4073                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4074
4075                     for (i = 0,len = control.length; i < len; ++i) {
4076                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4077                     }
4078                 }
4079
4080                 this.runtimeAttributes[attr] = [start];
4081
4082                 if (control.length > 0) {
4083                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4084                 }
4085
4086                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4087             }
4088             else {
4089                 superclass.setRuntimeAttribute.call(this, attr);
4090             }
4091         };
4092
4093         var translateValues = function(val, start) {
4094             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4095             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4096
4097             return val;
4098         };
4099
4100         var isset = function(prop) {
4101             return (typeof prop !== 'undefined');
4102         };
4103     })();
4104 /*
4105  * Portions of this file are based on pieces of Yahoo User Interface Library
4106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4107  * YUI licensed under the BSD License:
4108  * http://developer.yahoo.net/yui/license.txt
4109  * <script type="text/javascript">
4110  *
4111  */
4112     (function() {
4113         Roo.lib.Scroll = function(el, attributes, duration, method) {
4114             if (el) {
4115                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4116             }
4117         };
4118
4119         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4120
4121
4122         var Y = Roo.lib;
4123         var superclass = Y.Scroll.superclass;
4124         var proto = Y.Scroll.prototype;
4125
4126         proto.toString = function() {
4127             var el = this.getEl();
4128             var id = el.id || el.tagName;
4129             return ("Scroll " + id);
4130         };
4131
4132         proto.doMethod = function(attr, start, end) {
4133             var val = null;
4134
4135             if (attr == 'scroll') {
4136                 val = [
4137                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4138                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4139                         ];
4140
4141             } else {
4142                 val = superclass.doMethod.call(this, attr, start, end);
4143             }
4144             return val;
4145         };
4146
4147         proto.getAttribute = function(attr) {
4148             var val = null;
4149             var el = this.getEl();
4150
4151             if (attr == 'scroll') {
4152                 val = [ el.scrollLeft, el.scrollTop ];
4153             } else {
4154                 val = superclass.getAttribute.call(this, attr);
4155             }
4156
4157             return val;
4158         };
4159
4160         proto.setAttribute = function(attr, val, unit) {
4161             var el = this.getEl();
4162
4163             if (attr == 'scroll') {
4164                 el.scrollLeft = val[0];
4165                 el.scrollTop = val[1];
4166             } else {
4167                 superclass.setAttribute.call(this, attr, val, unit);
4168             }
4169         };
4170     })();
4171 /*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181
4182
4183 // nasty IE9 hack - what a pile of crap that is..
4184
4185  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4186     Range.prototype.createContextualFragment = function (html) {
4187         var doc = window.document;
4188         var container = doc.createElement("div");
4189         container.innerHTML = html;
4190         var frag = doc.createDocumentFragment(), n;
4191         while ((n = container.firstChild)) {
4192             frag.appendChild(n);
4193         }
4194         return frag;
4195     };
4196 }
4197
4198 /**
4199  * @class Roo.DomHelper
4200  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4201  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4202  * @singleton
4203  */
4204 Roo.DomHelper = function(){
4205     var tempTableEl = null;
4206     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4207     var tableRe = /^table|tbody|tr|td$/i;
4208     var xmlns = {};
4209     // build as innerHTML where available
4210     /** @ignore */
4211     var createHtml = function(o){
4212         if(typeof o == 'string'){
4213             return o;
4214         }
4215         var b = "";
4216         if(!o.tag){
4217             o.tag = "div";
4218         }
4219         b += "<" + o.tag;
4220         for(var attr in o){
4221             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4222             if(attr == "style"){
4223                 var s = o["style"];
4224                 if(typeof s == "function"){
4225                     s = s.call();
4226                 }
4227                 if(typeof s == "string"){
4228                     b += ' style="' + s + '"';
4229                 }else if(typeof s == "object"){
4230                     b += ' style="';
4231                     for(var key in s){
4232                         if(typeof s[key] != "function"){
4233                             b += key + ":" + s[key] + ";";
4234                         }
4235                     }
4236                     b += '"';
4237                 }
4238             }else{
4239                 if(attr == "cls"){
4240                     b += ' class="' + o["cls"] + '"';
4241                 }else if(attr == "htmlFor"){
4242                     b += ' for="' + o["htmlFor"] + '"';
4243                 }else{
4244                     b += " " + attr + '="' + o[attr] + '"';
4245                 }
4246             }
4247         }
4248         if(emptyTags.test(o.tag)){
4249             b += "/>";
4250         }else{
4251             b += ">";
4252             var cn = o.children || o.cn;
4253             if(cn){
4254                 //http://bugs.kde.org/show_bug.cgi?id=71506
4255                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4256                     for(var i = 0, len = cn.length; i < len; i++) {
4257                         b += createHtml(cn[i], b);
4258                     }
4259                 }else{
4260                     b += createHtml(cn, b);
4261                 }
4262             }
4263             if(o.html){
4264                 b += o.html;
4265             }
4266             b += "</" + o.tag + ">";
4267         }
4268         return b;
4269     };
4270
4271     // build as dom
4272     /** @ignore */
4273     var createDom = function(o, parentNode){
4274          
4275         // defininition craeted..
4276         var ns = false;
4277         if (o.ns && o.ns != 'html') {
4278                
4279             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4280                 xmlns[o.ns] = o.xmlns;
4281                 ns = o.xmlns;
4282             }
4283             if (typeof(xmlns[o.ns]) == 'undefined') {
4284                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4285             }
4286             ns = xmlns[o.ns];
4287         }
4288         
4289         
4290         if (typeof(o) == 'string') {
4291             return parentNode.appendChild(document.createTextNode(o));
4292         }
4293         o.tag = o.tag || div;
4294         if (o.ns && Roo.isIE) {
4295             ns = false;
4296             o.tag = o.ns + ':' + o.tag;
4297             
4298         }
4299         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4300         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4301         for(var attr in o){
4302             
4303             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4304                     attr == "style" || typeof o[attr] == "function") { continue; }
4305                     
4306             if(attr=="cls" && Roo.isIE){
4307                 el.className = o["cls"];
4308             }else{
4309                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4310                 else { 
4311                     el[attr] = o[attr];
4312                 }
4313             }
4314         }
4315         Roo.DomHelper.applyStyles(el, o.style);
4316         var cn = o.children || o.cn;
4317         if(cn){
4318             //http://bugs.kde.org/show_bug.cgi?id=71506
4319              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4320                 for(var i = 0, len = cn.length; i < len; i++) {
4321                     createDom(cn[i], el);
4322                 }
4323             }else{
4324                 createDom(cn, el);
4325             }
4326         }
4327         if(o.html){
4328             el.innerHTML = o.html;
4329         }
4330         if(parentNode){
4331            parentNode.appendChild(el);
4332         }
4333         return el;
4334     };
4335
4336     var ieTable = function(depth, s, h, e){
4337         tempTableEl.innerHTML = [s, h, e].join('');
4338         var i = -1, el = tempTableEl;
4339         while(++i < depth){
4340             el = el.firstChild;
4341         }
4342         return el;
4343     };
4344
4345     // kill repeat to save bytes
4346     var ts = '<table>',
4347         te = '</table>',
4348         tbs = ts+'<tbody>',
4349         tbe = '</tbody>'+te,
4350         trs = tbs + '<tr>',
4351         tre = '</tr>'+tbe;
4352
4353     /**
4354      * @ignore
4355      * Nasty code for IE's broken table implementation
4356      */
4357     var insertIntoTable = function(tag, where, el, html){
4358         if(!tempTableEl){
4359             tempTableEl = document.createElement('div');
4360         }
4361         var node;
4362         var before = null;
4363         if(tag == 'td'){
4364             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4365                 return;
4366             }
4367             if(where == 'beforebegin'){
4368                 before = el;
4369                 el = el.parentNode;
4370             } else{
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373             }
4374             node = ieTable(4, trs, html, tre);
4375         }
4376         else if(tag == 'tr'){
4377             if(where == 'beforebegin'){
4378                 before = el;
4379                 el = el.parentNode;
4380                 node = ieTable(3, tbs, html, tbe);
4381             } else if(where == 'afterend'){
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384                 node = ieTable(3, tbs, html, tbe);
4385             } else{ // INTO a TR
4386                 if(where == 'afterbegin'){
4387                     before = el.firstChild;
4388                 }
4389                 node = ieTable(4, trs, html, tre);
4390             }
4391         } else if(tag == 'tbody'){
4392             if(where == 'beforebegin'){
4393                 before = el;
4394                 el = el.parentNode;
4395                 node = ieTable(2, ts, html, te);
4396             } else if(where == 'afterend'){
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399                 node = ieTable(2, ts, html, te);
4400             } else{
4401                 if(where == 'afterbegin'){
4402                     before = el.firstChild;
4403                 }
4404                 node = ieTable(3, tbs, html, tbe);
4405             }
4406         } else{ // TABLE
4407             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4408                 return;
4409             }
4410             if(where == 'afterbegin'){
4411                 before = el.firstChild;
4412             }
4413             node = ieTable(2, ts, html, te);
4414         }
4415         el.insertBefore(node, before);
4416         return node;
4417     };
4418
4419     return {
4420     /** True to force the use of DOM instead of html fragments @type Boolean */
4421     useDom : false,
4422
4423     /**
4424      * Returns the markup for the passed Element(s) config
4425      * @param {Object} o The Dom object spec (and children)
4426      * @return {String}
4427      */
4428     markup : function(o){
4429         return createHtml(o);
4430     },
4431
4432     /**
4433      * Applies a style specification to an element
4434      * @param {String/HTMLElement} el The element to apply styles to
4435      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4436      * a function which returns such a specification.
4437      */
4438     applyStyles : function(el, styles){
4439         if(styles){
4440            el = Roo.fly(el);
4441            if(typeof styles == "string"){
4442                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4443                var matches;
4444                while ((matches = re.exec(styles)) != null){
4445                    el.setStyle(matches[1], matches[2]);
4446                }
4447            }else if (typeof styles == "object"){
4448                for (var style in styles){
4449                   el.setStyle(style, styles[style]);
4450                }
4451            }else if (typeof styles == "function"){
4452                 Roo.DomHelper.applyStyles(el, styles.call());
4453            }
4454         }
4455     },
4456
4457     /**
4458      * Inserts an HTML fragment into the Dom
4459      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4460      * @param {HTMLElement} el The context element
4461      * @param {String} html The HTML fragmenet
4462      * @return {HTMLElement} The new node
4463      */
4464     insertHtml : function(where, el, html){
4465         where = where.toLowerCase();
4466         if(el.insertAdjacentHTML){
4467             if(tableRe.test(el.tagName)){
4468                 var rs;
4469                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4470                     return rs;
4471                 }
4472             }
4473             switch(where){
4474                 case "beforebegin":
4475                     el.insertAdjacentHTML('BeforeBegin', html);
4476                     return el.previousSibling;
4477                 case "afterbegin":
4478                     el.insertAdjacentHTML('AfterBegin', html);
4479                     return el.firstChild;
4480                 case "beforeend":
4481                     el.insertAdjacentHTML('BeforeEnd', html);
4482                     return el.lastChild;
4483                 case "afterend":
4484                     el.insertAdjacentHTML('AfterEnd', html);
4485                     return el.nextSibling;
4486             }
4487             throw 'Illegal insertion point -> "' + where + '"';
4488         }
4489         var range = el.ownerDocument.createRange();
4490         var frag;
4491         switch(where){
4492              case "beforebegin":
4493                 range.setStartBefore(el);
4494                 frag = range.createContextualFragment(html);
4495                 el.parentNode.insertBefore(frag, el);
4496                 return el.previousSibling;
4497              case "afterbegin":
4498                 if(el.firstChild){
4499                     range.setStartBefore(el.firstChild);
4500                     frag = range.createContextualFragment(html);
4501                     el.insertBefore(frag, el.firstChild);
4502                     return el.firstChild;
4503                 }else{
4504                     el.innerHTML = html;
4505                     return el.firstChild;
4506                 }
4507             case "beforeend":
4508                 if(el.lastChild){
4509                     range.setStartAfter(el.lastChild);
4510                     frag = range.createContextualFragment(html);
4511                     el.appendChild(frag);
4512                     return el.lastChild;
4513                 }else{
4514                     el.innerHTML = html;
4515                     return el.lastChild;
4516                 }
4517             case "afterend":
4518                 range.setStartAfter(el);
4519                 frag = range.createContextualFragment(html);
4520                 el.parentNode.insertBefore(frag, el.nextSibling);
4521                 return el.nextSibling;
4522             }
4523             throw 'Illegal insertion point -> "' + where + '"';
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them before el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertBefore : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "beforeBegin");
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them after el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object} o The Dom object spec (and children)
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertAfter : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them as the first child of el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertFirst : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterBegin");
4557     },
4558
4559     // private
4560     doInsert : function(el, o, returnElement, pos, sibling){
4561         el = Roo.getDom(el);
4562         var newNode;
4563         if(this.useDom || o.ns){
4564             newNode = createDom(o, null);
4565             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4566         }else{
4567             var html = createHtml(o);
4568             newNode = this.insertHtml(pos, el, html);
4569         }
4570         return returnElement ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Creates new Dom element(s) and appends them to el
4575      * @param {String/HTMLElement/Element} el The context element
4576      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4578      * @return {HTMLElement/Roo.Element} The new node
4579      */
4580     append : function(el, o, returnElement){
4581         el = Roo.getDom(el);
4582         var newNode;
4583         if(this.useDom || o.ns){
4584             newNode = createDom(o, null);
4585             el.appendChild(newNode);
4586         }else{
4587             var html = createHtml(o);
4588             newNode = this.insertHtml("beforeEnd", el, html);
4589         }
4590         return returnElement ? Roo.get(newNode, true) : newNode;
4591     },
4592
4593     /**
4594      * Creates new Dom element(s) and overwrites the contents of el with them
4595      * @param {String/HTMLElement/Element} el The context element
4596      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4597      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4598      * @return {HTMLElement/Roo.Element} The new node
4599      */
4600     overwrite : function(el, o, returnElement){
4601         el = Roo.getDom(el);
4602         if (o.ns) {
4603           
4604             while (el.childNodes.length) {
4605                 el.removeChild(el.firstChild);
4606             }
4607             createDom(o, el);
4608         } else {
4609             el.innerHTML = createHtml(o);   
4610         }
4611         
4612         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4613     },
4614
4615     /**
4616      * Creates a new Roo.DomHelper.Template from the Dom object spec
4617      * @param {Object} o The Dom object spec (and children)
4618      * @return {Roo.DomHelper.Template} The new template
4619      */
4620     createTemplate : function(o){
4621         var html = createHtml(o);
4622         return new Roo.Template(html);
4623     }
4624     };
4625 }();
4626 /*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637 /**
4638 * @class Roo.Template
4639 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4640 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4641 * Usage:
4642 <pre><code>
4643 var t = new Roo.Template({
4644     html :  '&lt;div name="{id}"&gt;' + 
4645         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4646         '&lt;/div&gt;',
4647     myformat: function (value, allValues) {
4648         return 'XX' + value;
4649     }
4650 });
4651 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4652 </code></pre>
4653 * For more information see this blog post with examples:
4654 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4655      - Create Elements using DOM, HTML fragments and Templates</a>. 
4656 * @constructor
4657 * @param {Object} cfg - Configuration object.
4658 */
4659 Roo.Template = function(cfg){
4660     // BC!
4661     if(cfg instanceof Array){
4662         cfg = cfg.join("");
4663     }else if(arguments.length > 1){
4664         cfg = Array.prototype.join.call(arguments, "");
4665     }
4666     
4667     
4668     if (typeof(cfg) == 'object') {
4669         Roo.apply(this,cfg)
4670     } else {
4671         // bc
4672         this.html = cfg;
4673     }
4674     if (this.url) {
4675         this.load();
4676     }
4677     
4678 };
4679 Roo.Template.prototype = {
4680     
4681     /**
4682      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4683      */
4684     onLoad : false,
4685     
4686     
4687     /**
4688      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4689      *                    it should be fixed so that template is observable...
4690      */
4691     url : false,
4692     /**
4693      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4694      */
4695     html : '',
4696     
4697     
4698     compiled : false,
4699     loaded : false,
4700     /**
4701      * Returns an HTML fragment of this template with the specified values applied.
4702      * @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'})
4703      * @return {String} The HTML fragment
4704      */
4705     
4706    
4707     
4708     applyTemplate : function(values){
4709         //Roo.log(["applyTemplate", values]);
4710         try {
4711            
4712             if(this.compiled){
4713                 return this.compiled(values);
4714             }
4715             var useF = this.disableFormats !== true;
4716             var fm = Roo.util.Format, tpl = this;
4717             var fn = function(m, name, format, args){
4718                 if(format && useF){
4719                     if(format.substr(0, 5) == "this."){
4720                         return tpl.call(format.substr(5), values[name], values);
4721                     }else{
4722                         if(args){
4723                             // quoted values are required for strings in compiled templates, 
4724                             // but for non compiled we need to strip them
4725                             // quoted reversed for jsmin
4726                             var re = /^\s*['"](.*)["']\s*$/;
4727                             args = args.split(',');
4728                             for(var i = 0, len = args.length; i < len; i++){
4729                                 args[i] = args[i].replace(re, "$1");
4730                             }
4731                             args = [values[name]].concat(args);
4732                         }else{
4733                             args = [values[name]];
4734                         }
4735                         return fm[format].apply(fm, args);
4736                     }
4737                 }else{
4738                     return values[name] !== undefined ? values[name] : "";
4739                 }
4740             };
4741             return this.html.replace(this.re, fn);
4742         } catch (e) {
4743             Roo.log(e);
4744             throw e;
4745         }
4746          
4747     },
4748     
4749     loading : false,
4750       
4751     load : function ()
4752     {
4753          
4754         if (this.loading) {
4755             return;
4756         }
4757         var _t = this;
4758         
4759         this.loading = true;
4760         this.compiled = false;
4761         
4762         var cx = new Roo.data.Connection();
4763         cx.request({
4764             url : this.url,
4765             method : 'GET',
4766             success : function (response) {
4767                 _t.loading = false;
4768                 _t.url = false;
4769                 
4770                 _t.set(response.responseText,true);
4771                 _t.loaded = true;
4772                 if (_t.onLoad) {
4773                     _t.onLoad();
4774                 }
4775              },
4776             failure : function(response) {
4777                 Roo.log("Template failed to load from " + _t.url);
4778                 _t.loading = false;
4779             }
4780         });
4781     },
4782
4783     /**
4784      * Sets the HTML used as the template and optionally compiles it.
4785      * @param {String} html
4786      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4787      * @return {Roo.Template} this
4788      */
4789     set : function(html, compile){
4790         this.html = html;
4791         this.compiled = false;
4792         if(compile){
4793             this.compile();
4794         }
4795         return this;
4796     },
4797     
4798     /**
4799      * True to disable format functions (defaults to false)
4800      * @type Boolean
4801      */
4802     disableFormats : false,
4803     
4804     /**
4805     * The regular expression used to match template variables 
4806     * @type RegExp
4807     * @property 
4808     */
4809     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4810     
4811     /**
4812      * Compiles the template into an internal function, eliminating the RegEx overhead.
4813      * @return {Roo.Template} this
4814      */
4815     compile : function(){
4816         var fm = Roo.util.Format;
4817         var useF = this.disableFormats !== true;
4818         var sep = Roo.isGecko ? "+" : ",";
4819         var fn = function(m, name, format, args){
4820             if(format && useF){
4821                 args = args ? ',' + args : "";
4822                 if(format.substr(0, 5) != "this."){
4823                     format = "fm." + format + '(';
4824                 }else{
4825                     format = 'this.call("'+ format.substr(5) + '", ';
4826                     args = ", values";
4827                 }
4828             }else{
4829                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4830             }
4831             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4832         };
4833         var body;
4834         // branched to use + in gecko and [].join() in others
4835         if(Roo.isGecko){
4836             body = "this.compiled = function(values){ return '" +
4837                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4838                     "';};";
4839         }else{
4840             body = ["this.compiled = function(values){ return ['"];
4841             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4842             body.push("'].join('');};");
4843             body = body.join('');
4844         }
4845         /**
4846          * eval:var:values
4847          * eval:var:fm
4848          */
4849         eval(body);
4850         return this;
4851     },
4852     
4853     // private function used to call members
4854     call : function(fnName, value, allValues){
4855         return this[fnName](value, allValues);
4856     },
4857     
4858     /**
4859      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4860      * @param {String/HTMLElement/Roo.Element} el The context element
4861      * @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'})
4862      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4863      * @return {HTMLElement/Roo.Element} The new node or Element
4864      */
4865     insertFirst: function(el, values, returnElement){
4866         return this.doInsert('afterBegin', el, values, returnElement);
4867     },
4868
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) before el.
4871      * @param {String/HTMLElement/Roo.Element} el The context element
4872      * @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'})
4873      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4874      * @return {HTMLElement/Roo.Element} The new node or Element
4875      */
4876     insertBefore: function(el, values, returnElement){
4877         return this.doInsert('beforeBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) after el.
4882      * @param {String/HTMLElement/Roo.Element} el The context element
4883      * @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'})
4884      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4885      * @return {HTMLElement/Roo.Element} The new node or Element
4886      */
4887     insertAfter : function(el, values, returnElement){
4888         return this.doInsert('afterEnd', el, values, returnElement);
4889     },
4890     
4891     /**
4892      * Applies the supplied values to the template and appends the new node(s) to el.
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @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'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     append : function(el, values, returnElement){
4899         return this.doInsert('beforeEnd', el, values, returnElement);
4900     },
4901
4902     doInsert : function(where, el, values, returnEl){
4903         el = Roo.getDom(el);
4904         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4905         return returnEl ? Roo.get(newNode, true) : newNode;
4906     },
4907
4908     /**
4909      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4910      * @param {String/HTMLElement/Roo.Element} el The context element
4911      * @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'})
4912      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4913      * @return {HTMLElement/Roo.Element} The new node or Element
4914      */
4915     overwrite : function(el, values, returnElement){
4916         el = Roo.getDom(el);
4917         el.innerHTML = this.applyTemplate(values);
4918         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4919     }
4920 };
4921 /**
4922  * Alias for {@link #applyTemplate}
4923  * @method
4924  */
4925 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4926
4927 // backwards compat
4928 Roo.DomHelper.Template = Roo.Template;
4929
4930 /**
4931  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4932  * @param {String/HTMLElement} el A DOM element or its id
4933  * @returns {Roo.Template} The created template
4934  * @static
4935  */
4936 Roo.Template.from = function(el){
4937     el = Roo.getDom(el);
4938     return new Roo.Template(el.value || el.innerHTML);
4939 };/*
4940  * Based on:
4941  * Ext JS Library 1.1.1
4942  * Copyright(c) 2006-2007, Ext JS, LLC.
4943  *
4944  * Originally Released Under LGPL - original licence link has changed is not relivant.
4945  *
4946  * Fork - LGPL
4947  * <script type="text/javascript">
4948  */
4949  
4950
4951 /*
4952  * This is code is also distributed under MIT license for use
4953  * with jQuery and prototype JavaScript libraries.
4954  */
4955 /**
4956  * @class Roo.DomQuery
4957 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).
4958 <p>
4959 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>
4960
4961 <p>
4962 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.
4963 </p>
4964 <h4>Element Selectors:</h4>
4965 <ul class="list">
4966     <li> <b>*</b> any element</li>
4967     <li> <b>E</b> an element with the tag E</li>
4968     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4969     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4970     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4971     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4972 </ul>
4973 <h4>Attribute Selectors:</h4>
4974 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4975 <ul class="list">
4976     <li> <b>E[foo]</b> has an attribute "foo"</li>
4977     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4978     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4979     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4980     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4981     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4982     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4983 </ul>
4984 <h4>Pseudo Classes:</h4>
4985 <ul class="list">
4986     <li> <b>E:first-child</b> E is the first child of its parent</li>
4987     <li> <b>E:last-child</b> E is the last child of its parent</li>
4988     <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>
4989     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4990     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4991     <li> <b>E:only-child</b> E is the only child of its parent</li>
4992     <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>
4993     <li> <b>E:first</b> the first E in the resultset</li>
4994     <li> <b>E:last</b> the last E in the resultset</li>
4995     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4996     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4997     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4998     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4999     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5000     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5001     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5002     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5003     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5004 </ul>
5005 <h4>CSS Value Selectors:</h4>
5006 <ul class="list">
5007     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5008     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5009     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5010     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5011     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5012     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5013 </ul>
5014  * @singleton
5015  */
5016 Roo.DomQuery = function(){
5017     var cache = {}, simpleCache = {}, valueCache = {};
5018     var nonSpace = /\S/;
5019     var trimRe = /^\s+|\s+$/g;
5020     var tplRe = /\{(\d+)\}/g;
5021     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5022     var tagTokenRe = /^(#)?([\w-\*]+)/;
5023     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5024
5025     function child(p, index){
5026         var i = 0;
5027         var n = p.firstChild;
5028         while(n){
5029             if(n.nodeType == 1){
5030                if(++i == index){
5031                    return n;
5032                }
5033             }
5034             n = n.nextSibling;
5035         }
5036         return null;
5037     };
5038
5039     function next(n){
5040         while((n = n.nextSibling) && n.nodeType != 1);
5041         return n;
5042     };
5043
5044     function prev(n){
5045         while((n = n.previousSibling) && n.nodeType != 1);
5046         return n;
5047     };
5048
5049     function children(d){
5050         var n = d.firstChild, ni = -1;
5051             while(n){
5052                 var nx = n.nextSibling;
5053                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5054                     d.removeChild(n);
5055                 }else{
5056                     n.nodeIndex = ++ni;
5057                 }
5058                 n = nx;
5059             }
5060             return this;
5061         };
5062
5063     function byClassName(c, a, v){
5064         if(!v){
5065             return c;
5066         }
5067         var r = [], ri = -1, cn;
5068         for(var i = 0, ci; ci = c[i]; i++){
5069             if((' '+ci.className+' ').indexOf(v) != -1){
5070                 r[++ri] = ci;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function attrValue(n, attr){
5077         if(!n.tagName && typeof n.length != "undefined"){
5078             n = n[0];
5079         }
5080         if(!n){
5081             return null;
5082         }
5083         if(attr == "for"){
5084             return n.htmlFor;
5085         }
5086         if(attr == "class" || attr == "className"){
5087             return n.className;
5088         }
5089         return n.getAttribute(attr) || n[attr];
5090
5091     };
5092
5093     function getNodes(ns, mode, tagName){
5094         var result = [], ri = -1, cs;
5095         if(!ns){
5096             return result;
5097         }
5098         tagName = tagName || "*";
5099         if(typeof ns.getElementsByTagName != "undefined"){
5100             ns = [ns];
5101         }
5102         if(!mode){
5103             for(var i = 0, ni; ni = ns[i]; i++){
5104                 cs = ni.getElementsByTagName(tagName);
5105                 for(var j = 0, ci; ci = cs[j]; j++){
5106                     result[++ri] = ci;
5107                 }
5108             }
5109         }else if(mode == "/" || mode == ">"){
5110             var utag = tagName.toUpperCase();
5111             for(var i = 0, ni, cn; ni = ns[i]; i++){
5112                 cn = ni.children || ni.childNodes;
5113                 for(var j = 0, cj; cj = cn[j]; j++){
5114                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5115                         result[++ri] = cj;
5116                     }
5117                 }
5118             }
5119         }else if(mode == "+"){
5120             var utag = tagName.toUpperCase();
5121             for(var i = 0, n; n = ns[i]; i++){
5122                 while((n = n.nextSibling) && n.nodeType != 1);
5123                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5124                     result[++ri] = n;
5125                 }
5126             }
5127         }else if(mode == "~"){
5128             for(var i = 0, n; n = ns[i]; i++){
5129                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5130                 if(n){
5131                     result[++ri] = n;
5132                 }
5133             }
5134         }
5135         return result;
5136     };
5137
5138     function concat(a, b){
5139         if(b.slice){
5140             return a.concat(b);
5141         }
5142         for(var i = 0, l = b.length; i < l; i++){
5143             a[a.length] = b[i];
5144         }
5145         return a;
5146     }
5147
5148     function byTag(cs, tagName){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!tagName){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         tagName = tagName.toLowerCase();
5157         for(var i = 0, ci; ci = cs[i]; i++){
5158             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5159                 r[++ri] = ci;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byId(cs, attr, id){
5166         if(cs.tagName || cs == document){
5167             cs = [cs];
5168         }
5169         if(!id){
5170             return cs;
5171         }
5172         var r = [], ri = -1;
5173         for(var i = 0,ci; ci = cs[i]; i++){
5174             if(ci && ci.id == id){
5175                 r[++ri] = ci;
5176                 return r;
5177             }
5178         }
5179         return r;
5180     };
5181
5182     function byAttribute(cs, attr, value, op, custom){
5183         var r = [], ri = -1, st = custom=="{";
5184         var f = Roo.DomQuery.operators[op];
5185         for(var i = 0, ci; ci = cs[i]; i++){
5186             var a;
5187             if(st){
5188                 a = Roo.DomQuery.getStyle(ci, attr);
5189             }
5190             else if(attr == "class" || attr == "className"){
5191                 a = ci.className;
5192             }else if(attr == "for"){
5193                 a = ci.htmlFor;
5194             }else if(attr == "href"){
5195                 a = ci.getAttribute("href", 2);
5196             }else{
5197                 a = ci.getAttribute(attr);
5198             }
5199             if((f && f(a, value)) || (!f && a)){
5200                 r[++ri] = ci;
5201             }
5202         }
5203         return r;
5204     };
5205
5206     function byPseudo(cs, name, value){
5207         return Roo.DomQuery.pseudos[name](cs, value);
5208     };
5209
5210     // This is for IE MSXML which does not support expandos.
5211     // IE runs the same speed using setAttribute, however FF slows way down
5212     // and Safari completely fails so they need to continue to use expandos.
5213     var isIE = window.ActiveXObject ? true : false;
5214
5215     // this eval is stop the compressor from
5216     // renaming the variable to something shorter
5217     
5218     /** eval:var:batch */
5219     var batch = 30803; 
5220
5221     var key = 30803;
5222
5223     function nodupIEXml(cs){
5224         var d = ++key;
5225         cs[0].setAttribute("_nodup", d);
5226         var r = [cs[0]];
5227         for(var i = 1, len = cs.length; i < len; i++){
5228             var c = cs[i];
5229             if(!c.getAttribute("_nodup") != d){
5230                 c.setAttribute("_nodup", d);
5231                 r[r.length] = c;
5232             }
5233         }
5234         for(var i = 0, len = cs.length; i < len; i++){
5235             cs[i].removeAttribute("_nodup");
5236         }
5237         return r;
5238     }
5239
5240     function nodup(cs){
5241         if(!cs){
5242             return [];
5243         }
5244         var len = cs.length, c, i, r = cs, cj, ri = -1;
5245         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5246             return cs;
5247         }
5248         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5249             return nodupIEXml(cs);
5250         }
5251         var d = ++key;
5252         cs[0]._nodup = d;
5253         for(i = 1; c = cs[i]; i++){
5254             if(c._nodup != d){
5255                 c._nodup = d;
5256             }else{
5257                 r = [];
5258                 for(var j = 0; j < i; j++){
5259                     r[++ri] = cs[j];
5260                 }
5261                 for(j = i+1; cj = cs[j]; j++){
5262                     if(cj._nodup != d){
5263                         cj._nodup = d;
5264                         r[++ri] = cj;
5265                     }
5266                 }
5267                 return r;
5268             }
5269         }
5270         return r;
5271     }
5272
5273     function quickDiffIEXml(c1, c2){
5274         var d = ++key;
5275         for(var i = 0, len = c1.length; i < len; i++){
5276             c1[i].setAttribute("_qdiff", d);
5277         }
5278         var r = [];
5279         for(var i = 0, len = c2.length; i < len; i++){
5280             if(c2[i].getAttribute("_qdiff") != d){
5281                 r[r.length] = c2[i];
5282             }
5283         }
5284         for(var i = 0, len = c1.length; i < len; i++){
5285            c1[i].removeAttribute("_qdiff");
5286         }
5287         return r;
5288     }
5289
5290     function quickDiff(c1, c2){
5291         var len1 = c1.length;
5292         if(!len1){
5293             return c2;
5294         }
5295         if(isIE && c1[0].selectSingleNode){
5296             return quickDiffIEXml(c1, c2);
5297         }
5298         var d = ++key;
5299         for(var i = 0; i < len1; i++){
5300             c1[i]._qdiff = d;
5301         }
5302         var r = [];
5303         for(var i = 0, len = c2.length; i < len; i++){
5304             if(c2[i]._qdiff != d){
5305                 r[r.length] = c2[i];
5306             }
5307         }
5308         return r;
5309     }
5310
5311     function quickId(ns, mode, root, id){
5312         if(ns == root){
5313            var d = root.ownerDocument || root;
5314            return d.getElementById(id);
5315         }
5316         ns = getNodes(ns, mode, "*");
5317         return byId(ns, null, id);
5318     }
5319
5320     return {
5321         getStyle : function(el, name){
5322             return Roo.fly(el).getStyle(name);
5323         },
5324         /**
5325          * Compiles a selector/xpath query into a reusable function. The returned function
5326          * takes one parameter "root" (optional), which is the context node from where the query should start.
5327          * @param {String} selector The selector/xpath query
5328          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5329          * @return {Function}
5330          */
5331         compile : function(path, type){
5332             type = type || "select";
5333             
5334             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5335             var q = path, mode, lq;
5336             var tk = Roo.DomQuery.matchers;
5337             var tklen = tk.length;
5338             var mm;
5339
5340             // accept leading mode switch
5341             var lmode = q.match(modeRe);
5342             if(lmode && lmode[1]){
5343                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5344                 q = q.replace(lmode[1], "");
5345             }
5346             // strip leading slashes
5347             while(path.substr(0, 1)=="/"){
5348                 path = path.substr(1);
5349             }
5350
5351             while(q && lq != q){
5352                 lq = q;
5353                 var tm = q.match(tagTokenRe);
5354                 if(type == "select"){
5355                     if(tm){
5356                         if(tm[1] == "#"){
5357                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5358                         }else{
5359                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5360                         }
5361                         q = q.replace(tm[0], "");
5362                     }else if(q.substr(0, 1) != '@'){
5363                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5364                     }
5365                 }else{
5366                     if(tm){
5367                         if(tm[1] == "#"){
5368                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5369                         }else{
5370                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5371                         }
5372                         q = q.replace(tm[0], "");
5373                     }
5374                 }
5375                 while(!(mm = q.match(modeRe))){
5376                     var matched = false;
5377                     for(var j = 0; j < tklen; j++){
5378                         var t = tk[j];
5379                         var m = q.match(t.re);
5380                         if(m){
5381                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5382                                                     return m[i];
5383                                                 });
5384                             q = q.replace(m[0], "");
5385                             matched = true;
5386                             break;
5387                         }
5388                     }
5389                     // prevent infinite loop on bad selector
5390                     if(!matched){
5391                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5392                     }
5393                 }
5394                 if(mm[1]){
5395                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5396                     q = q.replace(mm[1], "");
5397                 }
5398             }
5399             fn[fn.length] = "return nodup(n);\n}";
5400             
5401              /** 
5402               * list of variables that need from compression as they are used by eval.
5403              *  eval:var:batch 
5404              *  eval:var:nodup
5405              *  eval:var:byTag
5406              *  eval:var:ById
5407              *  eval:var:getNodes
5408              *  eval:var:quickId
5409              *  eval:var:mode
5410              *  eval:var:root
5411              *  eval:var:n
5412              *  eval:var:byClassName
5413              *  eval:var:byPseudo
5414              *  eval:var:byAttribute
5415              *  eval:var:attrValue
5416              * 
5417              **/ 
5418             eval(fn.join(""));
5419             return f;
5420         },
5421
5422         /**
5423          * Selects a group of elements.
5424          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @return {Array}
5427          */
5428         select : function(path, root, type){
5429             if(!root || root == document){
5430                 root = document;
5431             }
5432             if(typeof root == "string"){
5433                 root = document.getElementById(root);
5434             }
5435             var paths = path.split(",");
5436             var results = [];
5437             for(var i = 0, len = paths.length; i < len; i++){
5438                 var p = paths[i].replace(trimRe, "");
5439                 if(!cache[p]){
5440                     cache[p] = Roo.DomQuery.compile(p);
5441                     if(!cache[p]){
5442                         throw p + " is not a valid selector";
5443                     }
5444                 }
5445                 var result = cache[p](root);
5446                 if(result && result != document){
5447                     results = results.concat(result);
5448                 }
5449             }
5450             if(paths.length > 1){
5451                 return nodup(results);
5452             }
5453             return results;
5454         },
5455
5456         /**
5457          * Selects a single element.
5458          * @param {String} selector The selector/xpath query
5459          * @param {Node} root (optional) The start of the query (defaults to document).
5460          * @return {Element}
5461          */
5462         selectNode : function(path, root){
5463             return Roo.DomQuery.select(path, root)[0];
5464         },
5465
5466         /**
5467          * Selects the value of a node, optionally replacing null with the defaultValue.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {String} defaultValue
5471          */
5472         selectValue : function(path, root, defaultValue){
5473             path = path.replace(trimRe, "");
5474             if(!valueCache[path]){
5475                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5476             }
5477             var n = valueCache[path](root);
5478             n = n[0] ? n[0] : n;
5479             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5480             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5481         },
5482
5483         /**
5484          * Selects the value of a node, parsing integers and floats.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @param {Number} defaultValue
5488          * @return {Number}
5489          */
5490         selectNumber : function(path, root, defaultValue){
5491             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5492             return parseFloat(v);
5493         },
5494
5495         /**
5496          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5497          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5498          * @param {String} selector The simple selector to test
5499          * @return {Boolean}
5500          */
5501         is : function(el, ss){
5502             if(typeof el == "string"){
5503                 el = document.getElementById(el);
5504             }
5505             var isArray = (el instanceof Array);
5506             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5507             return isArray ? (result.length == el.length) : (result.length > 0);
5508         },
5509
5510         /**
5511          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5512          * @param {Array} el An array of elements to filter
5513          * @param {String} selector The simple selector to test
5514          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5515          * the selector instead of the ones that match
5516          * @return {Array}
5517          */
5518         filter : function(els, ss, nonMatches){
5519             ss = ss.replace(trimRe, "");
5520             if(!simpleCache[ss]){
5521                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5522             }
5523             var result = simpleCache[ss](els);
5524             return nonMatches ? quickDiff(result, els) : result;
5525         },
5526
5527         /**
5528          * Collection of matching regular expressions and code snippets.
5529          */
5530         matchers : [{
5531                 re: /^\.([\w-]+)/,
5532                 select: 'n = byClassName(n, null, " {1} ");'
5533             }, {
5534                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5535                 select: 'n = byPseudo(n, "{1}", "{2}");'
5536             },{
5537                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5538                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5539             }, {
5540                 re: /^#([\w-]+)/,
5541                 select: 'n = byId(n, null, "{1}");'
5542             },{
5543                 re: /^@([\w-]+)/,
5544                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5545             }
5546         ],
5547
5548         /**
5549          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5550          * 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;.
5551          */
5552         operators : {
5553             "=" : function(a, v){
5554                 return a == v;
5555             },
5556             "!=" : function(a, v){
5557                 return a != v;
5558             },
5559             "^=" : function(a, v){
5560                 return a && a.substr(0, v.length) == v;
5561             },
5562             "$=" : function(a, v){
5563                 return a && a.substr(a.length-v.length) == v;
5564             },
5565             "*=" : function(a, v){
5566                 return a && a.indexOf(v) !== -1;
5567             },
5568             "%=" : function(a, v){
5569                 return (a % v) == 0;
5570             },
5571             "|=" : function(a, v){
5572                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5573             },
5574             "~=" : function(a, v){
5575                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5576             }
5577         },
5578
5579         /**
5580          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5581          * and the argument (if any) supplied in the selector.
5582          */
5583         pseudos : {
5584             "first-child" : function(c){
5585                 var r = [], ri = -1, n;
5586                 for(var i = 0, ci; ci = n = c[i]; i++){
5587                     while((n = n.previousSibling) && n.nodeType != 1);
5588                     if(!n){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "last-child" : function(c){
5596                 var r = [], ri = -1, n;
5597                 for(var i = 0, ci; ci = n = c[i]; i++){
5598                     while((n = n.nextSibling) && n.nodeType != 1);
5599                     if(!n){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "nth-child" : function(c, a) {
5607                 var r = [], ri = -1;
5608                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5609                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5610                 for(var i = 0, n; n = c[i]; i++){
5611                     var pn = n.parentNode;
5612                     if (batch != pn._batch) {
5613                         var j = 0;
5614                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5615                             if(cn.nodeType == 1){
5616                                cn.nodeIndex = ++j;
5617                             }
5618                         }
5619                         pn._batch = batch;
5620                     }
5621                     if (f == 1) {
5622                         if (l == 0 || n.nodeIndex == l){
5623                             r[++ri] = n;
5624                         }
5625                     } else if ((n.nodeIndex + l) % f == 0){
5626                         r[++ri] = n;
5627                     }
5628                 }
5629
5630                 return r;
5631             },
5632
5633             "only-child" : function(c){
5634                 var r = [], ri = -1;;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(!prev(ci) && !next(ci)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "empty" : function(c){
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var cns = ci.childNodes, j = 0, cn, empty = true;
5647                     while(cn = cns[j]){
5648                         ++j;
5649                         if(cn.nodeType == 1 || cn.nodeType == 3){
5650                             empty = false;
5651                             break;
5652                         }
5653                     }
5654                     if(empty){
5655                         r[++ri] = ci;
5656                     }
5657                 }
5658                 return r;
5659             },
5660
5661             "contains" : function(c, v){
5662                 var r = [], ri = -1;
5663                 for(var i = 0, ci; ci = c[i]; i++){
5664                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5665                         r[++ri] = ci;
5666                     }
5667                 }
5668                 return r;
5669             },
5670
5671             "nodeValue" : function(c, v){
5672                 var r = [], ri = -1;
5673                 for(var i = 0, ci; ci = c[i]; i++){
5674                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5675                         r[++ri] = ci;
5676                     }
5677                 }
5678                 return r;
5679             },
5680
5681             "checked" : function(c){
5682                 var r = [], ri = -1;
5683                 for(var i = 0, ci; ci = c[i]; i++){
5684                     if(ci.checked == true){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "not" : function(c, ss){
5692                 return Roo.DomQuery.filter(c, ss, true);
5693             },
5694
5695             "odd" : function(c){
5696                 return this["nth-child"](c, "odd");
5697             },
5698
5699             "even" : function(c){
5700                 return this["nth-child"](c, "even");
5701             },
5702
5703             "nth" : function(c, a){
5704                 return c[a-1] || [];
5705             },
5706
5707             "first" : function(c){
5708                 return c[0] || [];
5709             },
5710
5711             "last" : function(c){
5712                 return c[c.length-1] || [];
5713             },
5714
5715             "has" : function(c, ss){
5716                 var s = Roo.DomQuery.select;
5717                 var r = [], ri = -1;
5718                 for(var i = 0, ci; ci = c[i]; i++){
5719                     if(s(ss, ci).length > 0){
5720                         r[++ri] = ci;
5721                     }
5722                 }
5723                 return r;
5724             },
5725
5726             "next" : function(c, ss){
5727                 var is = Roo.DomQuery.is;
5728                 var r = [], ri = -1;
5729                 for(var i = 0, ci; ci = c[i]; i++){
5730                     var n = next(ci);
5731                     if(n && is(n, ss)){
5732                         r[++ri] = ci;
5733                     }
5734                 }
5735                 return r;
5736             },
5737
5738             "prev" : function(c, ss){
5739                 var is = Roo.DomQuery.is;
5740                 var r = [], ri = -1;
5741                 for(var i = 0, ci; ci = c[i]; i++){
5742                     var n = prev(ci);
5743                     if(n && is(n, ss)){
5744                         r[++ri] = ci;
5745                     }
5746                 }
5747                 return r;
5748             }
5749         }
5750     };
5751 }();
5752
5753 /**
5754  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5755  * @param {String} path The selector/xpath query
5756  * @param {Node} root (optional) The start of the query (defaults to document).
5757  * @return {Array}
5758  * @member Roo
5759  * @method query
5760  */
5761 Roo.query = Roo.DomQuery.select;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772
5773 /**
5774  * @class Roo.util.Observable
5775  * Base class that provides a common interface for publishing events. Subclasses are expected to
5776  * to have a property "events" with all the events defined.<br>
5777  * For example:
5778  * <pre><code>
5779  Employee = function(name){
5780     this.name = name;
5781     this.addEvents({
5782         "fired" : true,
5783         "quit" : true
5784     });
5785  }
5786  Roo.extend(Employee, Roo.util.Observable);
5787 </code></pre>
5788  * @param {Object} config properties to use (incuding events / listeners)
5789  */
5790
5791 Roo.util.Observable = function(cfg){
5792     
5793     cfg = cfg|| {};
5794     this.addEvents(cfg.events || {});
5795     if (cfg.events) {
5796         delete cfg.events; // make sure
5797     }
5798      
5799     Roo.apply(this, cfg);
5800     
5801     if(this.listeners){
5802         this.on(this.listeners);
5803         delete this.listeners;
5804     }
5805 };
5806 Roo.util.Observable.prototype = {
5807     /** 
5808  * @cfg {Object} listeners  list of events and functions to call for this object, 
5809  * For example :
5810  * <pre><code>
5811     listeners :  { 
5812        'click' : function(e) {
5813            ..... 
5814         } ,
5815         .... 
5816     } 
5817   </code></pre>
5818  */
5819     
5820     
5821     /**
5822      * Fires the specified event with the passed parameters (minus the event name).
5823      * @param {String} eventName
5824      * @param {Object...} args Variable number of parameters are passed to handlers
5825      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5826      */
5827     fireEvent : function(){
5828         var ce = this.events[arguments[0].toLowerCase()];
5829         if(typeof ce == "object"){
5830             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5831         }else{
5832             return true;
5833         }
5834     },
5835
5836     // private
5837     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5838
5839     /**
5840      * Appends an event handler to this component
5841      * @param {String}   eventName The type of event to listen for
5842      * @param {Function} handler The method the event invokes
5843      * @param {Object}   scope (optional) The scope in which to execute the handler
5844      * function. The handler function's "this" context.
5845      * @param {Object}   options (optional) An object containing handler configuration
5846      * properties. This may contain any of the following properties:<ul>
5847      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5848      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5849      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5850      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5851      * by the specified number of milliseconds. If the event fires again within that time, the original
5852      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5853      * </ul><br>
5854      * <p>
5855      * <b>Combining Options</b><br>
5856      * Using the options argument, it is possible to combine different types of listeners:<br>
5857      * <br>
5858      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5859                 <pre><code>
5860                 el.on('click', this.onClick, this, {
5861                         single: true,
5862                 delay: 100,
5863                 forumId: 4
5864                 });
5865                 </code></pre>
5866      * <p>
5867      * <b>Attaching multiple handlers in 1 call</b><br>
5868      * The method also allows for a single argument to be passed which is a config object containing properties
5869      * which specify multiple handlers.
5870      * <pre><code>
5871                 el.on({
5872                         'click': {
5873                         fn: this.onClick,
5874                         scope: this,
5875                         delay: 100
5876                 }, 
5877                 'mouseover': {
5878                         fn: this.onMouseOver,
5879                         scope: this
5880                 },
5881                 'mouseout': {
5882                         fn: this.onMouseOut,
5883                         scope: this
5884                 }
5885                 });
5886                 </code></pre>
5887      * <p>
5888      * Or a shorthand syntax which passes the same scope object to all handlers:
5889         <pre><code>
5890                 el.on({
5891                         'click': this.onClick,
5892                 'mouseover': this.onMouseOver,
5893                 'mouseout': this.onMouseOut,
5894                 scope: this
5895                 });
5896                 </code></pre>
5897      */
5898     addListener : function(eventName, fn, scope, o){
5899         if(typeof eventName == "object"){
5900             o = eventName;
5901             for(var e in o){
5902                 if(this.filterOptRe.test(e)){
5903                     continue;
5904                 }
5905                 if(typeof o[e] == "function"){
5906                     // shared options
5907                     this.addListener(e, o[e], o.scope,  o);
5908                 }else{
5909                     // individual options
5910                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5911                 }
5912             }
5913             return;
5914         }
5915         o = (!o || typeof o == "boolean") ? {} : o;
5916         eventName = eventName.toLowerCase();
5917         var ce = this.events[eventName] || true;
5918         if(typeof ce == "boolean"){
5919             ce = new Roo.util.Event(this, eventName);
5920             this.events[eventName] = ce;
5921         }
5922         ce.addListener(fn, scope, o);
5923     },
5924
5925     /**
5926      * Removes a listener
5927      * @param {String}   eventName     The type of event to listen for
5928      * @param {Function} handler        The handler to remove
5929      * @param {Object}   scope  (optional) The scope (this object) for the handler
5930      */
5931     removeListener : function(eventName, fn, scope){
5932         var ce = this.events[eventName.toLowerCase()];
5933         if(typeof ce == "object"){
5934             ce.removeListener(fn, scope);
5935         }
5936     },
5937
5938     /**
5939      * Removes all listeners for this object
5940      */
5941     purgeListeners : function(){
5942         for(var evt in this.events){
5943             if(typeof this.events[evt] == "object"){
5944                  this.events[evt].clearListeners();
5945             }
5946         }
5947     },
5948
5949     relayEvents : function(o, events){
5950         var createHandler = function(ename){
5951             return function(){
5952                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5953             };
5954         };
5955         for(var i = 0, len = events.length; i < len; i++){
5956             var ename = events[i];
5957             if(!this.events[ename]){ this.events[ename] = true; };
5958             o.on(ename, createHandler(ename), this);
5959         }
5960     },
5961
5962     /**
5963      * Used to define events on this Observable
5964      * @param {Object} object The object with the events defined
5965      */
5966     addEvents : function(o){
5967         if(!this.events){
5968             this.events = {};
5969         }
5970         Roo.applyIf(this.events, o);
5971     },
5972
5973     /**
5974      * Checks to see if this object has any listeners for a specified event
5975      * @param {String} eventName The name of the event to check for
5976      * @return {Boolean} True if the event is being listened for, else false
5977      */
5978     hasListener : function(eventName){
5979         var e = this.events[eventName];
5980         return typeof e == "object" && e.listeners.length > 0;
5981     }
5982 };
5983 /**
5984  * Appends an event handler to this element (shorthand for addListener)
5985  * @param {String}   eventName     The type of event to listen for
5986  * @param {Function} handler        The method the event invokes
5987  * @param {Object}   scope (optional) The scope in which to execute the handler
5988  * function. The handler function's "this" context.
5989  * @param {Object}   options  (optional)
5990  * @method
5991  */
5992 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5993 /**
5994  * Removes a listener (shorthand for removeListener)
5995  * @param {String}   eventName     The type of event to listen for
5996  * @param {Function} handler        The handler to remove
5997  * @param {Object}   scope  (optional) The scope (this object) for the handler
5998  * @method
5999  */
6000 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6001
6002 /**
6003  * Starts capture on the specified Observable. All events will be passed
6004  * to the supplied function with the event name + standard signature of the event
6005  * <b>before</b> the event is fired. If the supplied function returns false,
6006  * the event will not fire.
6007  * @param {Observable} o The Observable to capture
6008  * @param {Function} fn The function to call
6009  * @param {Object} scope (optional) The scope (this object) for the fn
6010  * @static
6011  */
6012 Roo.util.Observable.capture = function(o, fn, scope){
6013     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6014 };
6015
6016 /**
6017  * Removes <b>all</b> added captures from the Observable.
6018  * @param {Observable} o The Observable to release
6019  * @static
6020  */
6021 Roo.util.Observable.releaseCapture = function(o){
6022     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6023 };
6024
6025 (function(){
6026
6027     var createBuffered = function(h, o, scope){
6028         var task = new Roo.util.DelayedTask();
6029         return function(){
6030             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6031         };
6032     };
6033
6034     var createSingle = function(h, e, fn, scope){
6035         return function(){
6036             e.removeListener(fn, scope);
6037             return h.apply(scope, arguments);
6038         };
6039     };
6040
6041     var createDelayed = function(h, o, scope){
6042         return function(){
6043             var args = Array.prototype.slice.call(arguments, 0);
6044             setTimeout(function(){
6045                 h.apply(scope, args);
6046             }, o.delay || 10);
6047         };
6048     };
6049
6050     Roo.util.Event = function(obj, name){
6051         this.name = name;
6052         this.obj = obj;
6053         this.listeners = [];
6054     };
6055
6056     Roo.util.Event.prototype = {
6057         addListener : function(fn, scope, options){
6058             var o = options || {};
6059             scope = scope || this.obj;
6060             if(!this.isListening(fn, scope)){
6061                 var l = {fn: fn, scope: scope, options: o};
6062                 var h = fn;
6063                 if(o.delay){
6064                     h = createDelayed(h, o, scope);
6065                 }
6066                 if(o.single){
6067                     h = createSingle(h, this, fn, scope);
6068                 }
6069                 if(o.buffer){
6070                     h = createBuffered(h, o, scope);
6071                 }
6072                 l.fireFn = h;
6073                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6074                     this.listeners.push(l);
6075                 }else{
6076                     this.listeners = this.listeners.slice(0);
6077                     this.listeners.push(l);
6078                 }
6079             }
6080         },
6081
6082         findListener : function(fn, scope){
6083             scope = scope || this.obj;
6084             var ls = this.listeners;
6085             for(var i = 0, len = ls.length; i < len; i++){
6086                 var l = ls[i];
6087                 if(l.fn == fn && l.scope == scope){
6088                     return i;
6089                 }
6090             }
6091             return -1;
6092         },
6093
6094         isListening : function(fn, scope){
6095             return this.findListener(fn, scope) != -1;
6096         },
6097
6098         removeListener : function(fn, scope){
6099             var index;
6100             if((index = this.findListener(fn, scope)) != -1){
6101                 if(!this.firing){
6102                     this.listeners.splice(index, 1);
6103                 }else{
6104                     this.listeners = this.listeners.slice(0);
6105                     this.listeners.splice(index, 1);
6106                 }
6107                 return true;
6108             }
6109             return false;
6110         },
6111
6112         clearListeners : function(){
6113             this.listeners = [];
6114         },
6115
6116         fire : function(){
6117             var ls = this.listeners, scope, len = ls.length;
6118             if(len > 0){
6119                 this.firing = true;
6120                 var args = Array.prototype.slice.call(arguments, 0);                
6121                 for(var i = 0; i < len; i++){
6122                     var l = ls[i];
6123                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6124                         this.firing = false;
6125                         return false;
6126                     }
6127                 }
6128                 this.firing = false;
6129             }
6130             return true;
6131         }
6132     };
6133 })();/*
6134  * RooJS Library 
6135  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6136  *
6137  * Licence LGPL 
6138  *
6139  */
6140  
6141 /**
6142  * @class Roo.Document
6143  * @extends Roo.util.Observable
6144  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6145  * 
6146  * @param {Object} config the methods and properties of the 'base' class for the application.
6147  * 
6148  *  Generic Page handler - implement this to start your app..
6149  * 
6150  * eg.
6151  *  MyProject = new Roo.Document({
6152         events : {
6153             'load' : true // your events..
6154         },
6155         listeners : {
6156             'ready' : function() {
6157                 // fired on Roo.onReady()
6158             }
6159         }
6160  * 
6161  */
6162 Roo.Document = function(cfg) {
6163      
6164     this.addEvents({ 
6165         'ready' : true
6166     });
6167     Roo.util.Observable.call(this,cfg);
6168     
6169     var _this = this;
6170     
6171     Roo.onReady(function() {
6172         _this.fireEvent('ready');
6173     },null,false);
6174     
6175     
6176 }
6177
6178 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6179  * Based on:
6180  * Ext JS Library 1.1.1
6181  * Copyright(c) 2006-2007, Ext JS, LLC.
6182  *
6183  * Originally Released Under LGPL - original licence link has changed is not relivant.
6184  *
6185  * Fork - LGPL
6186  * <script type="text/javascript">
6187  */
6188
6189 /**
6190  * @class Roo.EventManager
6191  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6192  * several useful events directly.
6193  * See {@link Roo.EventObject} for more details on normalized event objects.
6194  * @singleton
6195  */
6196 Roo.EventManager = function(){
6197     var docReadyEvent, docReadyProcId, docReadyState = false;
6198     var resizeEvent, resizeTask, textEvent, textSize;
6199     var E = Roo.lib.Event;
6200     var D = Roo.lib.Dom;
6201
6202     
6203     
6204
6205     var fireDocReady = function(){
6206         if(!docReadyState){
6207             docReadyState = true;
6208             Roo.isReady = true;
6209             if(docReadyProcId){
6210                 clearInterval(docReadyProcId);
6211             }
6212             if(Roo.isGecko || Roo.isOpera) {
6213                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6214             }
6215             if(Roo.isIE){
6216                 var defer = document.getElementById("ie-deferred-loader");
6217                 if(defer){
6218                     defer.onreadystatechange = null;
6219                     defer.parentNode.removeChild(defer);
6220                 }
6221             }
6222             if(docReadyEvent){
6223                 docReadyEvent.fire();
6224                 docReadyEvent.clearListeners();
6225             }
6226         }
6227     };
6228     
6229     var initDocReady = function(){
6230         docReadyEvent = new Roo.util.Event();
6231         if(Roo.isGecko || Roo.isOpera) {
6232             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6233         }else if(Roo.isIE){
6234             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6235             var defer = document.getElementById("ie-deferred-loader");
6236             defer.onreadystatechange = function(){
6237                 if(this.readyState == "complete"){
6238                     fireDocReady();
6239                 }
6240             };
6241         }else if(Roo.isSafari){ 
6242             docReadyProcId = setInterval(function(){
6243                 var rs = document.readyState;
6244                 if(rs == "complete") {
6245                     fireDocReady();     
6246                  }
6247             }, 10);
6248         }
6249         // no matter what, make sure it fires on load
6250         E.on(window, "load", fireDocReady);
6251     };
6252
6253     var createBuffered = function(h, o){
6254         var task = new Roo.util.DelayedTask(h);
6255         return function(e){
6256             // create new event object impl so new events don't wipe out properties
6257             e = new Roo.EventObjectImpl(e);
6258             task.delay(o.buffer, h, null, [e]);
6259         };
6260     };
6261
6262     var createSingle = function(h, el, ename, fn){
6263         return function(e){
6264             Roo.EventManager.removeListener(el, ename, fn);
6265             h(e);
6266         };
6267     };
6268
6269     var createDelayed = function(h, o){
6270         return function(e){
6271             // create new event object impl so new events don't wipe out properties
6272             e = new Roo.EventObjectImpl(e);
6273             setTimeout(function(){
6274                 h(e);
6275             }, o.delay || 10);
6276         };
6277     };
6278     var transitionEndVal = false;
6279     
6280     var transitionEnd = function()
6281     {
6282         if (transitionEndVal) {
6283             return transitionEndVal;
6284         }
6285         var el = document.createElement('div');
6286
6287         var transEndEventNames = {
6288             WebkitTransition : 'webkitTransitionEnd',
6289             MozTransition    : 'transitionend',
6290             OTransition      : 'oTransitionEnd otransitionend',
6291             transition       : 'transitionend'
6292         };
6293     
6294         for (var name in transEndEventNames) {
6295             if (el.style[name] !== undefined) {
6296                 transitionEndVal = transEndEventNames[name];
6297                 return  transitionEndVal ;
6298             }
6299         }
6300     }
6301     
6302
6303     var listen = function(element, ename, opt, fn, scope){
6304         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6305         fn = fn || o.fn; scope = scope || o.scope;
6306         var el = Roo.getDom(element);
6307         
6308         
6309         if(!el){
6310             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6311         }
6312         
6313         if (ename == 'transitionend') {
6314             ename = transitionEnd();
6315         }
6316         var h = function(e){
6317             e = Roo.EventObject.setEvent(e);
6318             var t;
6319             if(o.delegate){
6320                 t = e.getTarget(o.delegate, el);
6321                 if(!t){
6322                     return;
6323                 }
6324             }else{
6325                 t = e.target;
6326             }
6327             if(o.stopEvent === true){
6328                 e.stopEvent();
6329             }
6330             if(o.preventDefault === true){
6331                e.preventDefault();
6332             }
6333             if(o.stopPropagation === true){
6334                 e.stopPropagation();
6335             }
6336
6337             if(o.normalized === false){
6338                 e = e.browserEvent;
6339             }
6340
6341             fn.call(scope || el, e, t, o);
6342         };
6343         if(o.delay){
6344             h = createDelayed(h, o);
6345         }
6346         if(o.single){
6347             h = createSingle(h, el, ename, fn);
6348         }
6349         if(o.buffer){
6350             h = createBuffered(h, o);
6351         }
6352         
6353         fn._handlers = fn._handlers || [];
6354         
6355         
6356         fn._handlers.push([Roo.id(el), ename, h]);
6357         
6358         
6359          
6360         E.on(el, ename, h);
6361         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6362             el.addEventListener("DOMMouseScroll", h, false);
6363             E.on(window, 'unload', function(){
6364                 el.removeEventListener("DOMMouseScroll", h, false);
6365             });
6366         }
6367         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6368             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6369         }
6370         return h;
6371     };
6372
6373     var stopListening = function(el, ename, fn){
6374         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6375         if(hds){
6376             for(var i = 0, len = hds.length; i < len; i++){
6377                 var h = hds[i];
6378                 if(h[0] == id && h[1] == ename){
6379                     hd = h[2];
6380                     hds.splice(i, 1);
6381                     break;
6382                 }
6383             }
6384         }
6385         E.un(el, ename, hd);
6386         el = Roo.getDom(el);
6387         if(ename == "mousewheel" && el.addEventListener){
6388             el.removeEventListener("DOMMouseScroll", hd, false);
6389         }
6390         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6391             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6392         }
6393     };
6394
6395     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6396     
6397     var pub = {
6398         
6399         
6400         /** 
6401          * Fix for doc tools
6402          * @scope Roo.EventManager
6403          */
6404         
6405         
6406         /** 
6407          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6408          * object with a Roo.EventObject
6409          * @param {Function} fn        The method the event invokes
6410          * @param {Object}   scope    An object that becomes the scope of the handler
6411          * @param {boolean}  override If true, the obj passed in becomes
6412          *                             the execution scope of the listener
6413          * @return {Function} The wrapped function
6414          * @deprecated
6415          */
6416         wrap : function(fn, scope, override){
6417             return function(e){
6418                 Roo.EventObject.setEvent(e);
6419                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6420             };
6421         },
6422         
6423         /**
6424      * Appends an event handler to an element (shorthand for addListener)
6425      * @param {String/HTMLElement}   element        The html element or id to assign the
6426      * @param {String}   eventName The type of event to listen for
6427      * @param {Function} handler The method the event invokes
6428      * @param {Object}   scope (optional) The scope in which to execute the handler
6429      * function. The handler function's "this" context.
6430      * @param {Object}   options (optional) An object containing handler configuration
6431      * properties. This may contain any of the following properties:<ul>
6432      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6433      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6434      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6435      * <li>preventDefault {Boolean} True to prevent the default action</li>
6436      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6437      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6438      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6439      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6440      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6441      * by the specified number of milliseconds. If the event fires again within that time, the original
6442      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6443      * </ul><br>
6444      * <p>
6445      * <b>Combining Options</b><br>
6446      * Using the options argument, it is possible to combine different types of listeners:<br>
6447      * <br>
6448      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6449      * Code:<pre><code>
6450 el.on('click', this.onClick, this, {
6451     single: true,
6452     delay: 100,
6453     stopEvent : true,
6454     forumId: 4
6455 });</code></pre>
6456      * <p>
6457      * <b>Attaching multiple handlers in 1 call</b><br>
6458       * The method also allows for a single argument to be passed which is a config object containing properties
6459      * which specify multiple handlers.
6460      * <p>
6461      * Code:<pre><code>
6462 el.on({
6463     'click' : {
6464         fn: this.onClick
6465         scope: this,
6466         delay: 100
6467     },
6468     'mouseover' : {
6469         fn: this.onMouseOver
6470         scope: this
6471     },
6472     'mouseout' : {
6473         fn: this.onMouseOut
6474         scope: this
6475     }
6476 });</code></pre>
6477      * <p>
6478      * Or a shorthand syntax:<br>
6479      * Code:<pre><code>
6480 el.on({
6481     'click' : this.onClick,
6482     'mouseover' : this.onMouseOver,
6483     'mouseout' : this.onMouseOut
6484     scope: this
6485 });</code></pre>
6486      */
6487         addListener : function(element, eventName, fn, scope, options){
6488             if(typeof eventName == "object"){
6489                 var o = eventName;
6490                 for(var e in o){
6491                     if(propRe.test(e)){
6492                         continue;
6493                     }
6494                     if(typeof o[e] == "function"){
6495                         // shared options
6496                         listen(element, e, o, o[e], o.scope);
6497                     }else{
6498                         // individual options
6499                         listen(element, e, o[e]);
6500                     }
6501                 }
6502                 return;
6503             }
6504             return listen(element, eventName, options, fn, scope);
6505         },
6506         
6507         /**
6508          * Removes an event handler
6509          *
6510          * @param {String/HTMLElement}   element        The id or html element to remove the 
6511          *                             event from
6512          * @param {String}   eventName     The type of event
6513          * @param {Function} fn
6514          * @return {Boolean} True if a listener was actually removed
6515          */
6516         removeListener : function(element, eventName, fn){
6517             return stopListening(element, eventName, fn);
6518         },
6519         
6520         /**
6521          * Fires when the document is ready (before onload and before images are loaded). Can be 
6522          * accessed shorthanded Roo.onReady().
6523          * @param {Function} fn        The method the event invokes
6524          * @param {Object}   scope    An  object that becomes the scope of the handler
6525          * @param {boolean}  options
6526          */
6527         onDocumentReady : function(fn, scope, options){
6528             if(docReadyState){ // if it already fired
6529                 docReadyEvent.addListener(fn, scope, options);
6530                 docReadyEvent.fire();
6531                 docReadyEvent.clearListeners();
6532                 return;
6533             }
6534             if(!docReadyEvent){
6535                 initDocReady();
6536             }
6537             docReadyEvent.addListener(fn, scope, options);
6538         },
6539         
6540         /**
6541          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6542          * @param {Function} fn        The method the event invokes
6543          * @param {Object}   scope    An object that becomes the scope of the handler
6544          * @param {boolean}  options
6545          */
6546         onWindowResize : function(fn, scope, options){
6547             if(!resizeEvent){
6548                 resizeEvent = new Roo.util.Event();
6549                 resizeTask = new Roo.util.DelayedTask(function(){
6550                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6551                 });
6552                 E.on(window, "resize", function(){
6553                     if(Roo.isIE){
6554                         resizeTask.delay(50);
6555                     }else{
6556                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6557                     }
6558                 });
6559             }
6560             resizeEvent.addListener(fn, scope, options);
6561         },
6562
6563         /**
6564          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6565          * @param {Function} fn        The method the event invokes
6566          * @param {Object}   scope    An object that becomes the scope of the handler
6567          * @param {boolean}  options
6568          */
6569         onTextResize : function(fn, scope, options){
6570             if(!textEvent){
6571                 textEvent = new Roo.util.Event();
6572                 var textEl = new Roo.Element(document.createElement('div'));
6573                 textEl.dom.className = 'x-text-resize';
6574                 textEl.dom.innerHTML = 'X';
6575                 textEl.appendTo(document.body);
6576                 textSize = textEl.dom.offsetHeight;
6577                 setInterval(function(){
6578                     if(textEl.dom.offsetHeight != textSize){
6579                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6580                     }
6581                 }, this.textResizeInterval);
6582             }
6583             textEvent.addListener(fn, scope, options);
6584         },
6585
6586         /**
6587          * Removes the passed window resize listener.
6588          * @param {Function} fn        The method the event invokes
6589          * @param {Object}   scope    The scope of handler
6590          */
6591         removeResizeListener : function(fn, scope){
6592             if(resizeEvent){
6593                 resizeEvent.removeListener(fn, scope);
6594             }
6595         },
6596
6597         // private
6598         fireResize : function(){
6599             if(resizeEvent){
6600                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6601             }   
6602         },
6603         /**
6604          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6605          */
6606         ieDeferSrc : false,
6607         /**
6608          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6609          */
6610         textResizeInterval : 50
6611     };
6612     
6613     /**
6614      * Fix for doc tools
6615      * @scopeAlias pub=Roo.EventManager
6616      */
6617     
6618      /**
6619      * Appends an event handler to an element (shorthand for addListener)
6620      * @param {String/HTMLElement}   element        The html element or id to assign the
6621      * @param {String}   eventName The type of event to listen for
6622      * @param {Function} handler The method the event invokes
6623      * @param {Object}   scope (optional) The scope in which to execute the handler
6624      * function. The handler function's "this" context.
6625      * @param {Object}   options (optional) An object containing handler configuration
6626      * properties. This may contain any of the following properties:<ul>
6627      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6628      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6629      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6630      * <li>preventDefault {Boolean} True to prevent the default action</li>
6631      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6632      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6633      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6634      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6635      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6636      * by the specified number of milliseconds. If the event fires again within that time, the original
6637      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6638      * </ul><br>
6639      * <p>
6640      * <b>Combining Options</b><br>
6641      * Using the options argument, it is possible to combine different types of listeners:<br>
6642      * <br>
6643      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6644      * Code:<pre><code>
6645 el.on('click', this.onClick, this, {
6646     single: true,
6647     delay: 100,
6648     stopEvent : true,
6649     forumId: 4
6650 });</code></pre>
6651      * <p>
6652      * <b>Attaching multiple handlers in 1 call</b><br>
6653       * The method also allows for a single argument to be passed which is a config object containing properties
6654      * which specify multiple handlers.
6655      * <p>
6656      * Code:<pre><code>
6657 el.on({
6658     'click' : {
6659         fn: this.onClick
6660         scope: this,
6661         delay: 100
6662     },
6663     'mouseover' : {
6664         fn: this.onMouseOver
6665         scope: this
6666     },
6667     'mouseout' : {
6668         fn: this.onMouseOut
6669         scope: this
6670     }
6671 });</code></pre>
6672      * <p>
6673      * Or a shorthand syntax:<br>
6674      * Code:<pre><code>
6675 el.on({
6676     'click' : this.onClick,
6677     'mouseover' : this.onMouseOver,
6678     'mouseout' : this.onMouseOut
6679     scope: this
6680 });</code></pre>
6681      */
6682     pub.on = pub.addListener;
6683     pub.un = pub.removeListener;
6684
6685     pub.stoppedMouseDownEvent = new Roo.util.Event();
6686     return pub;
6687 }();
6688 /**
6689   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6690   * @param {Function} fn        The method the event invokes
6691   * @param {Object}   scope    An  object that becomes the scope of the handler
6692   * @param {boolean}  override If true, the obj passed in becomes
6693   *                             the execution scope of the listener
6694   * @member Roo
6695   * @method onReady
6696  */
6697 Roo.onReady = Roo.EventManager.onDocumentReady;
6698
6699 Roo.onReady(function(){
6700     var bd = Roo.get(document.body);
6701     if(!bd){ return; }
6702
6703     var cls = [
6704             Roo.isIE ? "roo-ie"
6705             : Roo.isIE11 ? "roo-ie11"
6706             : Roo.isEdge ? "roo-edge"
6707             : Roo.isGecko ? "roo-gecko"
6708             : Roo.isOpera ? "roo-opera"
6709             : Roo.isSafari ? "roo-safari" : ""];
6710
6711     if(Roo.isMac){
6712         cls.push("roo-mac");
6713     }
6714     if(Roo.isLinux){
6715         cls.push("roo-linux");
6716     }
6717     if(Roo.isIOS){
6718         cls.push("roo-ios");
6719     }
6720     if(Roo.isTouch){
6721         cls.push("roo-touch");
6722     }
6723     if(Roo.isBorderBox){
6724         cls.push('roo-border-box');
6725     }
6726     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6727         var p = bd.dom.parentNode;
6728         if(p){
6729             p.className += ' roo-strict';
6730         }
6731     }
6732     bd.addClass(cls.join(' '));
6733 });
6734
6735 /**
6736  * @class Roo.EventObject
6737  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6738  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6739  * Example:
6740  * <pre><code>
6741  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6742     e.preventDefault();
6743     var target = e.getTarget();
6744     ...
6745  }
6746  var myDiv = Roo.get("myDiv");
6747  myDiv.on("click", handleClick);
6748  //or
6749  Roo.EventManager.on("myDiv", 'click', handleClick);
6750  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6751  </code></pre>
6752  * @singleton
6753  */
6754 Roo.EventObject = function(){
6755     
6756     var E = Roo.lib.Event;
6757     
6758     // safari keypress events for special keys return bad keycodes
6759     var safariKeys = {
6760         63234 : 37, // left
6761         63235 : 39, // right
6762         63232 : 38, // up
6763         63233 : 40, // down
6764         63276 : 33, // page up
6765         63277 : 34, // page down
6766         63272 : 46, // delete
6767         63273 : 36, // home
6768         63275 : 35  // end
6769     };
6770
6771     // normalize button clicks
6772     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6773                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6774
6775     Roo.EventObjectImpl = function(e){
6776         if(e){
6777             this.setEvent(e.browserEvent || e);
6778         }
6779     };
6780     Roo.EventObjectImpl.prototype = {
6781         /**
6782          * Used to fix doc tools.
6783          * @scope Roo.EventObject.prototype
6784          */
6785             
6786
6787         
6788         
6789         /** The normal browser event */
6790         browserEvent : null,
6791         /** The button pressed in a mouse event */
6792         button : -1,
6793         /** True if the shift key was down during the event */
6794         shiftKey : false,
6795         /** True if the control key was down during the event */
6796         ctrlKey : false,
6797         /** True if the alt key was down during the event */
6798         altKey : false,
6799
6800         /** Key constant 
6801         * @type Number */
6802         BACKSPACE : 8,
6803         /** Key constant 
6804         * @type Number */
6805         TAB : 9,
6806         /** Key constant 
6807         * @type Number */
6808         RETURN : 13,
6809         /** Key constant 
6810         * @type Number */
6811         ENTER : 13,
6812         /** Key constant 
6813         * @type Number */
6814         SHIFT : 16,
6815         /** Key constant 
6816         * @type Number */
6817         CONTROL : 17,
6818         /** Key constant 
6819         * @type Number */
6820         ESC : 27,
6821         /** Key constant 
6822         * @type Number */
6823         SPACE : 32,
6824         /** Key constant 
6825         * @type Number */
6826         PAGEUP : 33,
6827         /** Key constant 
6828         * @type Number */
6829         PAGEDOWN : 34,
6830         /** Key constant 
6831         * @type Number */
6832         END : 35,
6833         /** Key constant 
6834         * @type Number */
6835         HOME : 36,
6836         /** Key constant 
6837         * @type Number */
6838         LEFT : 37,
6839         /** Key constant 
6840         * @type Number */
6841         UP : 38,
6842         /** Key constant 
6843         * @type Number */
6844         RIGHT : 39,
6845         /** Key constant 
6846         * @type Number */
6847         DOWN : 40,
6848         /** Key constant 
6849         * @type Number */
6850         DELETE : 46,
6851         /** Key constant 
6852         * @type Number */
6853         F5 : 116,
6854
6855            /** @private */
6856         setEvent : function(e){
6857             if(e == this || (e && e.browserEvent)){ // already wrapped
6858                 return e;
6859             }
6860             this.browserEvent = e;
6861             if(e){
6862                 // normalize buttons
6863                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6864                 if(e.type == 'click' && this.button == -1){
6865                     this.button = 0;
6866                 }
6867                 this.type = e.type;
6868                 this.shiftKey = e.shiftKey;
6869                 // mac metaKey behaves like ctrlKey
6870                 this.ctrlKey = e.ctrlKey || e.metaKey;
6871                 this.altKey = e.altKey;
6872                 // in getKey these will be normalized for the mac
6873                 this.keyCode = e.keyCode;
6874                 // keyup warnings on firefox.
6875                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6876                 // cache the target for the delayed and or buffered events
6877                 this.target = E.getTarget(e);
6878                 // same for XY
6879                 this.xy = E.getXY(e);
6880             }else{
6881                 this.button = -1;
6882                 this.shiftKey = false;
6883                 this.ctrlKey = false;
6884                 this.altKey = false;
6885                 this.keyCode = 0;
6886                 this.charCode =0;
6887                 this.target = null;
6888                 this.xy = [0, 0];
6889             }
6890             return this;
6891         },
6892
6893         /**
6894          * Stop the event (preventDefault and stopPropagation)
6895          */
6896         stopEvent : function(){
6897             if(this.browserEvent){
6898                 if(this.browserEvent.type == 'mousedown'){
6899                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6900                 }
6901                 E.stopEvent(this.browserEvent);
6902             }
6903         },
6904
6905         /**
6906          * Prevents the browsers default handling of the event.
6907          */
6908         preventDefault : function(){
6909             if(this.browserEvent){
6910                 E.preventDefault(this.browserEvent);
6911             }
6912         },
6913
6914         /** @private */
6915         isNavKeyPress : function(){
6916             var k = this.keyCode;
6917             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6918             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6919         },
6920
6921         isSpecialKey : function(){
6922             var k = this.keyCode;
6923             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6924             (k == 16) || (k == 17) ||
6925             (k >= 18 && k <= 20) ||
6926             (k >= 33 && k <= 35) ||
6927             (k >= 36 && k <= 39) ||
6928             (k >= 44 && k <= 45);
6929         },
6930         /**
6931          * Cancels bubbling of the event.
6932          */
6933         stopPropagation : function(){
6934             if(this.browserEvent){
6935                 if(this.type == 'mousedown'){
6936                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6937                 }
6938                 E.stopPropagation(this.browserEvent);
6939             }
6940         },
6941
6942         /**
6943          * Gets the key code for the event.
6944          * @return {Number}
6945          */
6946         getCharCode : function(){
6947             return this.charCode || this.keyCode;
6948         },
6949
6950         /**
6951          * Returns a normalized keyCode for the event.
6952          * @return {Number} The key code
6953          */
6954         getKey : function(){
6955             var k = this.keyCode || this.charCode;
6956             return Roo.isSafari ? (safariKeys[k] || k) : k;
6957         },
6958
6959         /**
6960          * Gets the x coordinate of the event.
6961          * @return {Number}
6962          */
6963         getPageX : function(){
6964             return this.xy[0];
6965         },
6966
6967         /**
6968          * Gets the y coordinate of the event.
6969          * @return {Number}
6970          */
6971         getPageY : function(){
6972             return this.xy[1];
6973         },
6974
6975         /**
6976          * Gets the time of the event.
6977          * @return {Number}
6978          */
6979         getTime : function(){
6980             if(this.browserEvent){
6981                 return E.getTime(this.browserEvent);
6982             }
6983             return null;
6984         },
6985
6986         /**
6987          * Gets the page coordinates of the event.
6988          * @return {Array} The xy values like [x, y]
6989          */
6990         getXY : function(){
6991             return this.xy;
6992         },
6993
6994         /**
6995          * Gets the target for the event.
6996          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6997          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6998                 search as a number or element (defaults to 10 || document.body)
6999          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7000          * @return {HTMLelement}
7001          */
7002         getTarget : function(selector, maxDepth, returnEl){
7003             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7004         },
7005         /**
7006          * Gets the related target.
7007          * @return {HTMLElement}
7008          */
7009         getRelatedTarget : function(){
7010             if(this.browserEvent){
7011                 return E.getRelatedTarget(this.browserEvent);
7012             }
7013             return null;
7014         },
7015
7016         /**
7017          * Normalizes mouse wheel delta across browsers
7018          * @return {Number} The delta
7019          */
7020         getWheelDelta : function(){
7021             var e = this.browserEvent;
7022             var delta = 0;
7023             if(e.wheelDelta){ /* IE/Opera. */
7024                 delta = e.wheelDelta/120;
7025             }else if(e.detail){ /* Mozilla case. */
7026                 delta = -e.detail/3;
7027             }
7028             return delta;
7029         },
7030
7031         /**
7032          * Returns true if the control, meta, shift or alt key was pressed during this event.
7033          * @return {Boolean}
7034          */
7035         hasModifier : function(){
7036             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7037         },
7038
7039         /**
7040          * Returns true if the target of this event equals el or is a child of el
7041          * @param {String/HTMLElement/Element} el
7042          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7043          * @return {Boolean}
7044          */
7045         within : function(el, related){
7046             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7047             return t && Roo.fly(el).contains(t);
7048         },
7049
7050         getPoint : function(){
7051             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7052         }
7053     };
7054
7055     return new Roo.EventObjectImpl();
7056 }();
7057             
7058     /*
7059  * Based on:
7060  * Ext JS Library 1.1.1
7061  * Copyright(c) 2006-2007, Ext JS, LLC.
7062  *
7063  * Originally Released Under LGPL - original licence link has changed is not relivant.
7064  *
7065  * Fork - LGPL
7066  * <script type="text/javascript">
7067  */
7068
7069  
7070 // was in Composite Element!??!?!
7071  
7072 (function(){
7073     var D = Roo.lib.Dom;
7074     var E = Roo.lib.Event;
7075     var A = Roo.lib.Anim;
7076
7077     // local style camelizing for speed
7078     var propCache = {};
7079     var camelRe = /(-[a-z])/gi;
7080     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7081     var view = document.defaultView;
7082
7083 /**
7084  * @class Roo.Element
7085  * Represents an Element in the DOM.<br><br>
7086  * Usage:<br>
7087 <pre><code>
7088 var el = Roo.get("my-div");
7089
7090 // or with getEl
7091 var el = getEl("my-div");
7092
7093 // or with a DOM element
7094 var el = Roo.get(myDivElement);
7095 </code></pre>
7096  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7097  * each call instead of constructing a new one.<br><br>
7098  * <b>Animations</b><br />
7099  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7100  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7101 <pre>
7102 Option    Default   Description
7103 --------- --------  ---------------------------------------------
7104 duration  .35       The duration of the animation in seconds
7105 easing    easeOut   The YUI easing method
7106 callback  none      A function to execute when the anim completes
7107 scope     this      The scope (this) of the callback function
7108 </pre>
7109 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7110 * manipulate the animation. Here's an example:
7111 <pre><code>
7112 var el = Roo.get("my-div");
7113
7114 // no animation
7115 el.setWidth(100);
7116
7117 // default animation
7118 el.setWidth(100, true);
7119
7120 // animation with some options set
7121 el.setWidth(100, {
7122     duration: 1,
7123     callback: this.foo,
7124     scope: this
7125 });
7126
7127 // using the "anim" property to get the Anim object
7128 var opt = {
7129     duration: 1,
7130     callback: this.foo,
7131     scope: this
7132 };
7133 el.setWidth(100, opt);
7134 ...
7135 if(opt.anim.isAnimated()){
7136     opt.anim.stop();
7137 }
7138 </code></pre>
7139 * <b> Composite (Collections of) Elements</b><br />
7140  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7141  * @constructor Create a new Element directly.
7142  * @param {String/HTMLElement} element
7143  * @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).
7144  */
7145     Roo.Element = function(element, forceNew){
7146         var dom = typeof element == "string" ?
7147                 document.getElementById(element) : element;
7148         if(!dom){ // invalid id/element
7149             return null;
7150         }
7151         var id = dom.id;
7152         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7153             return Roo.Element.cache[id];
7154         }
7155
7156         /**
7157          * The DOM element
7158          * @type HTMLElement
7159          */
7160         this.dom = dom;
7161
7162         /**
7163          * The DOM element ID
7164          * @type String
7165          */
7166         this.id = id || Roo.id(dom);
7167     };
7168
7169     var El = Roo.Element;
7170
7171     El.prototype = {
7172         /**
7173          * The element's default display mode  (defaults to "") 
7174          * @type String
7175          */
7176         originalDisplay : "",
7177
7178         
7179         // note this is overridden in BS version..
7180         visibilityMode : 1, 
7181         /**
7182          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7183          * @type String
7184          */
7185         defaultUnit : "px",
7186         
7187         /**
7188          * Sets the element's visibility mode. When setVisible() is called it
7189          * will use this to determine whether to set the visibility or the display property.
7190          * @param visMode Element.VISIBILITY or Element.DISPLAY
7191          * @return {Roo.Element} this
7192          */
7193         setVisibilityMode : function(visMode){
7194             this.visibilityMode = visMode;
7195             return this;
7196         },
7197         /**
7198          * Convenience method for setVisibilityMode(Element.DISPLAY)
7199          * @param {String} display (optional) What to set display to when visible
7200          * @return {Roo.Element} this
7201          */
7202         enableDisplayMode : function(display){
7203             this.setVisibilityMode(El.DISPLAY);
7204             if(typeof display != "undefined") { this.originalDisplay = display; }
7205             return this;
7206         },
7207
7208         /**
7209          * 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)
7210          * @param {String} selector The simple selector to test
7211          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7212                 search as a number or element (defaults to 10 || document.body)
7213          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7214          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7215          */
7216         findParent : function(simpleSelector, maxDepth, returnEl){
7217             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7218             maxDepth = maxDepth || 50;
7219             if(typeof maxDepth != "number"){
7220                 stopEl = Roo.getDom(maxDepth);
7221                 maxDepth = 10;
7222             }
7223             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7224                 if(dq.is(p, simpleSelector)){
7225                     return returnEl ? Roo.get(p) : p;
7226                 }
7227                 depth++;
7228                 p = p.parentNode;
7229             }
7230             return null;
7231         },
7232
7233
7234         /**
7235          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7236          * @param {String} selector The simple selector to test
7237          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7238                 search as a number or element (defaults to 10 || document.body)
7239          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7240          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7241          */
7242         findParentNode : function(simpleSelector, maxDepth, returnEl){
7243             var p = Roo.fly(this.dom.parentNode, '_internal');
7244             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7245         },
7246         
7247         /**
7248          * Looks at  the scrollable parent element
7249          */
7250         findScrollableParent : function()
7251         {
7252             var overflowRegex = /(auto|scroll)/;
7253             
7254             if(this.getStyle('position') === 'fixed'){
7255                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7256             }
7257             
7258             var excludeStaticParent = this.getStyle('position') === "absolute";
7259             
7260             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7261                 
7262                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7263                     continue;
7264                 }
7265                 
7266                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7267                     return parent;
7268                 }
7269                 
7270                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7271                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7272                 }
7273             }
7274             
7275             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7276         },
7277
7278         /**
7279          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7280          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7281          * @param {String} selector The simple selector to test
7282          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7283                 search as a number or element (defaults to 10 || document.body)
7284          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7285          */
7286         up : function(simpleSelector, maxDepth){
7287             return this.findParentNode(simpleSelector, maxDepth, true);
7288         },
7289
7290
7291
7292         /**
7293          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7294          * @param {String} selector The simple selector to test
7295          * @return {Boolean} True if this element matches the selector, else false
7296          */
7297         is : function(simpleSelector){
7298             return Roo.DomQuery.is(this.dom, simpleSelector);
7299         },
7300
7301         /**
7302          * Perform animation on this element.
7303          * @param {Object} args The YUI animation control args
7304          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7305          * @param {Function} onComplete (optional) Function to call when animation completes
7306          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7307          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7308          * @return {Roo.Element} this
7309          */
7310         animate : function(args, duration, onComplete, easing, animType){
7311             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7312             return this;
7313         },
7314
7315         /*
7316          * @private Internal animation call
7317          */
7318         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7319             animType = animType || 'run';
7320             opt = opt || {};
7321             var anim = Roo.lib.Anim[animType](
7322                 this.dom, args,
7323                 (opt.duration || defaultDur) || .35,
7324                 (opt.easing || defaultEase) || 'easeOut',
7325                 function(){
7326                     Roo.callback(cb, this);
7327                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7328                 },
7329                 this
7330             );
7331             opt.anim = anim;
7332             return anim;
7333         },
7334
7335         // private legacy anim prep
7336         preanim : function(a, i){
7337             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7338         },
7339
7340         /**
7341          * Removes worthless text nodes
7342          * @param {Boolean} forceReclean (optional) By default the element
7343          * keeps track if it has been cleaned already so
7344          * you can call this over and over. However, if you update the element and
7345          * need to force a reclean, you can pass true.
7346          */
7347         clean : function(forceReclean){
7348             if(this.isCleaned && forceReclean !== true){
7349                 return this;
7350             }
7351             var ns = /\S/;
7352             var d = this.dom, n = d.firstChild, ni = -1;
7353             while(n){
7354                 var nx = n.nextSibling;
7355                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7356                     d.removeChild(n);
7357                 }else{
7358                     n.nodeIndex = ++ni;
7359                 }
7360                 n = nx;
7361             }
7362             this.isCleaned = true;
7363             return this;
7364         },
7365
7366         // private
7367         calcOffsetsTo : function(el){
7368             el = Roo.get(el);
7369             var d = el.dom;
7370             var restorePos = false;
7371             if(el.getStyle('position') == 'static'){
7372                 el.position('relative');
7373                 restorePos = true;
7374             }
7375             var x = 0, y =0;
7376             var op = this.dom;
7377             while(op && op != d && op.tagName != 'HTML'){
7378                 x+= op.offsetLeft;
7379                 y+= op.offsetTop;
7380                 op = op.offsetParent;
7381             }
7382             if(restorePos){
7383                 el.position('static');
7384             }
7385             return [x, y];
7386         },
7387
7388         /**
7389          * Scrolls this element into view within the passed container.
7390          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7391          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7392          * @return {Roo.Element} this
7393          */
7394         scrollIntoView : function(container, hscroll){
7395             var c = Roo.getDom(container) || document.body;
7396             var el = this.dom;
7397
7398             var o = this.calcOffsetsTo(c),
7399                 l = o[0],
7400                 t = o[1],
7401                 b = t+el.offsetHeight,
7402                 r = l+el.offsetWidth;
7403
7404             var ch = c.clientHeight;
7405             var ct = parseInt(c.scrollTop, 10);
7406             var cl = parseInt(c.scrollLeft, 10);
7407             var cb = ct + ch;
7408             var cr = cl + c.clientWidth;
7409
7410             if(t < ct){
7411                 c.scrollTop = t;
7412             }else if(b > cb){
7413                 c.scrollTop = b-ch;
7414             }
7415
7416             if(hscroll !== false){
7417                 if(l < cl){
7418                     c.scrollLeft = l;
7419                 }else if(r > cr){
7420                     c.scrollLeft = r-c.clientWidth;
7421                 }
7422             }
7423             return this;
7424         },
7425
7426         // private
7427         scrollChildIntoView : function(child, hscroll){
7428             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7429         },
7430
7431         /**
7432          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7433          * the new height may not be available immediately.
7434          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7435          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7436          * @param {Function} onComplete (optional) Function to call when animation completes
7437          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7438          * @return {Roo.Element} this
7439          */
7440         autoHeight : function(animate, duration, onComplete, easing){
7441             var oldHeight = this.getHeight();
7442             this.clip();
7443             this.setHeight(1); // force clipping
7444             setTimeout(function(){
7445                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7446                 if(!animate){
7447                     this.setHeight(height);
7448                     this.unclip();
7449                     if(typeof onComplete == "function"){
7450                         onComplete();
7451                     }
7452                 }else{
7453                     this.setHeight(oldHeight); // restore original height
7454                     this.setHeight(height, animate, duration, function(){
7455                         this.unclip();
7456                         if(typeof onComplete == "function") { onComplete(); }
7457                     }.createDelegate(this), easing);
7458                 }
7459             }.createDelegate(this), 0);
7460             return this;
7461         },
7462
7463         /**
7464          * Returns true if this element is an ancestor of the passed element
7465          * @param {HTMLElement/String} el The element to check
7466          * @return {Boolean} True if this element is an ancestor of el, else false
7467          */
7468         contains : function(el){
7469             if(!el){return false;}
7470             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7471         },
7472
7473         /**
7474          * Checks whether the element is currently visible using both visibility and display properties.
7475          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7476          * @return {Boolean} True if the element is currently visible, else false
7477          */
7478         isVisible : function(deep) {
7479             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7480             if(deep !== true || !vis){
7481                 return vis;
7482             }
7483             var p = this.dom.parentNode;
7484             while(p && p.tagName.toLowerCase() != "body"){
7485                 if(!Roo.fly(p, '_isVisible').isVisible()){
7486                     return false;
7487                 }
7488                 p = p.parentNode;
7489             }
7490             return true;
7491         },
7492
7493         /**
7494          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7495          * @param {String} selector The CSS selector
7496          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7497          * @return {CompositeElement/CompositeElementLite} The composite element
7498          */
7499         select : function(selector, unique){
7500             return El.select(selector, unique, this.dom);
7501         },
7502
7503         /**
7504          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7505          * @param {String} selector The CSS selector
7506          * @return {Array} An array of the matched nodes
7507          */
7508         query : function(selector, unique){
7509             return Roo.DomQuery.select(selector, this.dom);
7510         },
7511
7512         /**
7513          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7514          * @param {String} selector The CSS selector
7515          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7516          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7517          */
7518         child : function(selector, returnDom){
7519             var n = Roo.DomQuery.selectNode(selector, this.dom);
7520             return returnDom ? n : Roo.get(n);
7521         },
7522
7523         /**
7524          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7525          * @param {String} selector The CSS selector
7526          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7527          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7528          */
7529         down : function(selector, returnDom){
7530             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7531             return returnDom ? n : Roo.get(n);
7532         },
7533
7534         /**
7535          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7536          * @param {String} group The group the DD object is member of
7537          * @param {Object} config The DD config object
7538          * @param {Object} overrides An object containing methods to override/implement on the DD object
7539          * @return {Roo.dd.DD} The DD object
7540          */
7541         initDD : function(group, config, overrides){
7542             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7543             return Roo.apply(dd, overrides);
7544         },
7545
7546         /**
7547          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7548          * @param {String} group The group the DDProxy object is member of
7549          * @param {Object} config The DDProxy config object
7550          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7551          * @return {Roo.dd.DDProxy} The DDProxy object
7552          */
7553         initDDProxy : function(group, config, overrides){
7554             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7555             return Roo.apply(dd, overrides);
7556         },
7557
7558         /**
7559          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7560          * @param {String} group The group the DDTarget object is member of
7561          * @param {Object} config The DDTarget config object
7562          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7563          * @return {Roo.dd.DDTarget} The DDTarget object
7564          */
7565         initDDTarget : function(group, config, overrides){
7566             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7567             return Roo.apply(dd, overrides);
7568         },
7569
7570         /**
7571          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7572          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7573          * @param {Boolean} visible Whether the element is visible
7574          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7575          * @return {Roo.Element} this
7576          */
7577          setVisible : function(visible, animate){
7578             if(!animate || !A){
7579                 if(this.visibilityMode == El.DISPLAY){
7580                     this.setDisplayed(visible);
7581                 }else{
7582                     this.fixDisplay();
7583                     this.dom.style.visibility = visible ? "visible" : "hidden";
7584                 }
7585             }else{
7586                 // closure for composites
7587                 var dom = this.dom;
7588                 var visMode = this.visibilityMode;
7589                 if(visible){
7590                     this.setOpacity(.01);
7591                     this.setVisible(true);
7592                 }
7593                 this.anim({opacity: { to: (visible?1:0) }},
7594                       this.preanim(arguments, 1),
7595                       null, .35, 'easeIn', function(){
7596                          if(!visible){
7597                              if(visMode == El.DISPLAY){
7598                                  dom.style.display = "none";
7599                              }else{
7600                                  dom.style.visibility = "hidden";
7601                              }
7602                              Roo.get(dom).setOpacity(1);
7603                          }
7604                      });
7605             }
7606             return this;
7607         },
7608
7609         /**
7610          * Returns true if display is not "none"
7611          * @return {Boolean}
7612          */
7613         isDisplayed : function() {
7614             return this.getStyle("display") != "none";
7615         },
7616
7617         /**
7618          * Toggles the element's visibility or display, depending on visibility mode.
7619          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7620          * @return {Roo.Element} this
7621          */
7622         toggle : function(animate){
7623             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7629          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7630          * @return {Roo.Element} this
7631          */
7632         setDisplayed : function(value) {
7633             if(typeof value == "boolean"){
7634                value = value ? this.originalDisplay : "none";
7635             }
7636             this.setStyle("display", value);
7637             return this;
7638         },
7639
7640         /**
7641          * Tries to focus the element. Any exceptions are caught and ignored.
7642          * @return {Roo.Element} this
7643          */
7644         focus : function() {
7645             try{
7646                 this.dom.focus();
7647             }catch(e){}
7648             return this;
7649         },
7650
7651         /**
7652          * Tries to blur the element. Any exceptions are caught and ignored.
7653          * @return {Roo.Element} this
7654          */
7655         blur : function() {
7656             try{
7657                 this.dom.blur();
7658             }catch(e){}
7659             return this;
7660         },
7661
7662         /**
7663          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7664          * @param {String/Array} className The CSS class to add, or an array of classes
7665          * @return {Roo.Element} this
7666          */
7667         addClass : function(className){
7668             if(className instanceof Array){
7669                 for(var i = 0, len = className.length; i < len; i++) {
7670                     this.addClass(className[i]);
7671                 }
7672             }else{
7673                 if(className && !this.hasClass(className)){
7674                     this.dom.className = this.dom.className + " " + className;
7675                 }
7676             }
7677             return this;
7678         },
7679
7680         /**
7681          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7682          * @param {String/Array} className The CSS class to add, or an array of classes
7683          * @return {Roo.Element} this
7684          */
7685         radioClass : function(className){
7686             var siblings = this.dom.parentNode.childNodes;
7687             for(var i = 0; i < siblings.length; i++) {
7688                 var s = siblings[i];
7689                 if(s.nodeType == 1){
7690                     Roo.get(s).removeClass(className);
7691                 }
7692             }
7693             this.addClass(className);
7694             return this;
7695         },
7696
7697         /**
7698          * Removes one or more CSS classes from the element.
7699          * @param {String/Array} className The CSS class to remove, or an array of classes
7700          * @return {Roo.Element} this
7701          */
7702         removeClass : function(className){
7703             if(!className || !this.dom.className){
7704                 return this;
7705             }
7706             if(className instanceof Array){
7707                 for(var i = 0, len = className.length; i < len; i++) {
7708                     this.removeClass(className[i]);
7709                 }
7710             }else{
7711                 if(this.hasClass(className)){
7712                     var re = this.classReCache[className];
7713                     if (!re) {
7714                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7715                        this.classReCache[className] = re;
7716                     }
7717                     this.dom.className =
7718                         this.dom.className.replace(re, " ");
7719                 }
7720             }
7721             return this;
7722         },
7723
7724         // private
7725         classReCache: {},
7726
7727         /**
7728          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7729          * @param {String} className The CSS class to toggle
7730          * @return {Roo.Element} this
7731          */
7732         toggleClass : function(className){
7733             if(this.hasClass(className)){
7734                 this.removeClass(className);
7735             }else{
7736                 this.addClass(className);
7737             }
7738             return this;
7739         },
7740
7741         /**
7742          * Checks if the specified CSS class exists on this element's DOM node.
7743          * @param {String} className The CSS class to check for
7744          * @return {Boolean} True if the class exists, else false
7745          */
7746         hasClass : function(className){
7747             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7748         },
7749
7750         /**
7751          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7752          * @param {String} oldClassName The CSS class to replace
7753          * @param {String} newClassName The replacement CSS class
7754          * @return {Roo.Element} this
7755          */
7756         replaceClass : function(oldClassName, newClassName){
7757             this.removeClass(oldClassName);
7758             this.addClass(newClassName);
7759             return this;
7760         },
7761
7762         /**
7763          * Returns an object with properties matching the styles requested.
7764          * For example, el.getStyles('color', 'font-size', 'width') might return
7765          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7766          * @param {String} style1 A style name
7767          * @param {String} style2 A style name
7768          * @param {String} etc.
7769          * @return {Object} The style object
7770          */
7771         getStyles : function(){
7772             var a = arguments, len = a.length, r = {};
7773             for(var i = 0; i < len; i++){
7774                 r[a[i]] = this.getStyle(a[i]);
7775             }
7776             return r;
7777         },
7778
7779         /**
7780          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7781          * @param {String} property The style property whose value is returned.
7782          * @return {String} The current value of the style property for this element.
7783          */
7784         getStyle : function(){
7785             return view && view.getComputedStyle ?
7786                 function(prop){
7787                     var el = this.dom, v, cs, camel;
7788                     if(prop == 'float'){
7789                         prop = "cssFloat";
7790                     }
7791                     if(el.style && (v = el.style[prop])){
7792                         return v;
7793                     }
7794                     if(cs = view.getComputedStyle(el, "")){
7795                         if(!(camel = propCache[prop])){
7796                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7797                         }
7798                         return cs[camel];
7799                     }
7800                     return null;
7801                 } :
7802                 function(prop){
7803                     var el = this.dom, v, cs, camel;
7804                     if(prop == 'opacity'){
7805                         if(typeof el.style.filter == 'string'){
7806                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7807                             if(m){
7808                                 var fv = parseFloat(m[1]);
7809                                 if(!isNaN(fv)){
7810                                     return fv ? fv / 100 : 0;
7811                                 }
7812                             }
7813                         }
7814                         return 1;
7815                     }else if(prop == 'float'){
7816                         prop = "styleFloat";
7817                     }
7818                     if(!(camel = propCache[prop])){
7819                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7820                     }
7821                     if(v = el.style[camel]){
7822                         return v;
7823                     }
7824                     if(cs = el.currentStyle){
7825                         return cs[camel];
7826                     }
7827                     return null;
7828                 };
7829         }(),
7830
7831         /**
7832          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7833          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7834          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7835          * @return {Roo.Element} this
7836          */
7837         setStyle : function(prop, value){
7838             if(typeof prop == "string"){
7839                 
7840                 if (prop == 'float') {
7841                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7842                     return this;
7843                 }
7844                 
7845                 var camel;
7846                 if(!(camel = propCache[prop])){
7847                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7848                 }
7849                 
7850                 if(camel == 'opacity') {
7851                     this.setOpacity(value);
7852                 }else{
7853                     this.dom.style[camel] = value;
7854                 }
7855             }else{
7856                 for(var style in prop){
7857                     if(typeof prop[style] != "function"){
7858                        this.setStyle(style, prop[style]);
7859                     }
7860                 }
7861             }
7862             return this;
7863         },
7864
7865         /**
7866          * More flexible version of {@link #setStyle} for setting style properties.
7867          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7868          * a function which returns such a specification.
7869          * @return {Roo.Element} this
7870          */
7871         applyStyles : function(style){
7872             Roo.DomHelper.applyStyles(this.dom, style);
7873             return this;
7874         },
7875
7876         /**
7877           * 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).
7878           * @return {Number} The X position of the element
7879           */
7880         getX : function(){
7881             return D.getX(this.dom);
7882         },
7883
7884         /**
7885           * 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).
7886           * @return {Number} The Y position of the element
7887           */
7888         getY : function(){
7889             return D.getY(this.dom);
7890         },
7891
7892         /**
7893           * 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).
7894           * @return {Array} The XY position of the element
7895           */
7896         getXY : function(){
7897             return D.getXY(this.dom);
7898         },
7899
7900         /**
7901          * 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).
7902          * @param {Number} The X position of the element
7903          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7904          * @return {Roo.Element} this
7905          */
7906         setX : function(x, animate){
7907             if(!animate || !A){
7908                 D.setX(this.dom, x);
7909             }else{
7910                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7911             }
7912             return this;
7913         },
7914
7915         /**
7916          * 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).
7917          * @param {Number} The Y position of the element
7918          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7919          * @return {Roo.Element} this
7920          */
7921         setY : function(y, animate){
7922             if(!animate || !A){
7923                 D.setY(this.dom, y);
7924             }else{
7925                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7926             }
7927             return this;
7928         },
7929
7930         /**
7931          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7932          * @param {String} left The left CSS property value
7933          * @return {Roo.Element} this
7934          */
7935         setLeft : function(left){
7936             this.setStyle("left", this.addUnits(left));
7937             return this;
7938         },
7939
7940         /**
7941          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7942          * @param {String} top The top CSS property value
7943          * @return {Roo.Element} this
7944          */
7945         setTop : function(top){
7946             this.setStyle("top", this.addUnits(top));
7947             return this;
7948         },
7949
7950         /**
7951          * Sets the element's CSS right style.
7952          * @param {String} right The right CSS property value
7953          * @return {Roo.Element} this
7954          */
7955         setRight : function(right){
7956             this.setStyle("right", this.addUnits(right));
7957             return this;
7958         },
7959
7960         /**
7961          * Sets the element's CSS bottom style.
7962          * @param {String} bottom The bottom CSS property value
7963          * @return {Roo.Element} this
7964          */
7965         setBottom : function(bottom){
7966             this.setStyle("bottom", this.addUnits(bottom));
7967             return this;
7968         },
7969
7970         /**
7971          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7972          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7973          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7974          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7975          * @return {Roo.Element} this
7976          */
7977         setXY : function(pos, animate){
7978             if(!animate || !A){
7979                 D.setXY(this.dom, pos);
7980             }else{
7981                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7982             }
7983             return this;
7984         },
7985
7986         /**
7987          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7988          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7989          * @param {Number} x X value for new position (coordinates are page-based)
7990          * @param {Number} y Y value for new position (coordinates are page-based)
7991          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7992          * @return {Roo.Element} this
7993          */
7994         setLocation : function(x, y, animate){
7995             this.setXY([x, y], this.preanim(arguments, 2));
7996             return this;
7997         },
7998
7999         /**
8000          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8001          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8002          * @param {Number} x X value for new position (coordinates are page-based)
8003          * @param {Number} y Y value for new position (coordinates are page-based)
8004          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8005          * @return {Roo.Element} this
8006          */
8007         moveTo : function(x, y, animate){
8008             this.setXY([x, y], this.preanim(arguments, 2));
8009             return this;
8010         },
8011
8012         /**
8013          * Returns the region of the given element.
8014          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8015          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8016          */
8017         getRegion : function(){
8018             return D.getRegion(this.dom);
8019         },
8020
8021         /**
8022          * Returns the offset height of the element
8023          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8024          * @return {Number} The element's height
8025          */
8026         getHeight : function(contentHeight){
8027             var h = this.dom.offsetHeight || 0;
8028             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8029         },
8030
8031         /**
8032          * Returns the offset width of the element
8033          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8034          * @return {Number} The element's width
8035          */
8036         getWidth : function(contentWidth){
8037             var w = this.dom.offsetWidth || 0;
8038             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8039         },
8040
8041         /**
8042          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8043          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8044          * if a height has not been set using CSS.
8045          * @return {Number}
8046          */
8047         getComputedHeight : function(){
8048             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8049             if(!h){
8050                 h = parseInt(this.getStyle('height'), 10) || 0;
8051                 if(!this.isBorderBox()){
8052                     h += this.getFrameWidth('tb');
8053                 }
8054             }
8055             return h;
8056         },
8057
8058         /**
8059          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8060          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8061          * if a width has not been set using CSS.
8062          * @return {Number}
8063          */
8064         getComputedWidth : function(){
8065             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8066             if(!w){
8067                 w = parseInt(this.getStyle('width'), 10) || 0;
8068                 if(!this.isBorderBox()){
8069                     w += this.getFrameWidth('lr');
8070                 }
8071             }
8072             return w;
8073         },
8074
8075         /**
8076          * Returns the size of the element.
8077          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8078          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8079          */
8080         getSize : function(contentSize){
8081             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8082         },
8083
8084         /**
8085          * Returns the width and height of the viewport.
8086          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8087          */
8088         getViewSize : function(){
8089             var d = this.dom, doc = document, aw = 0, ah = 0;
8090             if(d == doc || d == doc.body){
8091                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8092             }else{
8093                 return {
8094                     width : d.clientWidth,
8095                     height: d.clientHeight
8096                 };
8097             }
8098         },
8099
8100         /**
8101          * Returns the value of the "value" attribute
8102          * @param {Boolean} asNumber true to parse the value as a number
8103          * @return {String/Number}
8104          */
8105         getValue : function(asNumber){
8106             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8107         },
8108
8109         // private
8110         adjustWidth : function(width){
8111             if(typeof width == "number"){
8112                 if(this.autoBoxAdjust && !this.isBorderBox()){
8113                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8114                 }
8115                 if(width < 0){
8116                     width = 0;
8117                 }
8118             }
8119             return width;
8120         },
8121
8122         // private
8123         adjustHeight : function(height){
8124             if(typeof height == "number"){
8125                if(this.autoBoxAdjust && !this.isBorderBox()){
8126                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8127                }
8128                if(height < 0){
8129                    height = 0;
8130                }
8131             }
8132             return height;
8133         },
8134
8135         /**
8136          * Set the width of the element
8137          * @param {Number} width The new width
8138          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8139          * @return {Roo.Element} this
8140          */
8141         setWidth : function(width, animate){
8142             width = this.adjustWidth(width);
8143             if(!animate || !A){
8144                 this.dom.style.width = this.addUnits(width);
8145             }else{
8146                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8147             }
8148             return this;
8149         },
8150
8151         /**
8152          * Set the height of the element
8153          * @param {Number} height The new height
8154          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8155          * @return {Roo.Element} this
8156          */
8157          setHeight : function(height, animate){
8158             height = this.adjustHeight(height);
8159             if(!animate || !A){
8160                 this.dom.style.height = this.addUnits(height);
8161             }else{
8162                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8163             }
8164             return this;
8165         },
8166
8167         /**
8168          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8169          * @param {Number} width The new width
8170          * @param {Number} height The new height
8171          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8172          * @return {Roo.Element} this
8173          */
8174          setSize : function(width, height, animate){
8175             if(typeof width == "object"){ // in case of object from getSize()
8176                 height = width.height; width = width.width;
8177             }
8178             width = this.adjustWidth(width); height = this.adjustHeight(height);
8179             if(!animate || !A){
8180                 this.dom.style.width = this.addUnits(width);
8181                 this.dom.style.height = this.addUnits(height);
8182             }else{
8183                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8184             }
8185             return this;
8186         },
8187
8188         /**
8189          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8190          * @param {Number} x X value for new position (coordinates are page-based)
8191          * @param {Number} y Y value for new position (coordinates are page-based)
8192          * @param {Number} width The new width
8193          * @param {Number} height The new height
8194          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8195          * @return {Roo.Element} this
8196          */
8197         setBounds : function(x, y, width, height, animate){
8198             if(!animate || !A){
8199                 this.setSize(width, height);
8200                 this.setLocation(x, y);
8201             }else{
8202                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8203                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8204                               this.preanim(arguments, 4), 'motion');
8205             }
8206             return this;
8207         },
8208
8209         /**
8210          * 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.
8211          * @param {Roo.lib.Region} region The region to fill
8212          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8213          * @return {Roo.Element} this
8214          */
8215         setRegion : function(region, animate){
8216             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8217             return this;
8218         },
8219
8220         /**
8221          * Appends an event handler
8222          *
8223          * @param {String}   eventName     The type of event to append
8224          * @param {Function} fn        The method the event invokes
8225          * @param {Object} scope       (optional) The scope (this object) of the fn
8226          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8227          */
8228         addListener : function(eventName, fn, scope, options){
8229             if (this.dom) {
8230                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8231             }
8232         },
8233
8234         /**
8235          * Removes an event handler from this element
8236          * @param {String} eventName the type of event to remove
8237          * @param {Function} fn the method the event invokes
8238          * @return {Roo.Element} this
8239          */
8240         removeListener : function(eventName, fn){
8241             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8242             return this;
8243         },
8244
8245         /**
8246          * Removes all previous added listeners from this element
8247          * @return {Roo.Element} this
8248          */
8249         removeAllListeners : function(){
8250             E.purgeElement(this.dom);
8251             return this;
8252         },
8253
8254         relayEvent : function(eventName, observable){
8255             this.on(eventName, function(e){
8256                 observable.fireEvent(eventName, e);
8257             });
8258         },
8259
8260         /**
8261          * Set the opacity of the element
8262          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8263          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8264          * @return {Roo.Element} this
8265          */
8266          setOpacity : function(opacity, animate){
8267             if(!animate || !A){
8268                 var s = this.dom.style;
8269                 if(Roo.isIE){
8270                     s.zoom = 1;
8271                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8272                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8273                 }else{
8274                     s.opacity = opacity;
8275                 }
8276             }else{
8277                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8278             }
8279             return this;
8280         },
8281
8282         /**
8283          * Gets the left X coordinate
8284          * @param {Boolean} local True to get the local css position instead of page coordinate
8285          * @return {Number}
8286          */
8287         getLeft : function(local){
8288             if(!local){
8289                 return this.getX();
8290             }else{
8291                 return parseInt(this.getStyle("left"), 10) || 0;
8292             }
8293         },
8294
8295         /**
8296          * Gets the right X coordinate of the element (element X position + element width)
8297          * @param {Boolean} local True to get the local css position instead of page coordinate
8298          * @return {Number}
8299          */
8300         getRight : function(local){
8301             if(!local){
8302                 return this.getX() + this.getWidth();
8303             }else{
8304                 return (this.getLeft(true) + this.getWidth()) || 0;
8305             }
8306         },
8307
8308         /**
8309          * Gets the top Y coordinate
8310          * @param {Boolean} local True to get the local css position instead of page coordinate
8311          * @return {Number}
8312          */
8313         getTop : function(local) {
8314             if(!local){
8315                 return this.getY();
8316             }else{
8317                 return parseInt(this.getStyle("top"), 10) || 0;
8318             }
8319         },
8320
8321         /**
8322          * Gets the bottom Y coordinate of the element (element Y position + element height)
8323          * @param {Boolean} local True to get the local css position instead of page coordinate
8324          * @return {Number}
8325          */
8326         getBottom : function(local){
8327             if(!local){
8328                 return this.getY() + this.getHeight();
8329             }else{
8330                 return (this.getTop(true) + this.getHeight()) || 0;
8331             }
8332         },
8333
8334         /**
8335         * Initializes positioning on this element. If a desired position is not passed, it will make the
8336         * the element positioned relative IF it is not already positioned.
8337         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8338         * @param {Number} zIndex (optional) The zIndex to apply
8339         * @param {Number} x (optional) Set the page X position
8340         * @param {Number} y (optional) Set the page Y position
8341         */
8342         position : function(pos, zIndex, x, y){
8343             if(!pos){
8344                if(this.getStyle('position') == 'static'){
8345                    this.setStyle('position', 'relative');
8346                }
8347             }else{
8348                 this.setStyle("position", pos);
8349             }
8350             if(zIndex){
8351                 this.setStyle("z-index", zIndex);
8352             }
8353             if(x !== undefined && y !== undefined){
8354                 this.setXY([x, y]);
8355             }else if(x !== undefined){
8356                 this.setX(x);
8357             }else if(y !== undefined){
8358                 this.setY(y);
8359             }
8360         },
8361
8362         /**
8363         * Clear positioning back to the default when the document was loaded
8364         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8365         * @return {Roo.Element} this
8366          */
8367         clearPositioning : function(value){
8368             value = value ||'';
8369             this.setStyle({
8370                 "left": value,
8371                 "right": value,
8372                 "top": value,
8373                 "bottom": value,
8374                 "z-index": "",
8375                 "position" : "static"
8376             });
8377             return this;
8378         },
8379
8380         /**
8381         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8382         * snapshot before performing an update and then restoring the element.
8383         * @return {Object}
8384         */
8385         getPositioning : function(){
8386             var l = this.getStyle("left");
8387             var t = this.getStyle("top");
8388             return {
8389                 "position" : this.getStyle("position"),
8390                 "left" : l,
8391                 "right" : l ? "" : this.getStyle("right"),
8392                 "top" : t,
8393                 "bottom" : t ? "" : this.getStyle("bottom"),
8394                 "z-index" : this.getStyle("z-index")
8395             };
8396         },
8397
8398         /**
8399          * Gets the width of the border(s) for the specified side(s)
8400          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8401          * passing lr would get the border (l)eft width + the border (r)ight width.
8402          * @return {Number} The width of the sides passed added together
8403          */
8404         getBorderWidth : function(side){
8405             return this.addStyles(side, El.borders);
8406         },
8407
8408         /**
8409          * Gets the width of the padding(s) for the specified side(s)
8410          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8411          * passing lr would get the padding (l)eft + the padding (r)ight.
8412          * @return {Number} The padding of the sides passed added together
8413          */
8414         getPadding : function(side){
8415             return this.addStyles(side, El.paddings);
8416         },
8417
8418         /**
8419         * Set positioning with an object returned by getPositioning().
8420         * @param {Object} posCfg
8421         * @return {Roo.Element} this
8422          */
8423         setPositioning : function(pc){
8424             this.applyStyles(pc);
8425             if(pc.right == "auto"){
8426                 this.dom.style.right = "";
8427             }
8428             if(pc.bottom == "auto"){
8429                 this.dom.style.bottom = "";
8430             }
8431             return this;
8432         },
8433
8434         // private
8435         fixDisplay : function(){
8436             if(this.getStyle("display") == "none"){
8437                 this.setStyle("visibility", "hidden");
8438                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8439                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8440                     this.setStyle("display", "block");
8441                 }
8442             }
8443         },
8444
8445         /**
8446          * Quick set left and top adding default units
8447          * @param {String} left The left CSS property value
8448          * @param {String} top The top CSS property value
8449          * @return {Roo.Element} this
8450          */
8451          setLeftTop : function(left, top){
8452             this.dom.style.left = this.addUnits(left);
8453             this.dom.style.top = this.addUnits(top);
8454             return this;
8455         },
8456
8457         /**
8458          * Move this element relative to its current position.
8459          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8460          * @param {Number} distance How far to move the element in pixels
8461          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8462          * @return {Roo.Element} this
8463          */
8464          move : function(direction, distance, animate){
8465             var xy = this.getXY();
8466             direction = direction.toLowerCase();
8467             switch(direction){
8468                 case "l":
8469                 case "left":
8470                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8471                     break;
8472                case "r":
8473                case "right":
8474                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8475                     break;
8476                case "t":
8477                case "top":
8478                case "up":
8479                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8480                     break;
8481                case "b":
8482                case "bottom":
8483                case "down":
8484                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8485                     break;
8486             }
8487             return this;
8488         },
8489
8490         /**
8491          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8492          * @return {Roo.Element} this
8493          */
8494         clip : function(){
8495             if(!this.isClipped){
8496                this.isClipped = true;
8497                this.originalClip = {
8498                    "o": this.getStyle("overflow"),
8499                    "x": this.getStyle("overflow-x"),
8500                    "y": this.getStyle("overflow-y")
8501                };
8502                this.setStyle("overflow", "hidden");
8503                this.setStyle("overflow-x", "hidden");
8504                this.setStyle("overflow-y", "hidden");
8505             }
8506             return this;
8507         },
8508
8509         /**
8510          *  Return clipping (overflow) to original clipping before clip() was called
8511          * @return {Roo.Element} this
8512          */
8513         unclip : function(){
8514             if(this.isClipped){
8515                 this.isClipped = false;
8516                 var o = this.originalClip;
8517                 if(o.o){this.setStyle("overflow", o.o);}
8518                 if(o.x){this.setStyle("overflow-x", o.x);}
8519                 if(o.y){this.setStyle("overflow-y", o.y);}
8520             }
8521             return this;
8522         },
8523
8524
8525         /**
8526          * Gets the x,y coordinates specified by the anchor position on the element.
8527          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8528          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8529          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8530          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8531          * @return {Array} [x, y] An array containing the element's x and y coordinates
8532          */
8533         getAnchorXY : function(anchor, local, s){
8534             //Passing a different size is useful for pre-calculating anchors,
8535             //especially for anchored animations that change the el size.
8536
8537             var w, h, vp = false;
8538             if(!s){
8539                 var d = this.dom;
8540                 if(d == document.body || d == document){
8541                     vp = true;
8542                     w = D.getViewWidth(); h = D.getViewHeight();
8543                 }else{
8544                     w = this.getWidth(); h = this.getHeight();
8545                 }
8546             }else{
8547                 w = s.width;  h = s.height;
8548             }
8549             var x = 0, y = 0, r = Math.round;
8550             switch((anchor || "tl").toLowerCase()){
8551                 case "c":
8552                     x = r(w*.5);
8553                     y = r(h*.5);
8554                 break;
8555                 case "t":
8556                     x = r(w*.5);
8557                     y = 0;
8558                 break;
8559                 case "l":
8560                     x = 0;
8561                     y = r(h*.5);
8562                 break;
8563                 case "r":
8564                     x = w;
8565                     y = r(h*.5);
8566                 break;
8567                 case "b":
8568                     x = r(w*.5);
8569                     y = h;
8570                 break;
8571                 case "tl":
8572                     x = 0;
8573                     y = 0;
8574                 break;
8575                 case "bl":
8576                     x = 0;
8577                     y = h;
8578                 break;
8579                 case "br":
8580                     x = w;
8581                     y = h;
8582                 break;
8583                 case "tr":
8584                     x = w;
8585                     y = 0;
8586                 break;
8587             }
8588             if(local === true){
8589                 return [x, y];
8590             }
8591             if(vp){
8592                 var sc = this.getScroll();
8593                 return [x + sc.left, y + sc.top];
8594             }
8595             //Add the element's offset xy
8596             var o = this.getXY();
8597             return [x+o[0], y+o[1]];
8598         },
8599
8600         /**
8601          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8602          * supported position values.
8603          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8604          * @param {String} position The position to align to.
8605          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8606          * @return {Array} [x, y]
8607          */
8608         getAlignToXY : function(el, p, o)
8609         {
8610             el = Roo.get(el);
8611             var d = this.dom;
8612             if(!el.dom){
8613                 throw "Element.alignTo with an element that doesn't exist";
8614             }
8615             var c = false; //constrain to viewport
8616             var p1 = "", p2 = "";
8617             o = o || [0,0];
8618
8619             if(!p){
8620                 p = "tl-bl";
8621             }else if(p == "?"){
8622                 p = "tl-bl?";
8623             }else if(p.indexOf("-") == -1){
8624                 p = "tl-" + p;
8625             }
8626             p = p.toLowerCase();
8627             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8628             if(!m){
8629                throw "Element.alignTo with an invalid alignment " + p;
8630             }
8631             p1 = m[1]; p2 = m[2]; c = !!m[3];
8632
8633             //Subtract the aligned el's internal xy from the target's offset xy
8634             //plus custom offset to get the aligned el's new offset xy
8635             var a1 = this.getAnchorXY(p1, true);
8636             var a2 = el.getAnchorXY(p2, false);
8637             var x = a2[0] - a1[0] + o[0];
8638             var y = a2[1] - a1[1] + o[1];
8639             if(c){
8640                 //constrain the aligned el to viewport if necessary
8641                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8642                 // 5px of margin for ie
8643                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8644
8645                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8646                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8647                 //otherwise swap the aligned el to the opposite border of the target.
8648                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8649                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8650                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8651                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8652
8653                var doc = document;
8654                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8655                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8656
8657                if((x+w) > dw + scrollX){
8658                     x = swapX ? r.left-w : dw+scrollX-w;
8659                 }
8660                if(x < scrollX){
8661                    x = swapX ? r.right : scrollX;
8662                }
8663                if((y+h) > dh + scrollY){
8664                     y = swapY ? r.top-h : dh+scrollY-h;
8665                 }
8666                if (y < scrollY){
8667                    y = swapY ? r.bottom : scrollY;
8668                }
8669             }
8670             return [x,y];
8671         },
8672
8673         // private
8674         getConstrainToXY : function(){
8675             var os = {top:0, left:0, bottom:0, right: 0};
8676
8677             return function(el, local, offsets, proposedXY){
8678                 el = Roo.get(el);
8679                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8680
8681                 var vw, vh, vx = 0, vy = 0;
8682                 if(el.dom == document.body || el.dom == document){
8683                     vw = Roo.lib.Dom.getViewWidth();
8684                     vh = Roo.lib.Dom.getViewHeight();
8685                 }else{
8686                     vw = el.dom.clientWidth;
8687                     vh = el.dom.clientHeight;
8688                     if(!local){
8689                         var vxy = el.getXY();
8690                         vx = vxy[0];
8691                         vy = vxy[1];
8692                     }
8693                 }
8694
8695                 var s = el.getScroll();
8696
8697                 vx += offsets.left + s.left;
8698                 vy += offsets.top + s.top;
8699
8700                 vw -= offsets.right;
8701                 vh -= offsets.bottom;
8702
8703                 var vr = vx+vw;
8704                 var vb = vy+vh;
8705
8706                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8707                 var x = xy[0], y = xy[1];
8708                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8709
8710                 // only move it if it needs it
8711                 var moved = false;
8712
8713                 // first validate right/bottom
8714                 if((x + w) > vr){
8715                     x = vr - w;
8716                     moved = true;
8717                 }
8718                 if((y + h) > vb){
8719                     y = vb - h;
8720                     moved = true;
8721                 }
8722                 // then make sure top/left isn't negative
8723                 if(x < vx){
8724                     x = vx;
8725                     moved = true;
8726                 }
8727                 if(y < vy){
8728                     y = vy;
8729                     moved = true;
8730                 }
8731                 return moved ? [x, y] : false;
8732             };
8733         }(),
8734
8735         // private
8736         adjustForConstraints : function(xy, parent, offsets){
8737             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8738         },
8739
8740         /**
8741          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8742          * document it aligns it to the viewport.
8743          * The position parameter is optional, and can be specified in any one of the following formats:
8744          * <ul>
8745          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8746          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8747          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8748          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8749          *   <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
8750          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8751          * </ul>
8752          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8753          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8754          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8755          * that specified in order to enforce the viewport constraints.
8756          * Following are all of the supported anchor positions:
8757     <pre>
8758     Value  Description
8759     -----  -----------------------------
8760     tl     The top left corner (default)
8761     t      The center of the top edge
8762     tr     The top right corner
8763     l      The center of the left edge
8764     c      In the center of the element
8765     r      The center of the right edge
8766     bl     The bottom left corner
8767     b      The center of the bottom edge
8768     br     The bottom right corner
8769     </pre>
8770     Example Usage:
8771     <pre><code>
8772     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8773     el.alignTo("other-el");
8774
8775     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8776     el.alignTo("other-el", "tr?");
8777
8778     // align the bottom right corner of el with the center left edge of other-el
8779     el.alignTo("other-el", "br-l?");
8780
8781     // align the center of el with the bottom left corner of other-el and
8782     // adjust the x position by -6 pixels (and the y position by 0)
8783     el.alignTo("other-el", "c-bl", [-6, 0]);
8784     </code></pre>
8785          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8786          * @param {String} position The position to align to.
8787          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8788          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8789          * @return {Roo.Element} this
8790          */
8791         alignTo : function(element, position, offsets, animate){
8792             var xy = this.getAlignToXY(element, position, offsets);
8793             this.setXY(xy, this.preanim(arguments, 3));
8794             return this;
8795         },
8796
8797         /**
8798          * Anchors an element to another element and realigns it when the window is resized.
8799          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8800          * @param {String} position The position to align to.
8801          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8802          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8803          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8804          * is a number, it is used as the buffer delay (defaults to 50ms).
8805          * @param {Function} callback The function to call after the animation finishes
8806          * @return {Roo.Element} this
8807          */
8808         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8809             var action = function(){
8810                 this.alignTo(el, alignment, offsets, animate);
8811                 Roo.callback(callback, this);
8812             };
8813             Roo.EventManager.onWindowResize(action, this);
8814             var tm = typeof monitorScroll;
8815             if(tm != 'undefined'){
8816                 Roo.EventManager.on(window, 'scroll', action, this,
8817                     {buffer: tm == 'number' ? monitorScroll : 50});
8818             }
8819             action.call(this); // align immediately
8820             return this;
8821         },
8822         /**
8823          * Clears any opacity settings from this element. Required in some cases for IE.
8824          * @return {Roo.Element} this
8825          */
8826         clearOpacity : function(){
8827             if (window.ActiveXObject) {
8828                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8829                     this.dom.style.filter = "";
8830                 }
8831             } else {
8832                 this.dom.style.opacity = "";
8833                 this.dom.style["-moz-opacity"] = "";
8834                 this.dom.style["-khtml-opacity"] = "";
8835             }
8836             return this;
8837         },
8838
8839         /**
8840          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8841          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8842          * @return {Roo.Element} this
8843          */
8844         hide : function(animate){
8845             this.setVisible(false, this.preanim(arguments, 0));
8846             return this;
8847         },
8848
8849         /**
8850         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8851         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8852          * @return {Roo.Element} this
8853          */
8854         show : function(animate){
8855             this.setVisible(true, this.preanim(arguments, 0));
8856             return this;
8857         },
8858
8859         /**
8860          * @private Test if size has a unit, otherwise appends the default
8861          */
8862         addUnits : function(size){
8863             return Roo.Element.addUnits(size, this.defaultUnit);
8864         },
8865
8866         /**
8867          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8868          * @return {Roo.Element} this
8869          */
8870         beginMeasure : function(){
8871             var el = this.dom;
8872             if(el.offsetWidth || el.offsetHeight){
8873                 return this; // offsets work already
8874             }
8875             var changed = [];
8876             var p = this.dom, b = document.body; // start with this element
8877             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8878                 var pe = Roo.get(p);
8879                 if(pe.getStyle('display') == 'none'){
8880                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8881                     p.style.visibility = "hidden";
8882                     p.style.display = "block";
8883                 }
8884                 p = p.parentNode;
8885             }
8886             this._measureChanged = changed;
8887             return this;
8888
8889         },
8890
8891         /**
8892          * Restores displays to before beginMeasure was called
8893          * @return {Roo.Element} this
8894          */
8895         endMeasure : function(){
8896             var changed = this._measureChanged;
8897             if(changed){
8898                 for(var i = 0, len = changed.length; i < len; i++) {
8899                     var r = changed[i];
8900                     r.el.style.visibility = r.visibility;
8901                     r.el.style.display = "none";
8902                 }
8903                 this._measureChanged = null;
8904             }
8905             return this;
8906         },
8907
8908         /**
8909         * Update the innerHTML of this element, optionally searching for and processing scripts
8910         * @param {String} html The new HTML
8911         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8912         * @param {Function} callback For async script loading you can be noticed when the update completes
8913         * @return {Roo.Element} this
8914          */
8915         update : function(html, loadScripts, callback){
8916             if(typeof html == "undefined"){
8917                 html = "";
8918             }
8919             if(loadScripts !== true){
8920                 this.dom.innerHTML = html;
8921                 if(typeof callback == "function"){
8922                     callback();
8923                 }
8924                 return this;
8925             }
8926             var id = Roo.id();
8927             var dom = this.dom;
8928
8929             html += '<span id="' + id + '"></span>';
8930
8931             E.onAvailable(id, function(){
8932                 var hd = document.getElementsByTagName("head")[0];
8933                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8934                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8935                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8936
8937                 var match;
8938                 while(match = re.exec(html)){
8939                     var attrs = match[1];
8940                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8941                     if(srcMatch && srcMatch[2]){
8942                        var s = document.createElement("script");
8943                        s.src = srcMatch[2];
8944                        var typeMatch = attrs.match(typeRe);
8945                        if(typeMatch && typeMatch[2]){
8946                            s.type = typeMatch[2];
8947                        }
8948                        hd.appendChild(s);
8949                     }else if(match[2] && match[2].length > 0){
8950                         if(window.execScript) {
8951                            window.execScript(match[2]);
8952                         } else {
8953                             /**
8954                              * eval:var:id
8955                              * eval:var:dom
8956                              * eval:var:html
8957                              * 
8958                              */
8959                            window.eval(match[2]);
8960                         }
8961                     }
8962                 }
8963                 var el = document.getElementById(id);
8964                 if(el){el.parentNode.removeChild(el);}
8965                 if(typeof callback == "function"){
8966                     callback();
8967                 }
8968             });
8969             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8970             return this;
8971         },
8972
8973         /**
8974          * Direct access to the UpdateManager update() method (takes the same parameters).
8975          * @param {String/Function} url The url for this request or a function to call to get the url
8976          * @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}
8977          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8978          * @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.
8979          * @return {Roo.Element} this
8980          */
8981         load : function(){
8982             var um = this.getUpdateManager();
8983             um.update.apply(um, arguments);
8984             return this;
8985         },
8986
8987         /**
8988         * Gets this element's UpdateManager
8989         * @return {Roo.UpdateManager} The UpdateManager
8990         */
8991         getUpdateManager : function(){
8992             if(!this.updateManager){
8993                 this.updateManager = new Roo.UpdateManager(this);
8994             }
8995             return this.updateManager;
8996         },
8997
8998         /**
8999          * Disables text selection for this element (normalized across browsers)
9000          * @return {Roo.Element} this
9001          */
9002         unselectable : function(){
9003             this.dom.unselectable = "on";
9004             this.swallowEvent("selectstart", true);
9005             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9006             this.addClass("x-unselectable");
9007             return this;
9008         },
9009
9010         /**
9011         * Calculates the x, y to center this element on the screen
9012         * @return {Array} The x, y values [x, y]
9013         */
9014         getCenterXY : function(){
9015             return this.getAlignToXY(document, 'c-c');
9016         },
9017
9018         /**
9019         * Centers the Element in either the viewport, or another Element.
9020         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9021         */
9022         center : function(centerIn){
9023             this.alignTo(centerIn || document, 'c-c');
9024             return this;
9025         },
9026
9027         /**
9028          * Tests various css rules/browsers to determine if this element uses a border box
9029          * @return {Boolean}
9030          */
9031         isBorderBox : function(){
9032             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9033         },
9034
9035         /**
9036          * Return a box {x, y, width, height} that can be used to set another elements
9037          * size/location to match this element.
9038          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9039          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9040          * @return {Object} box An object in the format {x, y, width, height}
9041          */
9042         getBox : function(contentBox, local){
9043             var xy;
9044             if(!local){
9045                 xy = this.getXY();
9046             }else{
9047                 var left = parseInt(this.getStyle("left"), 10) || 0;
9048                 var top = parseInt(this.getStyle("top"), 10) || 0;
9049                 xy = [left, top];
9050             }
9051             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9052             if(!contentBox){
9053                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9054             }else{
9055                 var l = this.getBorderWidth("l")+this.getPadding("l");
9056                 var r = this.getBorderWidth("r")+this.getPadding("r");
9057                 var t = this.getBorderWidth("t")+this.getPadding("t");
9058                 var b = this.getBorderWidth("b")+this.getPadding("b");
9059                 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)};
9060             }
9061             bx.right = bx.x + bx.width;
9062             bx.bottom = bx.y + bx.height;
9063             return bx;
9064         },
9065
9066         /**
9067          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9068          for more information about the sides.
9069          * @param {String} sides
9070          * @return {Number}
9071          */
9072         getFrameWidth : function(sides, onlyContentBox){
9073             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9074         },
9075
9076         /**
9077          * 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.
9078          * @param {Object} box The box to fill {x, y, width, height}
9079          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9080          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9081          * @return {Roo.Element} this
9082          */
9083         setBox : function(box, adjust, animate){
9084             var w = box.width, h = box.height;
9085             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9086                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9087                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9088             }
9089             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9090             return this;
9091         },
9092
9093         /**
9094          * Forces the browser to repaint this element
9095          * @return {Roo.Element} this
9096          */
9097          repaint : function(){
9098             var dom = this.dom;
9099             this.addClass("x-repaint");
9100             setTimeout(function(){
9101                 Roo.get(dom).removeClass("x-repaint");
9102             }, 1);
9103             return this;
9104         },
9105
9106         /**
9107          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9108          * then it returns the calculated width of the sides (see getPadding)
9109          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9110          * @return {Object/Number}
9111          */
9112         getMargins : function(side){
9113             if(!side){
9114                 return {
9115                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9116                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9117                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9118                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9119                 };
9120             }else{
9121                 return this.addStyles(side, El.margins);
9122              }
9123         },
9124
9125         // private
9126         addStyles : function(sides, styles){
9127             var val = 0, v, w;
9128             for(var i = 0, len = sides.length; i < len; i++){
9129                 v = this.getStyle(styles[sides.charAt(i)]);
9130                 if(v){
9131                      w = parseInt(v, 10);
9132                      if(w){ val += w; }
9133                 }
9134             }
9135             return val;
9136         },
9137
9138         /**
9139          * Creates a proxy element of this element
9140          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9141          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9142          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9143          * @return {Roo.Element} The new proxy element
9144          */
9145         createProxy : function(config, renderTo, matchBox){
9146             if(renderTo){
9147                 renderTo = Roo.getDom(renderTo);
9148             }else{
9149                 renderTo = document.body;
9150             }
9151             config = typeof config == "object" ?
9152                 config : {tag : "div", cls: config};
9153             var proxy = Roo.DomHelper.append(renderTo, config, true);
9154             if(matchBox){
9155                proxy.setBox(this.getBox());
9156             }
9157             return proxy;
9158         },
9159
9160         /**
9161          * Puts a mask over this element to disable user interaction. Requires core.css.
9162          * This method can only be applied to elements which accept child nodes.
9163          * @param {String} msg (optional) A message to display in the mask
9164          * @param {String} msgCls (optional) A css class to apply to the msg element
9165          * @return {Element} The mask  element
9166          */
9167         mask : function(msg, msgCls)
9168         {
9169             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9170                 this.setStyle("position", "relative");
9171             }
9172             if(!this._mask){
9173                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9174             }
9175             
9176             this.addClass("x-masked");
9177             this._mask.setDisplayed(true);
9178             
9179             // we wander
9180             var z = 0;
9181             var dom = this.dom;
9182             while (dom && dom.style) {
9183                 if (!isNaN(parseInt(dom.style.zIndex))) {
9184                     z = Math.max(z, parseInt(dom.style.zIndex));
9185                 }
9186                 dom = dom.parentNode;
9187             }
9188             // if we are masking the body - then it hides everything..
9189             if (this.dom == document.body) {
9190                 z = 1000000;
9191                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9192                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9193             }
9194            
9195             if(typeof msg == 'string'){
9196                 if(!this._maskMsg){
9197                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9198                         cls: "roo-el-mask-msg", 
9199                         cn: [
9200                             {
9201                                 tag: 'i',
9202                                 cls: 'fa fa-spinner fa-spin'
9203                             },
9204                             {
9205                                 tag: 'div'
9206                             }   
9207                         ]
9208                     }, true);
9209                 }
9210                 var mm = this._maskMsg;
9211                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9212                 if (mm.dom.lastChild) { // weird IE issue?
9213                     mm.dom.lastChild.innerHTML = msg;
9214                 }
9215                 mm.setDisplayed(true);
9216                 mm.center(this);
9217                 mm.setStyle('z-index', z + 102);
9218             }
9219             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9220                 this._mask.setHeight(this.getHeight());
9221             }
9222             this._mask.setStyle('z-index', z + 100);
9223             
9224             return this._mask;
9225         },
9226
9227         /**
9228          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9229          * it is cached for reuse.
9230          */
9231         unmask : function(removeEl){
9232             if(this._mask){
9233                 if(removeEl === true){
9234                     this._mask.remove();
9235                     delete this._mask;
9236                     if(this._maskMsg){
9237                         this._maskMsg.remove();
9238                         delete this._maskMsg;
9239                     }
9240                 }else{
9241                     this._mask.setDisplayed(false);
9242                     if(this._maskMsg){
9243                         this._maskMsg.setDisplayed(false);
9244                     }
9245                 }
9246             }
9247             this.removeClass("x-masked");
9248         },
9249
9250         /**
9251          * Returns true if this element is masked
9252          * @return {Boolean}
9253          */
9254         isMasked : function(){
9255             return this._mask && this._mask.isVisible();
9256         },
9257
9258         /**
9259          * Creates an iframe shim for this element to keep selects and other windowed objects from
9260          * showing through.
9261          * @return {Roo.Element} The new shim element
9262          */
9263         createShim : function(){
9264             var el = document.createElement('iframe');
9265             el.frameBorder = 'no';
9266             el.className = 'roo-shim';
9267             if(Roo.isIE && Roo.isSecure){
9268                 el.src = Roo.SSL_SECURE_URL;
9269             }
9270             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9271             shim.autoBoxAdjust = false;
9272             return shim;
9273         },
9274
9275         /**
9276          * Removes this element from the DOM and deletes it from the cache
9277          */
9278         remove : function(){
9279             if(this.dom.parentNode){
9280                 this.dom.parentNode.removeChild(this.dom);
9281             }
9282             delete El.cache[this.dom.id];
9283         },
9284
9285         /**
9286          * Sets up event handlers to add and remove a css class when the mouse is over this element
9287          * @param {String} className
9288          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9289          * mouseout events for children elements
9290          * @return {Roo.Element} this
9291          */
9292         addClassOnOver : function(className, preventFlicker){
9293             this.on("mouseover", function(){
9294                 Roo.fly(this, '_internal').addClass(className);
9295             }, this.dom);
9296             var removeFn = function(e){
9297                 if(preventFlicker !== true || !e.within(this, true)){
9298                     Roo.fly(this, '_internal').removeClass(className);
9299                 }
9300             };
9301             this.on("mouseout", removeFn, this.dom);
9302             return this;
9303         },
9304
9305         /**
9306          * Sets up event handlers to add and remove a css class when this element has the focus
9307          * @param {String} className
9308          * @return {Roo.Element} this
9309          */
9310         addClassOnFocus : function(className){
9311             this.on("focus", function(){
9312                 Roo.fly(this, '_internal').addClass(className);
9313             }, this.dom);
9314             this.on("blur", function(){
9315                 Roo.fly(this, '_internal').removeClass(className);
9316             }, this.dom);
9317             return this;
9318         },
9319         /**
9320          * 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)
9321          * @param {String} className
9322          * @return {Roo.Element} this
9323          */
9324         addClassOnClick : function(className){
9325             var dom = this.dom;
9326             this.on("mousedown", function(){
9327                 Roo.fly(dom, '_internal').addClass(className);
9328                 var d = Roo.get(document);
9329                 var fn = function(){
9330                     Roo.fly(dom, '_internal').removeClass(className);
9331                     d.removeListener("mouseup", fn);
9332                 };
9333                 d.on("mouseup", fn);
9334             });
9335             return this;
9336         },
9337
9338         /**
9339          * Stops the specified event from bubbling and optionally prevents the default action
9340          * @param {String} eventName
9341          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9342          * @return {Roo.Element} this
9343          */
9344         swallowEvent : function(eventName, preventDefault){
9345             var fn = function(e){
9346                 e.stopPropagation();
9347                 if(preventDefault){
9348                     e.preventDefault();
9349                 }
9350             };
9351             if(eventName instanceof Array){
9352                 for(var i = 0, len = eventName.length; i < len; i++){
9353                      this.on(eventName[i], fn);
9354                 }
9355                 return this;
9356             }
9357             this.on(eventName, fn);
9358             return this;
9359         },
9360
9361         /**
9362          * @private
9363          */
9364       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9365
9366         /**
9367          * Sizes this element to its parent element's dimensions performing
9368          * neccessary box adjustments.
9369          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9370          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9371          * @return {Roo.Element} this
9372          */
9373         fitToParent : function(monitorResize, targetParent) {
9374           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9375           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9376           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9377             return;
9378           }
9379           var p = Roo.get(targetParent || this.dom.parentNode);
9380           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9381           if (monitorResize === true) {
9382             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9383             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9384           }
9385           return this;
9386         },
9387
9388         /**
9389          * Gets the next sibling, skipping text nodes
9390          * @return {HTMLElement} The next sibling or null
9391          */
9392         getNextSibling : function(){
9393             var n = this.dom.nextSibling;
9394             while(n && n.nodeType != 1){
9395                 n = n.nextSibling;
9396             }
9397             return n;
9398         },
9399
9400         /**
9401          * Gets the previous sibling, skipping text nodes
9402          * @return {HTMLElement} The previous sibling or null
9403          */
9404         getPrevSibling : function(){
9405             var n = this.dom.previousSibling;
9406             while(n && n.nodeType != 1){
9407                 n = n.previousSibling;
9408             }
9409             return n;
9410         },
9411
9412
9413         /**
9414          * Appends the passed element(s) to this element
9415          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9416          * @return {Roo.Element} this
9417          */
9418         appendChild: function(el){
9419             el = Roo.get(el);
9420             el.appendTo(this);
9421             return this;
9422         },
9423
9424         /**
9425          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9426          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9427          * automatically generated with the specified attributes.
9428          * @param {HTMLElement} insertBefore (optional) a child element of this element
9429          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9430          * @return {Roo.Element} The new child element
9431          */
9432         createChild: function(config, insertBefore, returnDom){
9433             config = config || {tag:'div'};
9434             if(insertBefore){
9435                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9436             }
9437             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9438         },
9439
9440         /**
9441          * Appends this element to the passed element
9442          * @param {String/HTMLElement/Element} el The new parent element
9443          * @return {Roo.Element} this
9444          */
9445         appendTo: function(el){
9446             el = Roo.getDom(el);
9447             el.appendChild(this.dom);
9448             return this;
9449         },
9450
9451         /**
9452          * Inserts this element before the passed element in the DOM
9453          * @param {String/HTMLElement/Element} el The element to insert before
9454          * @return {Roo.Element} this
9455          */
9456         insertBefore: function(el){
9457             el = Roo.getDom(el);
9458             el.parentNode.insertBefore(this.dom, el);
9459             return this;
9460         },
9461
9462         /**
9463          * Inserts this element after the passed element in the DOM
9464          * @param {String/HTMLElement/Element} el The element to insert after
9465          * @return {Roo.Element} this
9466          */
9467         insertAfter: function(el){
9468             el = Roo.getDom(el);
9469             el.parentNode.insertBefore(this.dom, el.nextSibling);
9470             return this;
9471         },
9472
9473         /**
9474          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9475          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9476          * @return {Roo.Element} The new child
9477          */
9478         insertFirst: function(el, returnDom){
9479             el = el || {};
9480             if(typeof el == 'object' && !el.nodeType){ // dh config
9481                 return this.createChild(el, this.dom.firstChild, returnDom);
9482             }else{
9483                 el = Roo.getDom(el);
9484                 this.dom.insertBefore(el, this.dom.firstChild);
9485                 return !returnDom ? Roo.get(el) : el;
9486             }
9487         },
9488
9489         /**
9490          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9491          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9492          * @param {String} where (optional) 'before' or 'after' defaults to before
9493          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9494          * @return {Roo.Element} the inserted Element
9495          */
9496         insertSibling: function(el, where, returnDom){
9497             where = where ? where.toLowerCase() : 'before';
9498             el = el || {};
9499             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9500
9501             if(typeof el == 'object' && !el.nodeType){ // dh config
9502                 if(where == 'after' && !this.dom.nextSibling){
9503                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9504                 }else{
9505                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9506                 }
9507
9508             }else{
9509                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9510                             where == 'before' ? this.dom : this.dom.nextSibling);
9511                 if(!returnDom){
9512                     rt = Roo.get(rt);
9513                 }
9514             }
9515             return rt;
9516         },
9517
9518         /**
9519          * Creates and wraps this element with another element
9520          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9521          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9522          * @return {HTMLElement/Element} The newly created wrapper element
9523          */
9524         wrap: function(config, returnDom){
9525             if(!config){
9526                 config = {tag: "div"};
9527             }
9528             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9529             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9530             return newEl;
9531         },
9532
9533         /**
9534          * Replaces the passed element with this element
9535          * @param {String/HTMLElement/Element} el The element to replace
9536          * @return {Roo.Element} this
9537          */
9538         replace: function(el){
9539             el = Roo.get(el);
9540             this.insertBefore(el);
9541             el.remove();
9542             return this;
9543         },
9544
9545         /**
9546          * Inserts an html fragment into this element
9547          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9548          * @param {String} html The HTML fragment
9549          * @param {Boolean} returnEl True to return an Roo.Element
9550          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9551          */
9552         insertHtml : function(where, html, returnEl){
9553             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9554             return returnEl ? Roo.get(el) : el;
9555         },
9556
9557         /**
9558          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9559          * @param {Object} o The object with the attributes
9560          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9561          * @return {Roo.Element} this
9562          */
9563         set : function(o, useSet){
9564             var el = this.dom;
9565             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9566             for(var attr in o){
9567                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9568                 if(attr=="cls"){
9569                     el.className = o["cls"];
9570                 }else{
9571                     if(useSet) {
9572                         el.setAttribute(attr, o[attr]);
9573                     } else {
9574                         el[attr] = o[attr];
9575                     }
9576                 }
9577             }
9578             if(o.style){
9579                 Roo.DomHelper.applyStyles(el, o.style);
9580             }
9581             return this;
9582         },
9583
9584         /**
9585          * Convenience method for constructing a KeyMap
9586          * @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:
9587          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9588          * @param {Function} fn The function to call
9589          * @param {Object} scope (optional) The scope of the function
9590          * @return {Roo.KeyMap} The KeyMap created
9591          */
9592         addKeyListener : function(key, fn, scope){
9593             var config;
9594             if(typeof key != "object" || key instanceof Array){
9595                 config = {
9596                     key: key,
9597                     fn: fn,
9598                     scope: scope
9599                 };
9600             }else{
9601                 config = {
9602                     key : key.key,
9603                     shift : key.shift,
9604                     ctrl : key.ctrl,
9605                     alt : key.alt,
9606                     fn: fn,
9607                     scope: scope
9608                 };
9609             }
9610             return new Roo.KeyMap(this, config);
9611         },
9612
9613         /**
9614          * Creates a KeyMap for this element
9615          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9616          * @return {Roo.KeyMap} The KeyMap created
9617          */
9618         addKeyMap : function(config){
9619             return new Roo.KeyMap(this, config);
9620         },
9621
9622         /**
9623          * Returns true if this element is scrollable.
9624          * @return {Boolean}
9625          */
9626          isScrollable : function(){
9627             var dom = this.dom;
9628             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9629         },
9630
9631         /**
9632          * 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().
9633          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9634          * @param {Number} value The new scroll value
9635          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9636          * @return {Element} this
9637          */
9638
9639         scrollTo : function(side, value, animate){
9640             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9641             if(!animate || !A){
9642                 this.dom[prop] = value;
9643             }else{
9644                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9645                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9646             }
9647             return this;
9648         },
9649
9650         /**
9651          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9652          * within this element's scrollable range.
9653          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9654          * @param {Number} distance How far to scroll the element in pixels
9655          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9656          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9657          * was scrolled as far as it could go.
9658          */
9659          scroll : function(direction, distance, animate){
9660              if(!this.isScrollable()){
9661                  return;
9662              }
9663              var el = this.dom;
9664              var l = el.scrollLeft, t = el.scrollTop;
9665              var w = el.scrollWidth, h = el.scrollHeight;
9666              var cw = el.clientWidth, ch = el.clientHeight;
9667              direction = direction.toLowerCase();
9668              var scrolled = false;
9669              var a = this.preanim(arguments, 2);
9670              switch(direction){
9671                  case "l":
9672                  case "left":
9673                      if(w - l > cw){
9674                          var v = Math.min(l + distance, w-cw);
9675                          this.scrollTo("left", v, a);
9676                          scrolled = true;
9677                      }
9678                      break;
9679                 case "r":
9680                 case "right":
9681                      if(l > 0){
9682                          var v = Math.max(l - distance, 0);
9683                          this.scrollTo("left", v, a);
9684                          scrolled = true;
9685                      }
9686                      break;
9687                 case "t":
9688                 case "top":
9689                 case "up":
9690                      if(t > 0){
9691                          var v = Math.max(t - distance, 0);
9692                          this.scrollTo("top", v, a);
9693                          scrolled = true;
9694                      }
9695                      break;
9696                 case "b":
9697                 case "bottom":
9698                 case "down":
9699                      if(h - t > ch){
9700                          var v = Math.min(t + distance, h-ch);
9701                          this.scrollTo("top", v, a);
9702                          scrolled = true;
9703                      }
9704                      break;
9705              }
9706              return scrolled;
9707         },
9708
9709         /**
9710          * Translates the passed page coordinates into left/top css values for this element
9711          * @param {Number/Array} x The page x or an array containing [x, y]
9712          * @param {Number} y The page y
9713          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9714          */
9715         translatePoints : function(x, y){
9716             if(typeof x == 'object' || x instanceof Array){
9717                 y = x[1]; x = x[0];
9718             }
9719             var p = this.getStyle('position');
9720             var o = this.getXY();
9721
9722             var l = parseInt(this.getStyle('left'), 10);
9723             var t = parseInt(this.getStyle('top'), 10);
9724
9725             if(isNaN(l)){
9726                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9727             }
9728             if(isNaN(t)){
9729                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9730             }
9731
9732             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9733         },
9734
9735         /**
9736          * Returns the current scroll position of the element.
9737          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9738          */
9739         getScroll : function(){
9740             var d = this.dom, doc = document;
9741             if(d == doc || d == doc.body){
9742                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9743                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9744                 return {left: l, top: t};
9745             }else{
9746                 return {left: d.scrollLeft, top: d.scrollTop};
9747             }
9748         },
9749
9750         /**
9751          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9752          * are convert to standard 6 digit hex color.
9753          * @param {String} attr The css attribute
9754          * @param {String} defaultValue The default value to use when a valid color isn't found
9755          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9756          * YUI color anims.
9757          */
9758         getColor : function(attr, defaultValue, prefix){
9759             var v = this.getStyle(attr);
9760             if(!v || v == "transparent" || v == "inherit") {
9761                 return defaultValue;
9762             }
9763             var color = typeof prefix == "undefined" ? "#" : prefix;
9764             if(v.substr(0, 4) == "rgb("){
9765                 var rvs = v.slice(4, v.length -1).split(",");
9766                 for(var i = 0; i < 3; i++){
9767                     var h = parseInt(rvs[i]).toString(16);
9768                     if(h < 16){
9769                         h = "0" + h;
9770                     }
9771                     color += h;
9772                 }
9773             } else {
9774                 if(v.substr(0, 1) == "#"){
9775                     if(v.length == 4) {
9776                         for(var i = 1; i < 4; i++){
9777                             var c = v.charAt(i);
9778                             color +=  c + c;
9779                         }
9780                     }else if(v.length == 7){
9781                         color += v.substr(1);
9782                     }
9783                 }
9784             }
9785             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9786         },
9787
9788         /**
9789          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9790          * gradient background, rounded corners and a 4-way shadow.
9791          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9792          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9793          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9794          * @return {Roo.Element} this
9795          */
9796         boxWrap : function(cls){
9797             cls = cls || 'x-box';
9798             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9799             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9800             return el;
9801         },
9802
9803         /**
9804          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9805          * @param {String} namespace The namespace in which to look for the attribute
9806          * @param {String} name The attribute name
9807          * @return {String} The attribute value
9808          */
9809         getAttributeNS : Roo.isIE ? function(ns, name){
9810             var d = this.dom;
9811             var type = typeof d[ns+":"+name];
9812             if(type != 'undefined' && type != 'unknown'){
9813                 return d[ns+":"+name];
9814             }
9815             return d[name];
9816         } : function(ns, name){
9817             var d = this.dom;
9818             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9819         },
9820         
9821         
9822         /**
9823          * Sets or Returns the value the dom attribute value
9824          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9825          * @param {String} value (optional) The value to set the attribute to
9826          * @return {String} The attribute value
9827          */
9828         attr : function(name){
9829             if (arguments.length > 1) {
9830                 this.dom.setAttribute(name, arguments[1]);
9831                 return arguments[1];
9832             }
9833             if (typeof(name) == 'object') {
9834                 for(var i in name) {
9835                     this.attr(i, name[i]);
9836                 }
9837                 return name;
9838             }
9839             
9840             
9841             if (!this.dom.hasAttribute(name)) {
9842                 return undefined;
9843             }
9844             return this.dom.getAttribute(name);
9845         }
9846         
9847         
9848         
9849     };
9850
9851     var ep = El.prototype;
9852
9853     /**
9854      * Appends an event handler (Shorthand for addListener)
9855      * @param {String}   eventName     The type of event to append
9856      * @param {Function} fn        The method the event invokes
9857      * @param {Object} scope       (optional) The scope (this object) of the fn
9858      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9859      * @method
9860      */
9861     ep.on = ep.addListener;
9862         // backwards compat
9863     ep.mon = ep.addListener;
9864
9865     /**
9866      * Removes an event handler from this element (shorthand for removeListener)
9867      * @param {String} eventName the type of event to remove
9868      * @param {Function} fn the method the event invokes
9869      * @return {Roo.Element} this
9870      * @method
9871      */
9872     ep.un = ep.removeListener;
9873
9874     /**
9875      * true to automatically adjust width and height settings for box-model issues (default to true)
9876      */
9877     ep.autoBoxAdjust = true;
9878
9879     // private
9880     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9881
9882     // private
9883     El.addUnits = function(v, defaultUnit){
9884         if(v === "" || v == "auto"){
9885             return v;
9886         }
9887         if(v === undefined){
9888             return '';
9889         }
9890         if(typeof v == "number" || !El.unitPattern.test(v)){
9891             return v + (defaultUnit || 'px');
9892         }
9893         return v;
9894     };
9895
9896     // special markup used throughout Roo when box wrapping elements
9897     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>';
9898     /**
9899      * Visibility mode constant - Use visibility to hide element
9900      * @static
9901      * @type Number
9902      */
9903     El.VISIBILITY = 1;
9904     /**
9905      * Visibility mode constant - Use display to hide element
9906      * @static
9907      * @type Number
9908      */
9909     El.DISPLAY = 2;
9910
9911     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9912     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9913     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9914
9915
9916
9917     /**
9918      * @private
9919      */
9920     El.cache = {};
9921
9922     var docEl;
9923
9924     /**
9925      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9926      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9927      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9928      * @return {Element} The Element object
9929      * @static
9930      */
9931     El.get = function(el){
9932         var ex, elm, id;
9933         if(!el){ return null; }
9934         if(typeof el == "string"){ // element id
9935             if(!(elm = document.getElementById(el))){
9936                 return null;
9937             }
9938             if(ex = El.cache[el]){
9939                 ex.dom = elm;
9940             }else{
9941                 ex = El.cache[el] = new El(elm);
9942             }
9943             return ex;
9944         }else if(el.tagName){ // dom element
9945             if(!(id = el.id)){
9946                 id = Roo.id(el);
9947             }
9948             if(ex = El.cache[id]){
9949                 ex.dom = el;
9950             }else{
9951                 ex = El.cache[id] = new El(el);
9952             }
9953             return ex;
9954         }else if(el instanceof El){
9955             if(el != docEl){
9956                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9957                                                               // catch case where it hasn't been appended
9958                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9959             }
9960             return el;
9961         }else if(el.isComposite){
9962             return el;
9963         }else if(el instanceof Array){
9964             return El.select(el);
9965         }else if(el == document){
9966             // create a bogus element object representing the document object
9967             if(!docEl){
9968                 var f = function(){};
9969                 f.prototype = El.prototype;
9970                 docEl = new f();
9971                 docEl.dom = document;
9972             }
9973             return docEl;
9974         }
9975         return null;
9976     };
9977
9978     // private
9979     El.uncache = function(el){
9980         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9981             if(a[i]){
9982                 delete El.cache[a[i].id || a[i]];
9983             }
9984         }
9985     };
9986
9987     // private
9988     // Garbage collection - uncache elements/purge listeners on orphaned elements
9989     // so we don't hold a reference and cause the browser to retain them
9990     El.garbageCollect = function(){
9991         if(!Roo.enableGarbageCollector){
9992             clearInterval(El.collectorThread);
9993             return;
9994         }
9995         for(var eid in El.cache){
9996             var el = El.cache[eid], d = el.dom;
9997             // -------------------------------------------------------
9998             // Determining what is garbage:
9999             // -------------------------------------------------------
10000             // !d
10001             // dom node is null, definitely garbage
10002             // -------------------------------------------------------
10003             // !d.parentNode
10004             // no parentNode == direct orphan, definitely garbage
10005             // -------------------------------------------------------
10006             // !d.offsetParent && !document.getElementById(eid)
10007             // display none elements have no offsetParent so we will
10008             // also try to look it up by it's id. However, check
10009             // offsetParent first so we don't do unneeded lookups.
10010             // This enables collection of elements that are not orphans
10011             // directly, but somewhere up the line they have an orphan
10012             // parent.
10013             // -------------------------------------------------------
10014             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10015                 delete El.cache[eid];
10016                 if(d && Roo.enableListenerCollection){
10017                     E.purgeElement(d);
10018                 }
10019             }
10020         }
10021     }
10022     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10023
10024
10025     // dom is optional
10026     El.Flyweight = function(dom){
10027         this.dom = dom;
10028     };
10029     El.Flyweight.prototype = El.prototype;
10030
10031     El._flyweights = {};
10032     /**
10033      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10034      * the dom node can be overwritten by other code.
10035      * @param {String/HTMLElement} el The dom node or id
10036      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10037      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10038      * @static
10039      * @return {Element} The shared Element object
10040      */
10041     El.fly = function(el, named){
10042         named = named || '_global';
10043         el = Roo.getDom(el);
10044         if(!el){
10045             return null;
10046         }
10047         if(!El._flyweights[named]){
10048             El._flyweights[named] = new El.Flyweight();
10049         }
10050         El._flyweights[named].dom = el;
10051         return El._flyweights[named];
10052     };
10053
10054     /**
10055      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10056      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10057      * Shorthand of {@link Roo.Element#get}
10058      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10059      * @return {Element} The Element object
10060      * @member Roo
10061      * @method get
10062      */
10063     Roo.get = El.get;
10064     /**
10065      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10066      * the dom node can be overwritten by other code.
10067      * Shorthand of {@link Roo.Element#fly}
10068      * @param {String/HTMLElement} el The dom node or id
10069      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10070      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10071      * @static
10072      * @return {Element} The shared Element object
10073      * @member Roo
10074      * @method fly
10075      */
10076     Roo.fly = El.fly;
10077
10078     // speedy lookup for elements never to box adjust
10079     var noBoxAdjust = Roo.isStrict ? {
10080         select:1
10081     } : {
10082         input:1, select:1, textarea:1
10083     };
10084     if(Roo.isIE || Roo.isGecko){
10085         noBoxAdjust['button'] = 1;
10086     }
10087
10088
10089     Roo.EventManager.on(window, 'unload', function(){
10090         delete El.cache;
10091         delete El._flyweights;
10092     });
10093 })();
10094
10095
10096
10097
10098 if(Roo.DomQuery){
10099     Roo.Element.selectorFunction = Roo.DomQuery.select;
10100 }
10101
10102 Roo.Element.select = function(selector, unique, root){
10103     var els;
10104     if(typeof selector == "string"){
10105         els = Roo.Element.selectorFunction(selector, root);
10106     }else if(selector.length !== undefined){
10107         els = selector;
10108     }else{
10109         throw "Invalid selector";
10110     }
10111     if(unique === true){
10112         return new Roo.CompositeElement(els);
10113     }else{
10114         return new Roo.CompositeElementLite(els);
10115     }
10116 };
10117 /**
10118  * Selects elements based on the passed CSS selector to enable working on them as 1.
10119  * @param {String/Array} selector The CSS selector or an array of elements
10120  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10121  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10122  * @return {CompositeElementLite/CompositeElement}
10123  * @member Roo
10124  * @method select
10125  */
10126 Roo.select = Roo.Element.select;
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141 /*
10142  * Based on:
10143  * Ext JS Library 1.1.1
10144  * Copyright(c) 2006-2007, Ext JS, LLC.
10145  *
10146  * Originally Released Under LGPL - original licence link has changed is not relivant.
10147  *
10148  * Fork - LGPL
10149  * <script type="text/javascript">
10150  */
10151
10152
10153
10154 //Notifies Element that fx methods are available
10155 Roo.enableFx = true;
10156
10157 /**
10158  * @class Roo.Fx
10159  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10160  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10161  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10162  * Element effects to work.</p><br/>
10163  *
10164  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10165  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10166  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10167  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10168  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10169  * expected results and should be done with care.</p><br/>
10170  *
10171  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10172  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10173 <pre>
10174 Value  Description
10175 -----  -----------------------------
10176 tl     The top left corner
10177 t      The center of the top edge
10178 tr     The top right corner
10179 l      The center of the left edge
10180 r      The center of the right edge
10181 bl     The bottom left corner
10182 b      The center of the bottom edge
10183 br     The bottom right corner
10184 </pre>
10185  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10186  * below are common options that can be passed to any Fx method.</b>
10187  * @cfg {Function} callback A function called when the effect is finished
10188  * @cfg {Object} scope The scope of the effect function
10189  * @cfg {String} easing A valid Easing value for the effect
10190  * @cfg {String} afterCls A css class to apply after the effect
10191  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10192  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10193  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10194  * effects that end with the element being visually hidden, ignored otherwise)
10195  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10196  * a function which returns such a specification that will be applied to the Element after the effect finishes
10197  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10198  * @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
10199  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10200  */
10201 Roo.Fx = {
10202         /**
10203          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10204          * origin for the slide effect.  This function automatically handles wrapping the element with
10205          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10206          * Usage:
10207          *<pre><code>
10208 // default: slide the element in from the top
10209 el.slideIn();
10210
10211 // custom: slide the element in from the right with a 2-second duration
10212 el.slideIn('r', { duration: 2 });
10213
10214 // common config options shown with default values
10215 el.slideIn('t', {
10216     easing: 'easeOut',
10217     duration: .5
10218 });
10219 </code></pre>
10220          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10221          * @param {Object} options (optional) Object literal with any of the Fx config options
10222          * @return {Roo.Element} The Element
10223          */
10224     slideIn : function(anchor, o){
10225         var el = this.getFxEl();
10226         o = o || {};
10227
10228         el.queueFx(o, function(){
10229
10230             anchor = anchor || "t";
10231
10232             // fix display to visibility
10233             this.fixDisplay();
10234
10235             // restore values after effect
10236             var r = this.getFxRestore();
10237             var b = this.getBox();
10238             // fixed size for slide
10239             this.setSize(b);
10240
10241             // wrap if needed
10242             var wrap = this.fxWrap(r.pos, o, "hidden");
10243
10244             var st = this.dom.style;
10245             st.visibility = "visible";
10246             st.position = "absolute";
10247
10248             // clear out temp styles after slide and unwrap
10249             var after = function(){
10250                 el.fxUnwrap(wrap, r.pos, o);
10251                 st.width = r.width;
10252                 st.height = r.height;
10253                 el.afterFx(o);
10254             };
10255             // time to calc the positions
10256             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10257
10258             switch(anchor.toLowerCase()){
10259                 case "t":
10260                     wrap.setSize(b.width, 0);
10261                     st.left = st.bottom = "0";
10262                     a = {height: bh};
10263                 break;
10264                 case "l":
10265                     wrap.setSize(0, b.height);
10266                     st.right = st.top = "0";
10267                     a = {width: bw};
10268                 break;
10269                 case "r":
10270                     wrap.setSize(0, b.height);
10271                     wrap.setX(b.right);
10272                     st.left = st.top = "0";
10273                     a = {width: bw, points: pt};
10274                 break;
10275                 case "b":
10276                     wrap.setSize(b.width, 0);
10277                     wrap.setY(b.bottom);
10278                     st.left = st.top = "0";
10279                     a = {height: bh, points: pt};
10280                 break;
10281                 case "tl":
10282                     wrap.setSize(0, 0);
10283                     st.right = st.bottom = "0";
10284                     a = {width: bw, height: bh};
10285                 break;
10286                 case "bl":
10287                     wrap.setSize(0, 0);
10288                     wrap.setY(b.y+b.height);
10289                     st.right = st.top = "0";
10290                     a = {width: bw, height: bh, points: pt};
10291                 break;
10292                 case "br":
10293                     wrap.setSize(0, 0);
10294                     wrap.setXY([b.right, b.bottom]);
10295                     st.left = st.top = "0";
10296                     a = {width: bw, height: bh, points: pt};
10297                 break;
10298                 case "tr":
10299                     wrap.setSize(0, 0);
10300                     wrap.setX(b.x+b.width);
10301                     st.left = st.bottom = "0";
10302                     a = {width: bw, height: bh, points: pt};
10303                 break;
10304             }
10305             this.dom.style.visibility = "visible";
10306             wrap.show();
10307
10308             arguments.callee.anim = wrap.fxanim(a,
10309                 o,
10310                 'motion',
10311                 .5,
10312                 'easeOut', after);
10313         });
10314         return this;
10315     },
10316     
10317         /**
10318          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10319          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10320          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10321          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10322          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10323          * Usage:
10324          *<pre><code>
10325 // default: slide the element out to the top
10326 el.slideOut();
10327
10328 // custom: slide the element out to the right with a 2-second duration
10329 el.slideOut('r', { duration: 2 });
10330
10331 // common config options shown with default values
10332 el.slideOut('t', {
10333     easing: 'easeOut',
10334     duration: .5,
10335     remove: false,
10336     useDisplay: false
10337 });
10338 </code></pre>
10339          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10340          * @param {Object} options (optional) Object literal with any of the Fx config options
10341          * @return {Roo.Element} The Element
10342          */
10343     slideOut : function(anchor, o){
10344         var el = this.getFxEl();
10345         o = o || {};
10346
10347         el.queueFx(o, function(){
10348
10349             anchor = anchor || "t";
10350
10351             // restore values after effect
10352             var r = this.getFxRestore();
10353             
10354             var b = this.getBox();
10355             // fixed size for slide
10356             this.setSize(b);
10357
10358             // wrap if needed
10359             var wrap = this.fxWrap(r.pos, o, "visible");
10360
10361             var st = this.dom.style;
10362             st.visibility = "visible";
10363             st.position = "absolute";
10364
10365             wrap.setSize(b);
10366
10367             var after = function(){
10368                 if(o.useDisplay){
10369                     el.setDisplayed(false);
10370                 }else{
10371                     el.hide();
10372                 }
10373
10374                 el.fxUnwrap(wrap, r.pos, o);
10375
10376                 st.width = r.width;
10377                 st.height = r.height;
10378
10379                 el.afterFx(o);
10380             };
10381
10382             var a, zero = {to: 0};
10383             switch(anchor.toLowerCase()){
10384                 case "t":
10385                     st.left = st.bottom = "0";
10386                     a = {height: zero};
10387                 break;
10388                 case "l":
10389                     st.right = st.top = "0";
10390                     a = {width: zero};
10391                 break;
10392                 case "r":
10393                     st.left = st.top = "0";
10394                     a = {width: zero, points: {to:[b.right, b.y]}};
10395                 break;
10396                 case "b":
10397                     st.left = st.top = "0";
10398                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10399                 break;
10400                 case "tl":
10401                     st.right = st.bottom = "0";
10402                     a = {width: zero, height: zero};
10403                 break;
10404                 case "bl":
10405                     st.right = st.top = "0";
10406                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10407                 break;
10408                 case "br":
10409                     st.left = st.top = "0";
10410                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10411                 break;
10412                 case "tr":
10413                     st.left = st.bottom = "0";
10414                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10415                 break;
10416             }
10417
10418             arguments.callee.anim = wrap.fxanim(a,
10419                 o,
10420                 'motion',
10421                 .5,
10422                 "easeOut", after);
10423         });
10424         return this;
10425     },
10426
10427         /**
10428          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10429          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10430          * The element must be removed from the DOM using the 'remove' config option if desired.
10431          * Usage:
10432          *<pre><code>
10433 // default
10434 el.puff();
10435
10436 // common config options shown with default values
10437 el.puff({
10438     easing: 'easeOut',
10439     duration: .5,
10440     remove: false,
10441     useDisplay: false
10442 });
10443 </code></pre>
10444          * @param {Object} options (optional) Object literal with any of the Fx config options
10445          * @return {Roo.Element} The Element
10446          */
10447     puff : function(o){
10448         var el = this.getFxEl();
10449         o = o || {};
10450
10451         el.queueFx(o, function(){
10452             this.clearOpacity();
10453             this.show();
10454
10455             // restore values after effect
10456             var r = this.getFxRestore();
10457             var st = this.dom.style;
10458
10459             var after = function(){
10460                 if(o.useDisplay){
10461                     el.setDisplayed(false);
10462                 }else{
10463                     el.hide();
10464                 }
10465
10466                 el.clearOpacity();
10467
10468                 el.setPositioning(r.pos);
10469                 st.width = r.width;
10470                 st.height = r.height;
10471                 st.fontSize = '';
10472                 el.afterFx(o);
10473             };
10474
10475             var width = this.getWidth();
10476             var height = this.getHeight();
10477
10478             arguments.callee.anim = this.fxanim({
10479                     width : {to: this.adjustWidth(width * 2)},
10480                     height : {to: this.adjustHeight(height * 2)},
10481                     points : {by: [-(width * .5), -(height * .5)]},
10482                     opacity : {to: 0},
10483                     fontSize: {to:200, unit: "%"}
10484                 },
10485                 o,
10486                 'motion',
10487                 .5,
10488                 "easeOut", after);
10489         });
10490         return this;
10491     },
10492
10493         /**
10494          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10495          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10496          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10497          * Usage:
10498          *<pre><code>
10499 // default
10500 el.switchOff();
10501
10502 // all config options shown with default values
10503 el.switchOff({
10504     easing: 'easeIn',
10505     duration: .3,
10506     remove: false,
10507     useDisplay: false
10508 });
10509 </code></pre>
10510          * @param {Object} options (optional) Object literal with any of the Fx config options
10511          * @return {Roo.Element} The Element
10512          */
10513     switchOff : function(o){
10514         var el = this.getFxEl();
10515         o = o || {};
10516
10517         el.queueFx(o, function(){
10518             this.clearOpacity();
10519             this.clip();
10520
10521             // restore values after effect
10522             var r = this.getFxRestore();
10523             var st = this.dom.style;
10524
10525             var after = function(){
10526                 if(o.useDisplay){
10527                     el.setDisplayed(false);
10528                 }else{
10529                     el.hide();
10530                 }
10531
10532                 el.clearOpacity();
10533                 el.setPositioning(r.pos);
10534                 st.width = r.width;
10535                 st.height = r.height;
10536
10537                 el.afterFx(o);
10538             };
10539
10540             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10541                 this.clearOpacity();
10542                 (function(){
10543                     this.fxanim({
10544                         height:{to:1},
10545                         points:{by:[0, this.getHeight() * .5]}
10546                     }, o, 'motion', 0.3, 'easeIn', after);
10547                 }).defer(100, this);
10548             });
10549         });
10550         return this;
10551     },
10552
10553     /**
10554      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10555      * changed using the "attr" config option) and then fading back to the original color. If no original
10556      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10557      * Usage:
10558 <pre><code>
10559 // default: highlight background to yellow
10560 el.highlight();
10561
10562 // custom: highlight foreground text to blue for 2 seconds
10563 el.highlight("0000ff", { attr: 'color', duration: 2 });
10564
10565 // common config options shown with default values
10566 el.highlight("ffff9c", {
10567     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10568     endColor: (current color) or "ffffff",
10569     easing: 'easeIn',
10570     duration: 1
10571 });
10572 </code></pre>
10573      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10574      * @param {Object} options (optional) Object literal with any of the Fx config options
10575      * @return {Roo.Element} The Element
10576      */ 
10577     highlight : function(color, o){
10578         var el = this.getFxEl();
10579         o = o || {};
10580
10581         el.queueFx(o, function(){
10582             color = color || "ffff9c";
10583             attr = o.attr || "backgroundColor";
10584
10585             this.clearOpacity();
10586             this.show();
10587
10588             var origColor = this.getColor(attr);
10589             var restoreColor = this.dom.style[attr];
10590             endColor = (o.endColor || origColor) || "ffffff";
10591
10592             var after = function(){
10593                 el.dom.style[attr] = restoreColor;
10594                 el.afterFx(o);
10595             };
10596
10597             var a = {};
10598             a[attr] = {from: color, to: endColor};
10599             arguments.callee.anim = this.fxanim(a,
10600                 o,
10601                 'color',
10602                 1,
10603                 'easeIn', after);
10604         });
10605         return this;
10606     },
10607
10608    /**
10609     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10610     * Usage:
10611 <pre><code>
10612 // default: a single light blue ripple
10613 el.frame();
10614
10615 // custom: 3 red ripples lasting 3 seconds total
10616 el.frame("ff0000", 3, { duration: 3 });
10617
10618 // common config options shown with default values
10619 el.frame("C3DAF9", 1, {
10620     duration: 1 //duration of entire animation (not each individual ripple)
10621     // Note: Easing is not configurable and will be ignored if included
10622 });
10623 </code></pre>
10624     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10625     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10626     * @param {Object} options (optional) Object literal with any of the Fx config options
10627     * @return {Roo.Element} The Element
10628     */
10629     frame : function(color, count, o){
10630         var el = this.getFxEl();
10631         o = o || {};
10632
10633         el.queueFx(o, function(){
10634             color = color || "#C3DAF9";
10635             if(color.length == 6){
10636                 color = "#" + color;
10637             }
10638             count = count || 1;
10639             duration = o.duration || 1;
10640             this.show();
10641
10642             var b = this.getBox();
10643             var animFn = function(){
10644                 var proxy = this.createProxy({
10645
10646                      style:{
10647                         visbility:"hidden",
10648                         position:"absolute",
10649                         "z-index":"35000", // yee haw
10650                         border:"0px solid " + color
10651                      }
10652                   });
10653                 var scale = Roo.isBorderBox ? 2 : 1;
10654                 proxy.animate({
10655                     top:{from:b.y, to:b.y - 20},
10656                     left:{from:b.x, to:b.x - 20},
10657                     borderWidth:{from:0, to:10},
10658                     opacity:{from:1, to:0},
10659                     height:{from:b.height, to:(b.height + (20*scale))},
10660                     width:{from:b.width, to:(b.width + (20*scale))}
10661                 }, duration, function(){
10662                     proxy.remove();
10663                 });
10664                 if(--count > 0){
10665                      animFn.defer((duration/2)*1000, this);
10666                 }else{
10667                     el.afterFx(o);
10668                 }
10669             };
10670             animFn.call(this);
10671         });
10672         return this;
10673     },
10674
10675    /**
10676     * Creates a pause before any subsequent queued effects begin.  If there are
10677     * no effects queued after the pause it will have no effect.
10678     * Usage:
10679 <pre><code>
10680 el.pause(1);
10681 </code></pre>
10682     * @param {Number} seconds The length of time to pause (in seconds)
10683     * @return {Roo.Element} The Element
10684     */
10685     pause : function(seconds){
10686         var el = this.getFxEl();
10687         var o = {};
10688
10689         el.queueFx(o, function(){
10690             setTimeout(function(){
10691                 el.afterFx(o);
10692             }, seconds * 1000);
10693         });
10694         return this;
10695     },
10696
10697    /**
10698     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10699     * using the "endOpacity" config option.
10700     * Usage:
10701 <pre><code>
10702 // default: fade in from opacity 0 to 100%
10703 el.fadeIn();
10704
10705 // custom: fade in from opacity 0 to 75% over 2 seconds
10706 el.fadeIn({ endOpacity: .75, duration: 2});
10707
10708 // common config options shown with default values
10709 el.fadeIn({
10710     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10711     easing: 'easeOut',
10712     duration: .5
10713 });
10714 </code></pre>
10715     * @param {Object} options (optional) Object literal with any of the Fx config options
10716     * @return {Roo.Element} The Element
10717     */
10718     fadeIn : function(o){
10719         var el = this.getFxEl();
10720         o = o || {};
10721         el.queueFx(o, function(){
10722             this.setOpacity(0);
10723             this.fixDisplay();
10724             this.dom.style.visibility = 'visible';
10725             var to = o.endOpacity || 1;
10726             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10727                 o, null, .5, "easeOut", function(){
10728                 if(to == 1){
10729                     this.clearOpacity();
10730                 }
10731                 el.afterFx(o);
10732             });
10733         });
10734         return this;
10735     },
10736
10737    /**
10738     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10739     * using the "endOpacity" config option.
10740     * Usage:
10741 <pre><code>
10742 // default: fade out from the element's current opacity to 0
10743 el.fadeOut();
10744
10745 // custom: fade out from the element's current opacity to 25% over 2 seconds
10746 el.fadeOut({ endOpacity: .25, duration: 2});
10747
10748 // common config options shown with default values
10749 el.fadeOut({
10750     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10751     easing: 'easeOut',
10752     duration: .5
10753     remove: false,
10754     useDisplay: false
10755 });
10756 </code></pre>
10757     * @param {Object} options (optional) Object literal with any of the Fx config options
10758     * @return {Roo.Element} The Element
10759     */
10760     fadeOut : function(o){
10761         var el = this.getFxEl();
10762         o = o || {};
10763         el.queueFx(o, function(){
10764             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10765                 o, null, .5, "easeOut", function(){
10766                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10767                      this.dom.style.display = "none";
10768                 }else{
10769                      this.dom.style.visibility = "hidden";
10770                 }
10771                 this.clearOpacity();
10772                 el.afterFx(o);
10773             });
10774         });
10775         return this;
10776     },
10777
10778    /**
10779     * Animates the transition of an element's dimensions from a starting height/width
10780     * to an ending height/width.
10781     * Usage:
10782 <pre><code>
10783 // change height and width to 100x100 pixels
10784 el.scale(100, 100);
10785
10786 // common config options shown with default values.  The height and width will default to
10787 // the element's existing values if passed as null.
10788 el.scale(
10789     [element's width],
10790     [element's height], {
10791     easing: 'easeOut',
10792     duration: .35
10793 });
10794 </code></pre>
10795     * @param {Number} width  The new width (pass undefined to keep the original width)
10796     * @param {Number} height  The new height (pass undefined to keep the original height)
10797     * @param {Object} options (optional) Object literal with any of the Fx config options
10798     * @return {Roo.Element} The Element
10799     */
10800     scale : function(w, h, o){
10801         this.shift(Roo.apply({}, o, {
10802             width: w,
10803             height: h
10804         }));
10805         return this;
10806     },
10807
10808    /**
10809     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10810     * Any of these properties not specified in the config object will not be changed.  This effect 
10811     * requires that at least one new dimension, position or opacity setting must be passed in on
10812     * the config object in order for the function to have any effect.
10813     * Usage:
10814 <pre><code>
10815 // slide the element horizontally to x position 200 while changing the height and opacity
10816 el.shift({ x: 200, height: 50, opacity: .8 });
10817
10818 // common config options shown with default values.
10819 el.shift({
10820     width: [element's width],
10821     height: [element's height],
10822     x: [element's x position],
10823     y: [element's y position],
10824     opacity: [element's opacity],
10825     easing: 'easeOut',
10826     duration: .35
10827 });
10828 </code></pre>
10829     * @param {Object} options  Object literal with any of the Fx config options
10830     * @return {Roo.Element} The Element
10831     */
10832     shift : function(o){
10833         var el = this.getFxEl();
10834         o = o || {};
10835         el.queueFx(o, function(){
10836             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10837             if(w !== undefined){
10838                 a.width = {to: this.adjustWidth(w)};
10839             }
10840             if(h !== undefined){
10841                 a.height = {to: this.adjustHeight(h)};
10842             }
10843             if(x !== undefined || y !== undefined){
10844                 a.points = {to: [
10845                     x !== undefined ? x : this.getX(),
10846                     y !== undefined ? y : this.getY()
10847                 ]};
10848             }
10849             if(op !== undefined){
10850                 a.opacity = {to: op};
10851             }
10852             if(o.xy !== undefined){
10853                 a.points = {to: o.xy};
10854             }
10855             arguments.callee.anim = this.fxanim(a,
10856                 o, 'motion', .35, "easeOut", function(){
10857                 el.afterFx(o);
10858             });
10859         });
10860         return this;
10861     },
10862
10863         /**
10864          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10865          * ending point of the effect.
10866          * Usage:
10867          *<pre><code>
10868 // default: slide the element downward while fading out
10869 el.ghost();
10870
10871 // custom: slide the element out to the right with a 2-second duration
10872 el.ghost('r', { duration: 2 });
10873
10874 // common config options shown with default values
10875 el.ghost('b', {
10876     easing: 'easeOut',
10877     duration: .5
10878     remove: false,
10879     useDisplay: false
10880 });
10881 </code></pre>
10882          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10883          * @param {Object} options (optional) Object literal with any of the Fx config options
10884          * @return {Roo.Element} The Element
10885          */
10886     ghost : function(anchor, o){
10887         var el = this.getFxEl();
10888         o = o || {};
10889
10890         el.queueFx(o, function(){
10891             anchor = anchor || "b";
10892
10893             // restore values after effect
10894             var r = this.getFxRestore();
10895             var w = this.getWidth(),
10896                 h = this.getHeight();
10897
10898             var st = this.dom.style;
10899
10900             var after = function(){
10901                 if(o.useDisplay){
10902                     el.setDisplayed(false);
10903                 }else{
10904                     el.hide();
10905                 }
10906
10907                 el.clearOpacity();
10908                 el.setPositioning(r.pos);
10909                 st.width = r.width;
10910                 st.height = r.height;
10911
10912                 el.afterFx(o);
10913             };
10914
10915             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10916             switch(anchor.toLowerCase()){
10917                 case "t":
10918                     pt.by = [0, -h];
10919                 break;
10920                 case "l":
10921                     pt.by = [-w, 0];
10922                 break;
10923                 case "r":
10924                     pt.by = [w, 0];
10925                 break;
10926                 case "b":
10927                     pt.by = [0, h];
10928                 break;
10929                 case "tl":
10930                     pt.by = [-w, -h];
10931                 break;
10932                 case "bl":
10933                     pt.by = [-w, h];
10934                 break;
10935                 case "br":
10936                     pt.by = [w, h];
10937                 break;
10938                 case "tr":
10939                     pt.by = [w, -h];
10940                 break;
10941             }
10942
10943             arguments.callee.anim = this.fxanim(a,
10944                 o,
10945                 'motion',
10946                 .5,
10947                 "easeOut", after);
10948         });
10949         return this;
10950     },
10951
10952         /**
10953          * Ensures that all effects queued after syncFx is called on the element are
10954          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10955          * @return {Roo.Element} The Element
10956          */
10957     syncFx : function(){
10958         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10959             block : false,
10960             concurrent : true,
10961             stopFx : false
10962         });
10963         return this;
10964     },
10965
10966         /**
10967          * Ensures that all effects queued after sequenceFx is called on the element are
10968          * run in sequence.  This is the opposite of {@link #syncFx}.
10969          * @return {Roo.Element} The Element
10970          */
10971     sequenceFx : function(){
10972         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10973             block : false,
10974             concurrent : false,
10975             stopFx : false
10976         });
10977         return this;
10978     },
10979
10980         /* @private */
10981     nextFx : function(){
10982         var ef = this.fxQueue[0];
10983         if(ef){
10984             ef.call(this);
10985         }
10986     },
10987
10988         /**
10989          * Returns true if the element has any effects actively running or queued, else returns false.
10990          * @return {Boolean} True if element has active effects, else false
10991          */
10992     hasActiveFx : function(){
10993         return this.fxQueue && this.fxQueue[0];
10994     },
10995
10996         /**
10997          * Stops any running effects and clears the element's internal effects queue if it contains
10998          * any additional effects that haven't started yet.
10999          * @return {Roo.Element} The Element
11000          */
11001     stopFx : function(){
11002         if(this.hasActiveFx()){
11003             var cur = this.fxQueue[0];
11004             if(cur && cur.anim && cur.anim.isAnimated()){
11005                 this.fxQueue = [cur]; // clear out others
11006                 cur.anim.stop(true);
11007             }
11008         }
11009         return this;
11010     },
11011
11012         /* @private */
11013     beforeFx : function(o){
11014         if(this.hasActiveFx() && !o.concurrent){
11015            if(o.stopFx){
11016                this.stopFx();
11017                return true;
11018            }
11019            return false;
11020         }
11021         return true;
11022     },
11023
11024         /**
11025          * Returns true if the element is currently blocking so that no other effect can be queued
11026          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11027          * used to ensure that an effect initiated by a user action runs to completion prior to the
11028          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11029          * @return {Boolean} True if blocking, else false
11030          */
11031     hasFxBlock : function(){
11032         var q = this.fxQueue;
11033         return q && q[0] && q[0].block;
11034     },
11035
11036         /* @private */
11037     queueFx : function(o, fn){
11038         if(!this.fxQueue){
11039             this.fxQueue = [];
11040         }
11041         if(!this.hasFxBlock()){
11042             Roo.applyIf(o, this.fxDefaults);
11043             if(!o.concurrent){
11044                 var run = this.beforeFx(o);
11045                 fn.block = o.block;
11046                 this.fxQueue.push(fn);
11047                 if(run){
11048                     this.nextFx();
11049                 }
11050             }else{
11051                 fn.call(this);
11052             }
11053         }
11054         return this;
11055     },
11056
11057         /* @private */
11058     fxWrap : function(pos, o, vis){
11059         var wrap;
11060         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11061             var wrapXY;
11062             if(o.fixPosition){
11063                 wrapXY = this.getXY();
11064             }
11065             var div = document.createElement("div");
11066             div.style.visibility = vis;
11067             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11068             wrap.setPositioning(pos);
11069             if(wrap.getStyle("position") == "static"){
11070                 wrap.position("relative");
11071             }
11072             this.clearPositioning('auto');
11073             wrap.clip();
11074             wrap.dom.appendChild(this.dom);
11075             if(wrapXY){
11076                 wrap.setXY(wrapXY);
11077             }
11078         }
11079         return wrap;
11080     },
11081
11082         /* @private */
11083     fxUnwrap : function(wrap, pos, o){
11084         this.clearPositioning();
11085         this.setPositioning(pos);
11086         if(!o.wrap){
11087             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11088             wrap.remove();
11089         }
11090     },
11091
11092         /* @private */
11093     getFxRestore : function(){
11094         var st = this.dom.style;
11095         return {pos: this.getPositioning(), width: st.width, height : st.height};
11096     },
11097
11098         /* @private */
11099     afterFx : function(o){
11100         if(o.afterStyle){
11101             this.applyStyles(o.afterStyle);
11102         }
11103         if(o.afterCls){
11104             this.addClass(o.afterCls);
11105         }
11106         if(o.remove === true){
11107             this.remove();
11108         }
11109         Roo.callback(o.callback, o.scope, [this]);
11110         if(!o.concurrent){
11111             this.fxQueue.shift();
11112             this.nextFx();
11113         }
11114     },
11115
11116         /* @private */
11117     getFxEl : function(){ // support for composite element fx
11118         return Roo.get(this.dom);
11119     },
11120
11121         /* @private */
11122     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11123         animType = animType || 'run';
11124         opt = opt || {};
11125         var anim = Roo.lib.Anim[animType](
11126             this.dom, args,
11127             (opt.duration || defaultDur) || .35,
11128             (opt.easing || defaultEase) || 'easeOut',
11129             function(){
11130                 Roo.callback(cb, this);
11131             },
11132             this
11133         );
11134         opt.anim = anim;
11135         return anim;
11136     }
11137 };
11138
11139 // backwords compat
11140 Roo.Fx.resize = Roo.Fx.scale;
11141
11142 //When included, Roo.Fx is automatically applied to Element so that all basic
11143 //effects are available directly via the Element API
11144 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11145  * Based on:
11146  * Ext JS Library 1.1.1
11147  * Copyright(c) 2006-2007, Ext JS, LLC.
11148  *
11149  * Originally Released Under LGPL - original licence link has changed is not relivant.
11150  *
11151  * Fork - LGPL
11152  * <script type="text/javascript">
11153  */
11154
11155
11156 /**
11157  * @class Roo.CompositeElement
11158  * Standard composite class. Creates a Roo.Element for every element in the collection.
11159  * <br><br>
11160  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11161  * actions will be performed on all the elements in this collection.</b>
11162  * <br><br>
11163  * All methods return <i>this</i> and can be chained.
11164  <pre><code>
11165  var els = Roo.select("#some-el div.some-class", true);
11166  // or select directly from an existing element
11167  var el = Roo.get('some-el');
11168  el.select('div.some-class', true);
11169
11170  els.setWidth(100); // all elements become 100 width
11171  els.hide(true); // all elements fade out and hide
11172  // or
11173  els.setWidth(100).hide(true);
11174  </code></pre>
11175  */
11176 Roo.CompositeElement = function(els){
11177     this.elements = [];
11178     this.addElements(els);
11179 };
11180 Roo.CompositeElement.prototype = {
11181     isComposite: true,
11182     addElements : function(els){
11183         if(!els) {
11184             return this;
11185         }
11186         if(typeof els == "string"){
11187             els = Roo.Element.selectorFunction(els);
11188         }
11189         var yels = this.elements;
11190         var index = yels.length-1;
11191         for(var i = 0, len = els.length; i < len; i++) {
11192                 yels[++index] = Roo.get(els[i]);
11193         }
11194         return this;
11195     },
11196
11197     /**
11198     * Clears this composite and adds the elements returned by the passed selector.
11199     * @param {String/Array} els A string CSS selector, an array of elements or an element
11200     * @return {CompositeElement} this
11201     */
11202     fill : function(els){
11203         this.elements = [];
11204         this.add(els);
11205         return this;
11206     },
11207
11208     /**
11209     * Filters this composite to only elements that match the passed selector.
11210     * @param {String} selector A string CSS selector
11211     * @param {Boolean} inverse return inverse filter (not matches)
11212     * @return {CompositeElement} this
11213     */
11214     filter : function(selector, inverse){
11215         var els = [];
11216         inverse = inverse || false;
11217         this.each(function(el){
11218             var match = inverse ? !el.is(selector) : el.is(selector);
11219             if(match){
11220                 els[els.length] = el.dom;
11221             }
11222         });
11223         this.fill(els);
11224         return this;
11225     },
11226
11227     invoke : function(fn, args){
11228         var els = this.elements;
11229         for(var i = 0, len = els.length; i < len; i++) {
11230                 Roo.Element.prototype[fn].apply(els[i], args);
11231         }
11232         return this;
11233     },
11234     /**
11235     * Adds elements to this composite.
11236     * @param {String/Array} els A string CSS selector, an array of elements or an element
11237     * @return {CompositeElement} this
11238     */
11239     add : function(els){
11240         if(typeof els == "string"){
11241             this.addElements(Roo.Element.selectorFunction(els));
11242         }else if(els.length !== undefined){
11243             this.addElements(els);
11244         }else{
11245             this.addElements([els]);
11246         }
11247         return this;
11248     },
11249     /**
11250     * Calls the passed function passing (el, this, index) for each element in this composite.
11251     * @param {Function} fn The function to call
11252     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11253     * @return {CompositeElement} this
11254     */
11255     each : function(fn, scope){
11256         var els = this.elements;
11257         for(var i = 0, len = els.length; i < len; i++){
11258             if(fn.call(scope || els[i], els[i], this, i) === false) {
11259                 break;
11260             }
11261         }
11262         return this;
11263     },
11264
11265     /**
11266      * Returns the Element object at the specified index
11267      * @param {Number} index
11268      * @return {Roo.Element}
11269      */
11270     item : function(index){
11271         return this.elements[index] || null;
11272     },
11273
11274     /**
11275      * Returns the first Element
11276      * @return {Roo.Element}
11277      */
11278     first : function(){
11279         return this.item(0);
11280     },
11281
11282     /**
11283      * Returns the last Element
11284      * @return {Roo.Element}
11285      */
11286     last : function(){
11287         return this.item(this.elements.length-1);
11288     },
11289
11290     /**
11291      * Returns the number of elements in this composite
11292      * @return Number
11293      */
11294     getCount : function(){
11295         return this.elements.length;
11296     },
11297
11298     /**
11299      * Returns true if this composite contains the passed element
11300      * @return Boolean
11301      */
11302     contains : function(el){
11303         return this.indexOf(el) !== -1;
11304     },
11305
11306     /**
11307      * Returns true if this composite contains the passed element
11308      * @return Boolean
11309      */
11310     indexOf : function(el){
11311         return this.elements.indexOf(Roo.get(el));
11312     },
11313
11314
11315     /**
11316     * Removes the specified element(s).
11317     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11318     * or an array of any of those.
11319     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11320     * @return {CompositeElement} this
11321     */
11322     removeElement : function(el, removeDom){
11323         if(el instanceof Array){
11324             for(var i = 0, len = el.length; i < len; i++){
11325                 this.removeElement(el[i]);
11326             }
11327             return this;
11328         }
11329         var index = typeof el == 'number' ? el : this.indexOf(el);
11330         if(index !== -1){
11331             if(removeDom){
11332                 var d = this.elements[index];
11333                 if(d.dom){
11334                     d.remove();
11335                 }else{
11336                     d.parentNode.removeChild(d);
11337                 }
11338             }
11339             this.elements.splice(index, 1);
11340         }
11341         return this;
11342     },
11343
11344     /**
11345     * Replaces the specified element with the passed element.
11346     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11347     * to replace.
11348     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11349     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11350     * @return {CompositeElement} this
11351     */
11352     replaceElement : function(el, replacement, domReplace){
11353         var index = typeof el == 'number' ? el : this.indexOf(el);
11354         if(index !== -1){
11355             if(domReplace){
11356                 this.elements[index].replaceWith(replacement);
11357             }else{
11358                 this.elements.splice(index, 1, Roo.get(replacement))
11359             }
11360         }
11361         return this;
11362     },
11363
11364     /**
11365      * Removes all elements.
11366      */
11367     clear : function(){
11368         this.elements = [];
11369     }
11370 };
11371 (function(){
11372     Roo.CompositeElement.createCall = function(proto, fnName){
11373         if(!proto[fnName]){
11374             proto[fnName] = function(){
11375                 return this.invoke(fnName, arguments);
11376             };
11377         }
11378     };
11379     for(var fnName in Roo.Element.prototype){
11380         if(typeof Roo.Element.prototype[fnName] == "function"){
11381             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11382         }
11383     };
11384 })();
11385 /*
11386  * Based on:
11387  * Ext JS Library 1.1.1
11388  * Copyright(c) 2006-2007, Ext JS, LLC.
11389  *
11390  * Originally Released Under LGPL - original licence link has changed is not relivant.
11391  *
11392  * Fork - LGPL
11393  * <script type="text/javascript">
11394  */
11395
11396 /**
11397  * @class Roo.CompositeElementLite
11398  * @extends Roo.CompositeElement
11399  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11400  <pre><code>
11401  var els = Roo.select("#some-el div.some-class");
11402  // or select directly from an existing element
11403  var el = Roo.get('some-el');
11404  el.select('div.some-class');
11405
11406  els.setWidth(100); // all elements become 100 width
11407  els.hide(true); // all elements fade out and hide
11408  // or
11409  els.setWidth(100).hide(true);
11410  </code></pre><br><br>
11411  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11412  * actions will be performed on all the elements in this collection.</b>
11413  */
11414 Roo.CompositeElementLite = function(els){
11415     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11416     this.el = new Roo.Element.Flyweight();
11417 };
11418 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11419     addElements : function(els){
11420         if(els){
11421             if(els instanceof Array){
11422                 this.elements = this.elements.concat(els);
11423             }else{
11424                 var yels = this.elements;
11425                 var index = yels.length-1;
11426                 for(var i = 0, len = els.length; i < len; i++) {
11427                     yels[++index] = els[i];
11428                 }
11429             }
11430         }
11431         return this;
11432     },
11433     invoke : function(fn, args){
11434         var els = this.elements;
11435         var el = this.el;
11436         for(var i = 0, len = els.length; i < len; i++) {
11437             el.dom = els[i];
11438                 Roo.Element.prototype[fn].apply(el, args);
11439         }
11440         return this;
11441     },
11442     /**
11443      * Returns a flyweight Element of the dom element object at the specified index
11444      * @param {Number} index
11445      * @return {Roo.Element}
11446      */
11447     item : function(index){
11448         if(!this.elements[index]){
11449             return null;
11450         }
11451         this.el.dom = this.elements[index];
11452         return this.el;
11453     },
11454
11455     // fixes scope with flyweight
11456     addListener : function(eventName, handler, scope, opt){
11457         var els = this.elements;
11458         for(var i = 0, len = els.length; i < len; i++) {
11459             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11460         }
11461         return this;
11462     },
11463
11464     /**
11465     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11466     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11467     * a reference to the dom node, use el.dom.</b>
11468     * @param {Function} fn The function to call
11469     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11470     * @return {CompositeElement} this
11471     */
11472     each : function(fn, scope){
11473         var els = this.elements;
11474         var el = this.el;
11475         for(var i = 0, len = els.length; i < len; i++){
11476             el.dom = els[i];
11477                 if(fn.call(scope || el, el, this, i) === false){
11478                 break;
11479             }
11480         }
11481         return this;
11482     },
11483
11484     indexOf : function(el){
11485         return this.elements.indexOf(Roo.getDom(el));
11486     },
11487
11488     replaceElement : function(el, replacement, domReplace){
11489         var index = typeof el == 'number' ? el : this.indexOf(el);
11490         if(index !== -1){
11491             replacement = Roo.getDom(replacement);
11492             if(domReplace){
11493                 var d = this.elements[index];
11494                 d.parentNode.insertBefore(replacement, d);
11495                 d.parentNode.removeChild(d);
11496             }
11497             this.elements.splice(index, 1, replacement);
11498         }
11499         return this;
11500     }
11501 });
11502 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11503
11504 /*
11505  * Based on:
11506  * Ext JS Library 1.1.1
11507  * Copyright(c) 2006-2007, Ext JS, LLC.
11508  *
11509  * Originally Released Under LGPL - original licence link has changed is not relivant.
11510  *
11511  * Fork - LGPL
11512  * <script type="text/javascript">
11513  */
11514
11515  
11516
11517 /**
11518  * @class Roo.data.Connection
11519  * @extends Roo.util.Observable
11520  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11521  * either to a configured URL, or to a URL specified at request time. 
11522  * 
11523  * Requests made by this class are asynchronous, and will return immediately. No data from
11524  * the server will be available to the statement immediately following the {@link #request} call.
11525  * To process returned data, use a callback in the request options object, or an event listener.
11526  * 
11527  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11528  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11529  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11530  * property and, if present, the IFRAME's XML document as the responseXML property.
11531  * 
11532  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11533  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11534  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11535  * standard DOM methods.
11536  * @constructor
11537  * @param {Object} config a configuration object.
11538  */
11539 Roo.data.Connection = function(config){
11540     Roo.apply(this, config);
11541     this.addEvents({
11542         /**
11543          * @event beforerequest
11544          * Fires before a network request is made to retrieve a data object.
11545          * @param {Connection} conn This Connection object.
11546          * @param {Object} options The options config object passed to the {@link #request} method.
11547          */
11548         "beforerequest" : true,
11549         /**
11550          * @event requestcomplete
11551          * Fires if the request was successfully completed.
11552          * @param {Connection} conn This Connection object.
11553          * @param {Object} response The XHR object containing the response data.
11554          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11555          * @param {Object} options The options config object passed to the {@link #request} method.
11556          */
11557         "requestcomplete" : true,
11558         /**
11559          * @event requestexception
11560          * Fires if an error HTTP status was returned from the server.
11561          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11562          * @param {Connection} conn This Connection object.
11563          * @param {Object} response The XHR object containing the response data.
11564          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11565          * @param {Object} options The options config object passed to the {@link #request} method.
11566          */
11567         "requestexception" : true
11568     });
11569     Roo.data.Connection.superclass.constructor.call(this);
11570 };
11571
11572 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11573     /**
11574      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11575      */
11576     /**
11577      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11578      * extra parameters to each request made by this object. (defaults to undefined)
11579      */
11580     /**
11581      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11582      *  to each request made by this object. (defaults to undefined)
11583      */
11584     /**
11585      * @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)
11586      */
11587     /**
11588      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11589      */
11590     timeout : 30000,
11591     /**
11592      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11593      * @type Boolean
11594      */
11595     autoAbort:false,
11596
11597     /**
11598      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11599      * @type Boolean
11600      */
11601     disableCaching: true,
11602
11603     /**
11604      * Sends an HTTP request to a remote server.
11605      * @param {Object} options An object which may contain the following properties:<ul>
11606      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11607      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11608      * request, a url encoded string or a function to call to get either.</li>
11609      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11610      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11611      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11612      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11613      * <li>options {Object} The parameter to the request call.</li>
11614      * <li>success {Boolean} True if the request succeeded.</li>
11615      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11616      * </ul></li>
11617      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11618      * The callback is passed the following parameters:<ul>
11619      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11620      * <li>options {Object} The parameter to the request call.</li>
11621      * </ul></li>
11622      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11623      * The callback is passed the following parameters:<ul>
11624      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11625      * <li>options {Object} The parameter to the request call.</li>
11626      * </ul></li>
11627      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11628      * for the callback function. Defaults to the browser window.</li>
11629      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11630      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11631      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11632      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11633      * params for the post data. Any params will be appended to the URL.</li>
11634      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11635      * </ul>
11636      * @return {Number} transactionId
11637      */
11638     request : function(o){
11639         if(this.fireEvent("beforerequest", this, o) !== false){
11640             var p = o.params;
11641
11642             if(typeof p == "function"){
11643                 p = p.call(o.scope||window, o);
11644             }
11645             if(typeof p == "object"){
11646                 p = Roo.urlEncode(o.params);
11647             }
11648             if(this.extraParams){
11649                 var extras = Roo.urlEncode(this.extraParams);
11650                 p = p ? (p + '&' + extras) : extras;
11651             }
11652
11653             var url = o.url || this.url;
11654             if(typeof url == 'function'){
11655                 url = url.call(o.scope||window, o);
11656             }
11657
11658             if(o.form){
11659                 var form = Roo.getDom(o.form);
11660                 url = url || form.action;
11661
11662                 var enctype = form.getAttribute("enctype");
11663                 
11664                 if (o.formData) {
11665                     return this.doFormDataUpload(o, url);
11666                 }
11667                 
11668                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11669                     return this.doFormUpload(o, p, url);
11670                 }
11671                 var f = Roo.lib.Ajax.serializeForm(form);
11672                 p = p ? (p + '&' + f) : f;
11673             }
11674             
11675             if (!o.form && o.formData) {
11676                 o.formData = o.formData === true ? new FormData() : o.formData;
11677                 for (var k in o.params) {
11678                     o.formData.append(k,o.params[k]);
11679                 }
11680                     
11681                 return this.doFormDataUpload(o, url);
11682             }
11683             
11684
11685             var hs = o.headers;
11686             if(this.defaultHeaders){
11687                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11688                 if(!o.headers){
11689                     o.headers = hs;
11690                 }
11691             }
11692
11693             var cb = {
11694                 success: this.handleResponse,
11695                 failure: this.handleFailure,
11696                 scope: this,
11697                 argument: {options: o},
11698                 timeout : o.timeout || this.timeout
11699             };
11700
11701             var method = o.method||this.method||(p ? "POST" : "GET");
11702
11703             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11704                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11705             }
11706
11707             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11708                 if(o.autoAbort){
11709                     this.abort();
11710                 }
11711             }else if(this.autoAbort !== false){
11712                 this.abort();
11713             }
11714
11715             if((method == 'GET' && p) || o.xmlData){
11716                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11717                 p = '';
11718             }
11719             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11720             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11721             Roo.lib.Ajax.useDefaultHeader == true;
11722             return this.transId;
11723         }else{
11724             Roo.callback(o.callback, o.scope, [o, null, null]);
11725             return null;
11726         }
11727     },
11728
11729     /**
11730      * Determine whether this object has a request outstanding.
11731      * @param {Number} transactionId (Optional) defaults to the last transaction
11732      * @return {Boolean} True if there is an outstanding request.
11733      */
11734     isLoading : function(transId){
11735         if(transId){
11736             return Roo.lib.Ajax.isCallInProgress(transId);
11737         }else{
11738             return this.transId ? true : false;
11739         }
11740     },
11741
11742     /**
11743      * Aborts any outstanding request.
11744      * @param {Number} transactionId (Optional) defaults to the last transaction
11745      */
11746     abort : function(transId){
11747         if(transId || this.isLoading()){
11748             Roo.lib.Ajax.abort(transId || this.transId);
11749         }
11750     },
11751
11752     // private
11753     handleResponse : function(response){
11754         this.transId = false;
11755         var options = response.argument.options;
11756         response.argument = options ? options.argument : null;
11757         this.fireEvent("requestcomplete", this, response, options);
11758         Roo.callback(options.success, options.scope, [response, options]);
11759         Roo.callback(options.callback, options.scope, [options, true, response]);
11760     },
11761
11762     // private
11763     handleFailure : function(response, e){
11764         this.transId = false;
11765         var options = response.argument.options;
11766         response.argument = options ? options.argument : null;
11767         this.fireEvent("requestexception", this, response, options, e);
11768         Roo.callback(options.failure, options.scope, [response, options]);
11769         Roo.callback(options.callback, options.scope, [options, false, response]);
11770     },
11771
11772     // private
11773     doFormUpload : function(o, ps, url){
11774         var id = Roo.id();
11775         var frame = document.createElement('iframe');
11776         frame.id = id;
11777         frame.name = id;
11778         frame.className = 'x-hidden';
11779         if(Roo.isIE){
11780             frame.src = Roo.SSL_SECURE_URL;
11781         }
11782         document.body.appendChild(frame);
11783
11784         if(Roo.isIE){
11785            document.frames[id].name = id;
11786         }
11787
11788         var form = Roo.getDom(o.form);
11789         form.target = id;
11790         form.method = 'POST';
11791         form.enctype = form.encoding = 'multipart/form-data';
11792         if(url){
11793             form.action = url;
11794         }
11795
11796         var hiddens, hd;
11797         if(ps){ // add dynamic params
11798             hiddens = [];
11799             ps = Roo.urlDecode(ps, false);
11800             for(var k in ps){
11801                 if(ps.hasOwnProperty(k)){
11802                     hd = document.createElement('input');
11803                     hd.type = 'hidden';
11804                     hd.name = k;
11805                     hd.value = ps[k];
11806                     form.appendChild(hd);
11807                     hiddens.push(hd);
11808                 }
11809             }
11810         }
11811
11812         function cb(){
11813             var r = {  // bogus response object
11814                 responseText : '',
11815                 responseXML : null
11816             };
11817
11818             r.argument = o ? o.argument : null;
11819
11820             try { //
11821                 var doc;
11822                 if(Roo.isIE){
11823                     doc = frame.contentWindow.document;
11824                 }else {
11825                     doc = (frame.contentDocument || window.frames[id].document);
11826                 }
11827                 if(doc && doc.body){
11828                     r.responseText = doc.body.innerHTML;
11829                 }
11830                 if(doc && doc.XMLDocument){
11831                     r.responseXML = doc.XMLDocument;
11832                 }else {
11833                     r.responseXML = doc;
11834                 }
11835             }
11836             catch(e) {
11837                 // ignore
11838             }
11839
11840             Roo.EventManager.removeListener(frame, 'load', cb, this);
11841
11842             this.fireEvent("requestcomplete", this, r, o);
11843             Roo.callback(o.success, o.scope, [r, o]);
11844             Roo.callback(o.callback, o.scope, [o, true, r]);
11845
11846             setTimeout(function(){document.body.removeChild(frame);}, 100);
11847         }
11848
11849         Roo.EventManager.on(frame, 'load', cb, this);
11850         form.submit();
11851
11852         if(hiddens){ // remove dynamic params
11853             for(var i = 0, len = hiddens.length; i < len; i++){
11854                 form.removeChild(hiddens[i]);
11855             }
11856         }
11857     },
11858     // this is a 'formdata version???'
11859     
11860     
11861     doFormDataUpload : function(o,  url)
11862     {
11863         var formData;
11864         if (o.form) {
11865             var form =  Roo.getDom(o.form);
11866             form.enctype = form.encoding = 'multipart/form-data';
11867             formData = o.formData === true ? new FormData(form) : o.formData;
11868         } else {
11869             formData = o.formData === true ? new FormData() : o.formData;
11870         }
11871         
11872       
11873         var cb = {
11874             success: this.handleResponse,
11875             failure: this.handleFailure,
11876             scope: this,
11877             argument: {options: o},
11878             timeout : o.timeout || this.timeout
11879         };
11880  
11881         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11882             if(o.autoAbort){
11883                 this.abort();
11884             }
11885         }else if(this.autoAbort !== false){
11886             this.abort();
11887         }
11888
11889         //Roo.lib.Ajax.defaultPostHeader = null;
11890         Roo.lib.Ajax.useDefaultHeader = false;
11891         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11892         Roo.lib.Ajax.useDefaultHeader = true;
11893  
11894          
11895     }
11896     
11897 });
11898 /*
11899  * Based on:
11900  * Ext JS Library 1.1.1
11901  * Copyright(c) 2006-2007, Ext JS, LLC.
11902  *
11903  * Originally Released Under LGPL - original licence link has changed is not relivant.
11904  *
11905  * Fork - LGPL
11906  * <script type="text/javascript">
11907  */
11908  
11909 /**
11910  * Global Ajax request class.
11911  * 
11912  * @class Roo.Ajax
11913  * @extends Roo.data.Connection
11914  * @static
11915  * 
11916  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11917  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11918  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11919  * @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)
11920  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11921  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11922  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11923  */
11924 Roo.Ajax = new Roo.data.Connection({
11925     // fix up the docs
11926     /**
11927      * @scope Roo.Ajax
11928      * @type {Boolear} 
11929      */
11930     autoAbort : false,
11931
11932     /**
11933      * Serialize the passed form into a url encoded string
11934      * @scope Roo.Ajax
11935      * @param {String/HTMLElement} form
11936      * @return {String}
11937      */
11938     serializeForm : function(form){
11939         return Roo.lib.Ajax.serializeForm(form);
11940     }
11941 });/*
11942  * Based on:
11943  * Ext JS Library 1.1.1
11944  * Copyright(c) 2006-2007, Ext JS, LLC.
11945  *
11946  * Originally Released Under LGPL - original licence link has changed is not relivant.
11947  *
11948  * Fork - LGPL
11949  * <script type="text/javascript">
11950  */
11951
11952  
11953 /**
11954  * @class Roo.UpdateManager
11955  * @extends Roo.util.Observable
11956  * Provides AJAX-style update for Element object.<br><br>
11957  * Usage:<br>
11958  * <pre><code>
11959  * // Get it from a Roo.Element object
11960  * var el = Roo.get("foo");
11961  * var mgr = el.getUpdateManager();
11962  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11963  * ...
11964  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11965  * <br>
11966  * // or directly (returns the same UpdateManager instance)
11967  * var mgr = new Roo.UpdateManager("myElementId");
11968  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11969  * mgr.on("update", myFcnNeedsToKnow);
11970  * <br>
11971    // short handed call directly from the element object
11972    Roo.get("foo").load({
11973         url: "bar.php",
11974         scripts:true,
11975         params: "for=bar",
11976         text: "Loading Foo..."
11977    });
11978  * </code></pre>
11979  * @constructor
11980  * Create new UpdateManager directly.
11981  * @param {String/HTMLElement/Roo.Element} el The element to update
11982  * @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).
11983  */
11984 Roo.UpdateManager = function(el, forceNew){
11985     el = Roo.get(el);
11986     if(!forceNew && el.updateManager){
11987         return el.updateManager;
11988     }
11989     /**
11990      * The Element object
11991      * @type Roo.Element
11992      */
11993     this.el = el;
11994     /**
11995      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11996      * @type String
11997      */
11998     this.defaultUrl = null;
11999
12000     this.addEvents({
12001         /**
12002          * @event beforeupdate
12003          * Fired before an update is made, return false from your handler and the update is cancelled.
12004          * @param {Roo.Element} el
12005          * @param {String/Object/Function} url
12006          * @param {String/Object} params
12007          */
12008         "beforeupdate": true,
12009         /**
12010          * @event update
12011          * Fired after successful update is made.
12012          * @param {Roo.Element} el
12013          * @param {Object} oResponseObject The response Object
12014          */
12015         "update": true,
12016         /**
12017          * @event failure
12018          * Fired on update failure.
12019          * @param {Roo.Element} el
12020          * @param {Object} oResponseObject The response Object
12021          */
12022         "failure": true
12023     });
12024     var d = Roo.UpdateManager.defaults;
12025     /**
12026      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12027      * @type String
12028      */
12029     this.sslBlankUrl = d.sslBlankUrl;
12030     /**
12031      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12032      * @type Boolean
12033      */
12034     this.disableCaching = d.disableCaching;
12035     /**
12036      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12037      * @type String
12038      */
12039     this.indicatorText = d.indicatorText;
12040     /**
12041      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12042      * @type String
12043      */
12044     this.showLoadIndicator = d.showLoadIndicator;
12045     /**
12046      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12047      * @type Number
12048      */
12049     this.timeout = d.timeout;
12050
12051     /**
12052      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12053      * @type Boolean
12054      */
12055     this.loadScripts = d.loadScripts;
12056
12057     /**
12058      * Transaction object of current executing transaction
12059      */
12060     this.transaction = null;
12061
12062     /**
12063      * @private
12064      */
12065     this.autoRefreshProcId = null;
12066     /**
12067      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12068      * @type Function
12069      */
12070     this.refreshDelegate = this.refresh.createDelegate(this);
12071     /**
12072      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12073      * @type Function
12074      */
12075     this.updateDelegate = this.update.createDelegate(this);
12076     /**
12077      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12078      * @type Function
12079      */
12080     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12081     /**
12082      * @private
12083      */
12084     this.successDelegate = this.processSuccess.createDelegate(this);
12085     /**
12086      * @private
12087      */
12088     this.failureDelegate = this.processFailure.createDelegate(this);
12089
12090     if(!this.renderer){
12091      /**
12092       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12093       */
12094     this.renderer = new Roo.UpdateManager.BasicRenderer();
12095     }
12096     
12097     Roo.UpdateManager.superclass.constructor.call(this);
12098 };
12099
12100 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12101     /**
12102      * Get the Element this UpdateManager is bound to
12103      * @return {Roo.Element} The element
12104      */
12105     getEl : function(){
12106         return this.el;
12107     },
12108     /**
12109      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12110      * @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:
12111 <pre><code>
12112 um.update({<br/>
12113     url: "your-url.php",<br/>
12114     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12115     callback: yourFunction,<br/>
12116     scope: yourObject, //(optional scope)  <br/>
12117     discardUrl: false, <br/>
12118     nocache: false,<br/>
12119     text: "Loading...",<br/>
12120     timeout: 30,<br/>
12121     scripts: false<br/>
12122 });
12123 </code></pre>
12124      * The only required property is url. The optional properties nocache, text and scripts
12125      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12126      * @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}
12127      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12128      * @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.
12129      */
12130     update : function(url, params, callback, discardUrl){
12131         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12132             var method = this.method,
12133                 cfg;
12134             if(typeof url == "object"){ // must be config object
12135                 cfg = url;
12136                 url = cfg.url;
12137                 params = params || cfg.params;
12138                 callback = callback || cfg.callback;
12139                 discardUrl = discardUrl || cfg.discardUrl;
12140                 if(callback && cfg.scope){
12141                     callback = callback.createDelegate(cfg.scope);
12142                 }
12143                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12144                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12145                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12146                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12147                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12148             }
12149             this.showLoading();
12150             if(!discardUrl){
12151                 this.defaultUrl = url;
12152             }
12153             if(typeof url == "function"){
12154                 url = url.call(this);
12155             }
12156
12157             method = method || (params ? "POST" : "GET");
12158             if(method == "GET"){
12159                 url = this.prepareUrl(url);
12160             }
12161
12162             var o = Roo.apply(cfg ||{}, {
12163                 url : url,
12164                 params: params,
12165                 success: this.successDelegate,
12166                 failure: this.failureDelegate,
12167                 callback: undefined,
12168                 timeout: (this.timeout*1000),
12169                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12170             });
12171             Roo.log("updated manager called with timeout of " + o.timeout);
12172             this.transaction = Roo.Ajax.request(o);
12173         }
12174     },
12175
12176     /**
12177      * 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.
12178      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12179      * @param {String/HTMLElement} form The form Id or form element
12180      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12181      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12182      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12183      */
12184     formUpdate : function(form, url, reset, callback){
12185         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12186             if(typeof url == "function"){
12187                 url = url.call(this);
12188             }
12189             form = Roo.getDom(form);
12190             this.transaction = Roo.Ajax.request({
12191                 form: form,
12192                 url:url,
12193                 success: this.successDelegate,
12194                 failure: this.failureDelegate,
12195                 timeout: (this.timeout*1000),
12196                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12197             });
12198             this.showLoading.defer(1, this);
12199         }
12200     },
12201
12202     /**
12203      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12204      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12205      */
12206     refresh : function(callback){
12207         if(this.defaultUrl == null){
12208             return;
12209         }
12210         this.update(this.defaultUrl, null, callback, true);
12211     },
12212
12213     /**
12214      * Set this element to auto refresh.
12215      * @param {Number} interval How often to update (in seconds).
12216      * @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)
12217      * @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}
12218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12219      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12220      */
12221     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12222         if(refreshNow){
12223             this.update(url || this.defaultUrl, params, callback, true);
12224         }
12225         if(this.autoRefreshProcId){
12226             clearInterval(this.autoRefreshProcId);
12227         }
12228         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12229     },
12230
12231     /**
12232      * Stop auto refresh on this element.
12233      */
12234      stopAutoRefresh : function(){
12235         if(this.autoRefreshProcId){
12236             clearInterval(this.autoRefreshProcId);
12237             delete this.autoRefreshProcId;
12238         }
12239     },
12240
12241     isAutoRefreshing : function(){
12242        return this.autoRefreshProcId ? true : false;
12243     },
12244     /**
12245      * Called to update the element to "Loading" state. Override to perform custom action.
12246      */
12247     showLoading : function(){
12248         if(this.showLoadIndicator){
12249             this.el.update(this.indicatorText);
12250         }
12251     },
12252
12253     /**
12254      * Adds unique parameter to query string if disableCaching = true
12255      * @private
12256      */
12257     prepareUrl : function(url){
12258         if(this.disableCaching){
12259             var append = "_dc=" + (new Date().getTime());
12260             if(url.indexOf("?") !== -1){
12261                 url += "&" + append;
12262             }else{
12263                 url += "?" + append;
12264             }
12265         }
12266         return url;
12267     },
12268
12269     /**
12270      * @private
12271      */
12272     processSuccess : function(response){
12273         this.transaction = null;
12274         if(response.argument.form && response.argument.reset){
12275             try{ // put in try/catch since some older FF releases had problems with this
12276                 response.argument.form.reset();
12277             }catch(e){}
12278         }
12279         if(this.loadScripts){
12280             this.renderer.render(this.el, response, this,
12281                 this.updateComplete.createDelegate(this, [response]));
12282         }else{
12283             this.renderer.render(this.el, response, this);
12284             this.updateComplete(response);
12285         }
12286     },
12287
12288     updateComplete : function(response){
12289         this.fireEvent("update", this.el, response);
12290         if(typeof response.argument.callback == "function"){
12291             response.argument.callback(this.el, true, response);
12292         }
12293     },
12294
12295     /**
12296      * @private
12297      */
12298     processFailure : function(response){
12299         this.transaction = null;
12300         this.fireEvent("failure", this.el, response);
12301         if(typeof response.argument.callback == "function"){
12302             response.argument.callback(this.el, false, response);
12303         }
12304     },
12305
12306     /**
12307      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12308      * @param {Object} renderer The object implementing the render() method
12309      */
12310     setRenderer : function(renderer){
12311         this.renderer = renderer;
12312     },
12313
12314     getRenderer : function(){
12315        return this.renderer;
12316     },
12317
12318     /**
12319      * Set the defaultUrl used for updates
12320      * @param {String/Function} defaultUrl The url or a function to call to get the url
12321      */
12322     setDefaultUrl : function(defaultUrl){
12323         this.defaultUrl = defaultUrl;
12324     },
12325
12326     /**
12327      * Aborts the executing transaction
12328      */
12329     abort : function(){
12330         if(this.transaction){
12331             Roo.Ajax.abort(this.transaction);
12332         }
12333     },
12334
12335     /**
12336      * Returns true if an update is in progress
12337      * @return {Boolean}
12338      */
12339     isUpdating : function(){
12340         if(this.transaction){
12341             return Roo.Ajax.isLoading(this.transaction);
12342         }
12343         return false;
12344     }
12345 });
12346
12347 /**
12348  * @class Roo.UpdateManager.defaults
12349  * @static (not really - but it helps the doc tool)
12350  * The defaults collection enables customizing the default properties of UpdateManager
12351  */
12352    Roo.UpdateManager.defaults = {
12353        /**
12354          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12355          * @type Number
12356          */
12357          timeout : 30,
12358
12359          /**
12360          * True to process scripts by default (Defaults to false).
12361          * @type Boolean
12362          */
12363         loadScripts : false,
12364
12365         /**
12366         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12367         * @type String
12368         */
12369         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12370         /**
12371          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12372          * @type Boolean
12373          */
12374         disableCaching : false,
12375         /**
12376          * Whether to show indicatorText when loading (Defaults to true).
12377          * @type Boolean
12378          */
12379         showLoadIndicator : true,
12380         /**
12381          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12382          * @type String
12383          */
12384         indicatorText : '<div class="loading-indicator">Loading...</div>'
12385    };
12386
12387 /**
12388  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12389  *Usage:
12390  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12391  * @param {String/HTMLElement/Roo.Element} el The element to update
12392  * @param {String} url The url
12393  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12394  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12395  * @static
12396  * @deprecated
12397  * @member Roo.UpdateManager
12398  */
12399 Roo.UpdateManager.updateElement = function(el, url, params, options){
12400     var um = Roo.get(el, true).getUpdateManager();
12401     Roo.apply(um, options);
12402     um.update(url, params, options ? options.callback : null);
12403 };
12404 // alias for backwards compat
12405 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12406 /**
12407  * @class Roo.UpdateManager.BasicRenderer
12408  * Default Content renderer. Updates the elements innerHTML with the responseText.
12409  */
12410 Roo.UpdateManager.BasicRenderer = function(){};
12411
12412 Roo.UpdateManager.BasicRenderer.prototype = {
12413     /**
12414      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12415      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12416      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12417      * @param {Roo.Element} el The element being rendered
12418      * @param {Object} response The YUI Connect response object
12419      * @param {UpdateManager} updateManager The calling update manager
12420      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12421      */
12422      render : function(el, response, updateManager, callback){
12423         el.update(response.responseText, updateManager.loadScripts, callback);
12424     }
12425 };
12426 /*
12427  * Based on:
12428  * Roo JS
12429  * (c)) Alan Knowles
12430  * Licence : LGPL
12431  */
12432
12433
12434 /**
12435  * @class Roo.DomTemplate
12436  * @extends Roo.Template
12437  * An effort at a dom based template engine..
12438  *
12439  * Similar to XTemplate, except it uses dom parsing to create the template..
12440  *
12441  * Supported features:
12442  *
12443  *  Tags:
12444
12445 <pre><code>
12446       {a_variable} - output encoded.
12447       {a_variable.format:("Y-m-d")} - call a method on the variable
12448       {a_variable:raw} - unencoded output
12449       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12450       {a_variable:this.method_on_template(...)} - call a method on the template object.
12451  
12452 </code></pre>
12453  *  The tpl tag:
12454 <pre><code>
12455         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12456         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12457         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12458         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12459   
12460 </code></pre>
12461  *      
12462  */
12463 Roo.DomTemplate = function()
12464 {
12465      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12466      if (this.html) {
12467         this.compile();
12468      }
12469 };
12470
12471
12472 Roo.extend(Roo.DomTemplate, Roo.Template, {
12473     /**
12474      * id counter for sub templates.
12475      */
12476     id : 0,
12477     /**
12478      * flag to indicate if dom parser is inside a pre,
12479      * it will strip whitespace if not.
12480      */
12481     inPre : false,
12482     
12483     /**
12484      * The various sub templates
12485      */
12486     tpls : false,
12487     
12488     
12489     
12490     /**
12491      *
12492      * basic tag replacing syntax
12493      * WORD:WORD()
12494      *
12495      * // you can fake an object call by doing this
12496      *  x.t:(test,tesT) 
12497      * 
12498      */
12499     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12500     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12501     
12502     iterChild : function (node, method) {
12503         
12504         var oldPre = this.inPre;
12505         if (node.tagName == 'PRE') {
12506             this.inPre = true;
12507         }
12508         for( var i = 0; i < node.childNodes.length; i++) {
12509             method.call(this, node.childNodes[i]);
12510         }
12511         this.inPre = oldPre;
12512     },
12513     
12514     
12515     
12516     /**
12517      * compile the template
12518      *
12519      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12520      *
12521      */
12522     compile: function()
12523     {
12524         var s = this.html;
12525         
12526         // covert the html into DOM...
12527         var doc = false;
12528         var div =false;
12529         try {
12530             doc = document.implementation.createHTMLDocument("");
12531             doc.documentElement.innerHTML =   this.html  ;
12532             div = doc.documentElement;
12533         } catch (e) {
12534             // old IE... - nasty -- it causes all sorts of issues.. with
12535             // images getting pulled from server..
12536             div = document.createElement('div');
12537             div.innerHTML = this.html;
12538         }
12539         //doc.documentElement.innerHTML = htmlBody
12540          
12541         
12542         
12543         this.tpls = [];
12544         var _t = this;
12545         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12546         
12547         var tpls = this.tpls;
12548         
12549         // create a top level template from the snippet..
12550         
12551         //Roo.log(div.innerHTML);
12552         
12553         var tpl = {
12554             uid : 'master',
12555             id : this.id++,
12556             attr : false,
12557             value : false,
12558             body : div.innerHTML,
12559             
12560             forCall : false,
12561             execCall : false,
12562             dom : div,
12563             isTop : true
12564             
12565         };
12566         tpls.unshift(tpl);
12567         
12568         
12569         // compile them...
12570         this.tpls = [];
12571         Roo.each(tpls, function(tp){
12572             this.compileTpl(tp);
12573             this.tpls[tp.id] = tp;
12574         }, this);
12575         
12576         this.master = tpls[0];
12577         return this;
12578         
12579         
12580     },
12581     
12582     compileNode : function(node, istop) {
12583         // test for
12584         //Roo.log(node);
12585         
12586         
12587         // skip anything not a tag..
12588         if (node.nodeType != 1) {
12589             if (node.nodeType == 3 && !this.inPre) {
12590                 // reduce white space..
12591                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12592                 
12593             }
12594             return;
12595         }
12596         
12597         var tpl = {
12598             uid : false,
12599             id : false,
12600             attr : false,
12601             value : false,
12602             body : '',
12603             
12604             forCall : false,
12605             execCall : false,
12606             dom : false,
12607             isTop : istop
12608             
12609             
12610         };
12611         
12612         
12613         switch(true) {
12614             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12615             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12616             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12617             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12618             // no default..
12619         }
12620         
12621         
12622         if (!tpl.attr) {
12623             // just itterate children..
12624             this.iterChild(node,this.compileNode);
12625             return;
12626         }
12627         tpl.uid = this.id++;
12628         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12629         node.removeAttribute('roo-'+ tpl.attr);
12630         if (tpl.attr != 'name') {
12631             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12632             node.parentNode.replaceChild(placeholder,  node);
12633         } else {
12634             
12635             var placeholder =  document.createElement('span');
12636             placeholder.className = 'roo-tpl-' + tpl.value;
12637             node.parentNode.replaceChild(placeholder,  node);
12638         }
12639         
12640         // parent now sees '{domtplXXXX}
12641         this.iterChild(node,this.compileNode);
12642         
12643         // we should now have node body...
12644         var div = document.createElement('div');
12645         div.appendChild(node);
12646         tpl.dom = node;
12647         // this has the unfortunate side effect of converting tagged attributes
12648         // eg. href="{...}" into %7C...%7D
12649         // this has been fixed by searching for those combo's although it's a bit hacky..
12650         
12651         
12652         tpl.body = div.innerHTML;
12653         
12654         
12655          
12656         tpl.id = tpl.uid;
12657         switch(tpl.attr) {
12658             case 'for' :
12659                 switch (tpl.value) {
12660                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12661                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12662                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12663                 }
12664                 break;
12665             
12666             case 'exec':
12667                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12668                 break;
12669             
12670             case 'if':     
12671                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12672                 break;
12673             
12674             case 'name':
12675                 tpl.id  = tpl.value; // replace non characters???
12676                 break;
12677             
12678         }
12679         
12680         
12681         this.tpls.push(tpl);
12682         
12683         
12684         
12685     },
12686     
12687     
12688     
12689     
12690     /**
12691      * Compile a segment of the template into a 'sub-template'
12692      *
12693      * 
12694      * 
12695      *
12696      */
12697     compileTpl : function(tpl)
12698     {
12699         var fm = Roo.util.Format;
12700         var useF = this.disableFormats !== true;
12701         
12702         var sep = Roo.isGecko ? "+\n" : ",\n";
12703         
12704         var undef = function(str) {
12705             Roo.debug && Roo.log("Property not found :"  + str);
12706             return '';
12707         };
12708           
12709         //Roo.log(tpl.body);
12710         
12711         
12712         
12713         var fn = function(m, lbrace, name, format, args)
12714         {
12715             //Roo.log("ARGS");
12716             //Roo.log(arguments);
12717             args = args ? args.replace(/\\'/g,"'") : args;
12718             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12719             if (typeof(format) == 'undefined') {
12720                 format =  'htmlEncode'; 
12721             }
12722             if (format == 'raw' ) {
12723                 format = false;
12724             }
12725             
12726             if(name.substr(0, 6) == 'domtpl'){
12727                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12728             }
12729             
12730             // build an array of options to determine if value is undefined..
12731             
12732             // basically get 'xxxx.yyyy' then do
12733             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12734             //    (function () { Roo.log("Property not found"); return ''; })() :
12735             //    ......
12736             
12737             var udef_ar = [];
12738             var lookfor = '';
12739             Roo.each(name.split('.'), function(st) {
12740                 lookfor += (lookfor.length ? '.': '') + st;
12741                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12742             });
12743             
12744             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12745             
12746             
12747             if(format && useF){
12748                 
12749                 args = args ? ',' + args : "";
12750                  
12751                 if(format.substr(0, 5) != "this."){
12752                     format = "fm." + format + '(';
12753                 }else{
12754                     format = 'this.call("'+ format.substr(5) + '", ';
12755                     args = ", values";
12756                 }
12757                 
12758                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12759             }
12760              
12761             if (args && args.length) {
12762                 // called with xxyx.yuu:(test,test)
12763                 // change to ()
12764                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12765             }
12766             // raw.. - :raw modifier..
12767             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12768             
12769         };
12770         var body;
12771         // branched to use + in gecko and [].join() in others
12772         if(Roo.isGecko){
12773             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12774                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12775                     "';};};";
12776         }else{
12777             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12778             body.push(tpl.body.replace(/(\r\n|\n)/g,
12779                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12780             body.push("'].join('');};};");
12781             body = body.join('');
12782         }
12783         
12784         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12785        
12786         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12787         eval(body);
12788         
12789         return this;
12790     },
12791      
12792     /**
12793      * same as applyTemplate, except it's done to one of the subTemplates
12794      * when using named templates, you can do:
12795      *
12796      * var str = pl.applySubTemplate('your-name', values);
12797      *
12798      * 
12799      * @param {Number} id of the template
12800      * @param {Object} values to apply to template
12801      * @param {Object} parent (normaly the instance of this object)
12802      */
12803     applySubTemplate : function(id, values, parent)
12804     {
12805         
12806         
12807         var t = this.tpls[id];
12808         
12809         
12810         try { 
12811             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12812                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12813                 return '';
12814             }
12815         } catch(e) {
12816             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12817             Roo.log(values);
12818           
12819             return '';
12820         }
12821         try { 
12822             
12823             if(t.execCall && t.execCall.call(this, values, parent)){
12824                 return '';
12825             }
12826         } catch(e) {
12827             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12828             Roo.log(values);
12829             return '';
12830         }
12831         
12832         try {
12833             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12834             parent = t.target ? values : parent;
12835             if(t.forCall && vs instanceof Array){
12836                 var buf = [];
12837                 for(var i = 0, len = vs.length; i < len; i++){
12838                     try {
12839                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12840                     } catch (e) {
12841                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12842                         Roo.log(e.body);
12843                         //Roo.log(t.compiled);
12844                         Roo.log(vs[i]);
12845                     }   
12846                 }
12847                 return buf.join('');
12848             }
12849         } catch (e) {
12850             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12851             Roo.log(values);
12852             return '';
12853         }
12854         try {
12855             return t.compiled.call(this, vs, parent);
12856         } catch (e) {
12857             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12858             Roo.log(e.body);
12859             //Roo.log(t.compiled);
12860             Roo.log(values);
12861             return '';
12862         }
12863     },
12864
12865    
12866
12867     applyTemplate : function(values){
12868         return this.master.compiled.call(this, values, {});
12869         //var s = this.subs;
12870     },
12871
12872     apply : function(){
12873         return this.applyTemplate.apply(this, arguments);
12874     }
12875
12876  });
12877
12878 Roo.DomTemplate.from = function(el){
12879     el = Roo.getDom(el);
12880     return new Roo.Domtemplate(el.value || el.innerHTML);
12881 };/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891
12892 /**
12893  * @class Roo.util.DelayedTask
12894  * Provides a convenient method of performing setTimeout where a new
12895  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12896  * You can use this class to buffer
12897  * the keypress events for a certain number of milliseconds, and perform only if they stop
12898  * for that amount of time.
12899  * @constructor The parameters to this constructor serve as defaults and are not required.
12900  * @param {Function} fn (optional) The default function to timeout
12901  * @param {Object} scope (optional) The default scope of that timeout
12902  * @param {Array} args (optional) The default Array of arguments
12903  */
12904 Roo.util.DelayedTask = function(fn, scope, args){
12905     var id = null, d, t;
12906
12907     var call = function(){
12908         var now = new Date().getTime();
12909         if(now - t >= d){
12910             clearInterval(id);
12911             id = null;
12912             fn.apply(scope, args || []);
12913         }
12914     };
12915     /**
12916      * Cancels any pending timeout and queues a new one
12917      * @param {Number} delay The milliseconds to delay
12918      * @param {Function} newFn (optional) Overrides function passed to constructor
12919      * @param {Object} newScope (optional) Overrides scope passed to constructor
12920      * @param {Array} newArgs (optional) Overrides args passed to constructor
12921      */
12922     this.delay = function(delay, newFn, newScope, newArgs){
12923         if(id && delay != d){
12924             this.cancel();
12925         }
12926         d = delay;
12927         t = new Date().getTime();
12928         fn = newFn || fn;
12929         scope = newScope || scope;
12930         args = newArgs || args;
12931         if(!id){
12932             id = setInterval(call, d);
12933         }
12934     };
12935
12936     /**
12937      * Cancel the last queued timeout
12938      */
12939     this.cancel = function(){
12940         if(id){
12941             clearInterval(id);
12942             id = null;
12943         }
12944     };
12945 };/*
12946  * Based on:
12947  * Ext JS Library 1.1.1
12948  * Copyright(c) 2006-2007, Ext JS, LLC.
12949  *
12950  * Originally Released Under LGPL - original licence link has changed is not relivant.
12951  *
12952  * Fork - LGPL
12953  * <script type="text/javascript">
12954  */
12955  
12956  
12957 Roo.util.TaskRunner = function(interval){
12958     interval = interval || 10;
12959     var tasks = [], removeQueue = [];
12960     var id = 0;
12961     var running = false;
12962
12963     var stopThread = function(){
12964         running = false;
12965         clearInterval(id);
12966         id = 0;
12967     };
12968
12969     var startThread = function(){
12970         if(!running){
12971             running = true;
12972             id = setInterval(runTasks, interval);
12973         }
12974     };
12975
12976     var removeTask = function(task){
12977         removeQueue.push(task);
12978         if(task.onStop){
12979             task.onStop();
12980         }
12981     };
12982
12983     var runTasks = function(){
12984         if(removeQueue.length > 0){
12985             for(var i = 0, len = removeQueue.length; i < len; i++){
12986                 tasks.remove(removeQueue[i]);
12987             }
12988             removeQueue = [];
12989             if(tasks.length < 1){
12990                 stopThread();
12991                 return;
12992             }
12993         }
12994         var now = new Date().getTime();
12995         for(var i = 0, len = tasks.length; i < len; ++i){
12996             var t = tasks[i];
12997             var itime = now - t.taskRunTime;
12998             if(t.interval <= itime){
12999                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13000                 t.taskRunTime = now;
13001                 if(rt === false || t.taskRunCount === t.repeat){
13002                     removeTask(t);
13003                     return;
13004                 }
13005             }
13006             if(t.duration && t.duration <= (now - t.taskStartTime)){
13007                 removeTask(t);
13008             }
13009         }
13010     };
13011
13012     /**
13013      * Queues a new task.
13014      * @param {Object} task
13015      */
13016     this.start = function(task){
13017         tasks.push(task);
13018         task.taskStartTime = new Date().getTime();
13019         task.taskRunTime = 0;
13020         task.taskRunCount = 0;
13021         startThread();
13022         return task;
13023     };
13024
13025     this.stop = function(task){
13026         removeTask(task);
13027         return task;
13028     };
13029
13030     this.stopAll = function(){
13031         stopThread();
13032         for(var i = 0, len = tasks.length; i < len; i++){
13033             if(tasks[i].onStop){
13034                 tasks[i].onStop();
13035             }
13036         }
13037         tasks = [];
13038         removeQueue = [];
13039     };
13040 };
13041
13042 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13043  * Based on:
13044  * Ext JS Library 1.1.1
13045  * Copyright(c) 2006-2007, Ext JS, LLC.
13046  *
13047  * Originally Released Under LGPL - original licence link has changed is not relivant.
13048  *
13049  * Fork - LGPL
13050  * <script type="text/javascript">
13051  */
13052
13053  
13054 /**
13055  * @class Roo.util.MixedCollection
13056  * @extends Roo.util.Observable
13057  * A Collection class that maintains both numeric indexes and keys and exposes events.
13058  * @constructor
13059  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13060  * collection (defaults to false)
13061  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13062  * and return the key value for that item.  This is used when available to look up the key on items that
13063  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13064  * equivalent to providing an implementation for the {@link #getKey} method.
13065  */
13066 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13067     this.items = [];
13068     this.map = {};
13069     this.keys = [];
13070     this.length = 0;
13071     this.addEvents({
13072         /**
13073          * @event clear
13074          * Fires when the collection is cleared.
13075          */
13076         "clear" : true,
13077         /**
13078          * @event add
13079          * Fires when an item is added to the collection.
13080          * @param {Number} index The index at which the item was added.
13081          * @param {Object} o The item added.
13082          * @param {String} key The key associated with the added item.
13083          */
13084         "add" : true,
13085         /**
13086          * @event replace
13087          * Fires when an item is replaced in the collection.
13088          * @param {String} key he key associated with the new added.
13089          * @param {Object} old The item being replaced.
13090          * @param {Object} new The new item.
13091          */
13092         "replace" : true,
13093         /**
13094          * @event remove
13095          * Fires when an item is removed from the collection.
13096          * @param {Object} o The item being removed.
13097          * @param {String} key (optional) The key associated with the removed item.
13098          */
13099         "remove" : true,
13100         "sort" : true
13101     });
13102     this.allowFunctions = allowFunctions === true;
13103     if(keyFn){
13104         this.getKey = keyFn;
13105     }
13106     Roo.util.MixedCollection.superclass.constructor.call(this);
13107 };
13108
13109 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13110     allowFunctions : false,
13111     
13112 /**
13113  * Adds an item to the collection.
13114  * @param {String} key The key to associate with the item
13115  * @param {Object} o The item to add.
13116  * @return {Object} The item added.
13117  */
13118     add : function(key, o){
13119         if(arguments.length == 1){
13120             o = arguments[0];
13121             key = this.getKey(o);
13122         }
13123         if(typeof key == "undefined" || key === null){
13124             this.length++;
13125             this.items.push(o);
13126             this.keys.push(null);
13127         }else{
13128             var old = this.map[key];
13129             if(old){
13130                 return this.replace(key, o);
13131             }
13132             this.length++;
13133             this.items.push(o);
13134             this.map[key] = o;
13135             this.keys.push(key);
13136         }
13137         this.fireEvent("add", this.length-1, o, key);
13138         return o;
13139     },
13140        
13141 /**
13142   * MixedCollection has a generic way to fetch keys if you implement getKey.
13143 <pre><code>
13144 // normal way
13145 var mc = new Roo.util.MixedCollection();
13146 mc.add(someEl.dom.id, someEl);
13147 mc.add(otherEl.dom.id, otherEl);
13148 //and so on
13149
13150 // using getKey
13151 var mc = new Roo.util.MixedCollection();
13152 mc.getKey = function(el){
13153    return el.dom.id;
13154 };
13155 mc.add(someEl);
13156 mc.add(otherEl);
13157
13158 // or via the constructor
13159 var mc = new Roo.util.MixedCollection(false, function(el){
13160    return el.dom.id;
13161 });
13162 mc.add(someEl);
13163 mc.add(otherEl);
13164 </code></pre>
13165  * @param o {Object} The item for which to find the key.
13166  * @return {Object} The key for the passed item.
13167  */
13168     getKey : function(o){
13169          return o.id; 
13170     },
13171    
13172 /**
13173  * Replaces an item in the collection.
13174  * @param {String} key The key associated with the item to replace, or the item to replace.
13175  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13176  * @return {Object}  The new item.
13177  */
13178     replace : function(key, o){
13179         if(arguments.length == 1){
13180             o = arguments[0];
13181             key = this.getKey(o);
13182         }
13183         var old = this.item(key);
13184         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13185              return this.add(key, o);
13186         }
13187         var index = this.indexOfKey(key);
13188         this.items[index] = o;
13189         this.map[key] = o;
13190         this.fireEvent("replace", key, old, o);
13191         return o;
13192     },
13193    
13194 /**
13195  * Adds all elements of an Array or an Object to the collection.
13196  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13197  * an Array of values, each of which are added to the collection.
13198  */
13199     addAll : function(objs){
13200         if(arguments.length > 1 || objs instanceof Array){
13201             var args = arguments.length > 1 ? arguments : objs;
13202             for(var i = 0, len = args.length; i < len; i++){
13203                 this.add(args[i]);
13204             }
13205         }else{
13206             for(var key in objs){
13207                 if(this.allowFunctions || typeof objs[key] != "function"){
13208                     this.add(key, objs[key]);
13209                 }
13210             }
13211         }
13212     },
13213    
13214 /**
13215  * Executes the specified function once for every item in the collection, passing each
13216  * item as the first and only parameter. returning false from the function will stop the iteration.
13217  * @param {Function} fn The function to execute for each item.
13218  * @param {Object} scope (optional) The scope in which to execute the function.
13219  */
13220     each : function(fn, scope){
13221         var items = [].concat(this.items); // each safe for removal
13222         for(var i = 0, len = items.length; i < len; i++){
13223             if(fn.call(scope || items[i], items[i], i, len) === false){
13224                 break;
13225             }
13226         }
13227     },
13228    
13229 /**
13230  * Executes the specified function once for every key in the collection, passing each
13231  * key, and its associated item as the first two parameters.
13232  * @param {Function} fn The function to execute for each item.
13233  * @param {Object} scope (optional) The scope in which to execute the function.
13234  */
13235     eachKey : function(fn, scope){
13236         for(var i = 0, len = this.keys.length; i < len; i++){
13237             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13238         }
13239     },
13240    
13241 /**
13242  * Returns the first item in the collection which elicits a true return value from the
13243  * passed selection function.
13244  * @param {Function} fn The selection function to execute for each item.
13245  * @param {Object} scope (optional) The scope in which to execute the function.
13246  * @return {Object} The first item in the collection which returned true from the selection function.
13247  */
13248     find : function(fn, scope){
13249         for(var i = 0, len = this.items.length; i < len; i++){
13250             if(fn.call(scope || window, this.items[i], this.keys[i])){
13251                 return this.items[i];
13252             }
13253         }
13254         return null;
13255     },
13256    
13257 /**
13258  * Inserts an item at the specified index in the collection.
13259  * @param {Number} index The index to insert the item at.
13260  * @param {String} key The key to associate with the new item, or the item itself.
13261  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13262  * @return {Object} The item inserted.
13263  */
13264     insert : function(index, key, o){
13265         if(arguments.length == 2){
13266             o = arguments[1];
13267             key = this.getKey(o);
13268         }
13269         if(index >= this.length){
13270             return this.add(key, o);
13271         }
13272         this.length++;
13273         this.items.splice(index, 0, o);
13274         if(typeof key != "undefined" && key != null){
13275             this.map[key] = o;
13276         }
13277         this.keys.splice(index, 0, key);
13278         this.fireEvent("add", index, o, key);
13279         return o;
13280     },
13281    
13282 /**
13283  * Removed an item from the collection.
13284  * @param {Object} o The item to remove.
13285  * @return {Object} The item removed.
13286  */
13287     remove : function(o){
13288         return this.removeAt(this.indexOf(o));
13289     },
13290    
13291 /**
13292  * Remove an item from a specified index in the collection.
13293  * @param {Number} index The index within the collection of the item to remove.
13294  */
13295     removeAt : function(index){
13296         if(index < this.length && index >= 0){
13297             this.length--;
13298             var o = this.items[index];
13299             this.items.splice(index, 1);
13300             var key = this.keys[index];
13301             if(typeof key != "undefined"){
13302                 delete this.map[key];
13303             }
13304             this.keys.splice(index, 1);
13305             this.fireEvent("remove", o, key);
13306         }
13307     },
13308    
13309 /**
13310  * Removed an item associated with the passed key fom the collection.
13311  * @param {String} key The key of the item to remove.
13312  */
13313     removeKey : function(key){
13314         return this.removeAt(this.indexOfKey(key));
13315     },
13316    
13317 /**
13318  * Returns the number of items in the collection.
13319  * @return {Number} the number of items in the collection.
13320  */
13321     getCount : function(){
13322         return this.length; 
13323     },
13324    
13325 /**
13326  * Returns index within the collection of the passed Object.
13327  * @param {Object} o The item to find the index of.
13328  * @return {Number} index of the item.
13329  */
13330     indexOf : function(o){
13331         if(!this.items.indexOf){
13332             for(var i = 0, len = this.items.length; i < len; i++){
13333                 if(this.items[i] == o) {
13334                     return i;
13335                 }
13336             }
13337             return -1;
13338         }else{
13339             return this.items.indexOf(o);
13340         }
13341     },
13342    
13343 /**
13344  * Returns index within the collection of the passed key.
13345  * @param {String} key The key to find the index of.
13346  * @return {Number} index of the key.
13347  */
13348     indexOfKey : function(key){
13349         if(!this.keys.indexOf){
13350             for(var i = 0, len = this.keys.length; i < len; i++){
13351                 if(this.keys[i] == key) {
13352                     return i;
13353                 }
13354             }
13355             return -1;
13356         }else{
13357             return this.keys.indexOf(key);
13358         }
13359     },
13360    
13361 /**
13362  * Returns the item associated with the passed key OR index. Key has priority over index.
13363  * @param {String/Number} key The key or index of the item.
13364  * @return {Object} The item associated with the passed key.
13365  */
13366     item : function(key){
13367         if (key === 'length') {
13368             return null;
13369         }
13370         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13371         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13372     },
13373     
13374 /**
13375  * Returns the item at the specified index.
13376  * @param {Number} index The index of the item.
13377  * @return {Object}
13378  */
13379     itemAt : function(index){
13380         return this.items[index];
13381     },
13382     
13383 /**
13384  * Returns the item associated with the passed key.
13385  * @param {String/Number} key The key of the item.
13386  * @return {Object} The item associated with the passed key.
13387  */
13388     key : function(key){
13389         return this.map[key];
13390     },
13391    
13392 /**
13393  * Returns true if the collection contains the passed Object as an item.
13394  * @param {Object} o  The Object to look for in the collection.
13395  * @return {Boolean} True if the collection contains the Object as an item.
13396  */
13397     contains : function(o){
13398         return this.indexOf(o) != -1;
13399     },
13400    
13401 /**
13402  * Returns true if the collection contains the passed Object as a key.
13403  * @param {String} key The key to look for in the collection.
13404  * @return {Boolean} True if the collection contains the Object as a key.
13405  */
13406     containsKey : function(key){
13407         return typeof this.map[key] != "undefined";
13408     },
13409    
13410 /**
13411  * Removes all items from the collection.
13412  */
13413     clear : function(){
13414         this.length = 0;
13415         this.items = [];
13416         this.keys = [];
13417         this.map = {};
13418         this.fireEvent("clear");
13419     },
13420    
13421 /**
13422  * Returns the first item in the collection.
13423  * @return {Object} the first item in the collection..
13424  */
13425     first : function(){
13426         return this.items[0]; 
13427     },
13428    
13429 /**
13430  * Returns the last item in the collection.
13431  * @return {Object} the last item in the collection..
13432  */
13433     last : function(){
13434         return this.items[this.length-1];   
13435     },
13436     
13437     _sort : function(property, dir, fn){
13438         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13439         fn = fn || function(a, b){
13440             return a-b;
13441         };
13442         var c = [], k = this.keys, items = this.items;
13443         for(var i = 0, len = items.length; i < len; i++){
13444             c[c.length] = {key: k[i], value: items[i], index: i};
13445         }
13446         c.sort(function(a, b){
13447             var v = fn(a[property], b[property]) * dsc;
13448             if(v == 0){
13449                 v = (a.index < b.index ? -1 : 1);
13450             }
13451             return v;
13452         });
13453         for(var i = 0, len = c.length; i < len; i++){
13454             items[i] = c[i].value;
13455             k[i] = c[i].key;
13456         }
13457         this.fireEvent("sort", this);
13458     },
13459     
13460     /**
13461      * Sorts this collection with the passed comparison function
13462      * @param {String} direction (optional) "ASC" or "DESC"
13463      * @param {Function} fn (optional) comparison function
13464      */
13465     sort : function(dir, fn){
13466         this._sort("value", dir, fn);
13467     },
13468     
13469     /**
13470      * Sorts this collection by keys
13471      * @param {String} direction (optional) "ASC" or "DESC"
13472      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13473      */
13474     keySort : function(dir, fn){
13475         this._sort("key", dir, fn || function(a, b){
13476             return String(a).toUpperCase()-String(b).toUpperCase();
13477         });
13478     },
13479     
13480     /**
13481      * Returns a range of items in this collection
13482      * @param {Number} startIndex (optional) defaults to 0
13483      * @param {Number} endIndex (optional) default to the last item
13484      * @return {Array} An array of items
13485      */
13486     getRange : function(start, end){
13487         var items = this.items;
13488         if(items.length < 1){
13489             return [];
13490         }
13491         start = start || 0;
13492         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13493         var r = [];
13494         if(start <= end){
13495             for(var i = start; i <= end; i++) {
13496                     r[r.length] = items[i];
13497             }
13498         }else{
13499             for(var i = start; i >= end; i--) {
13500                     r[r.length] = items[i];
13501             }
13502         }
13503         return r;
13504     },
13505         
13506     /**
13507      * Filter the <i>objects</i> in this collection by a specific property. 
13508      * Returns a new collection that has been filtered.
13509      * @param {String} property A property on your objects
13510      * @param {String/RegExp} value Either string that the property values 
13511      * should start with or a RegExp to test against the property
13512      * @return {MixedCollection} The new filtered collection
13513      */
13514     filter : function(property, value){
13515         if(!value.exec){ // not a regex
13516             value = String(value);
13517             if(value.length == 0){
13518                 return this.clone();
13519             }
13520             value = new RegExp("^" + Roo.escapeRe(value), "i");
13521         }
13522         return this.filterBy(function(o){
13523             return o && value.test(o[property]);
13524         });
13525         },
13526     
13527     /**
13528      * Filter by a function. * Returns a new collection that has been filtered.
13529      * The passed function will be called with each 
13530      * object in the collection. If the function returns true, the value is included 
13531      * otherwise it is filtered.
13532      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13533      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13534      * @return {MixedCollection} The new filtered collection
13535      */
13536     filterBy : function(fn, scope){
13537         var r = new Roo.util.MixedCollection();
13538         r.getKey = this.getKey;
13539         var k = this.keys, it = this.items;
13540         for(var i = 0, len = it.length; i < len; i++){
13541             if(fn.call(scope||this, it[i], k[i])){
13542                                 r.add(k[i], it[i]);
13543                         }
13544         }
13545         return r;
13546     },
13547     
13548     /**
13549      * Creates a duplicate of this collection
13550      * @return {MixedCollection}
13551      */
13552     clone : function(){
13553         var r = new Roo.util.MixedCollection();
13554         var k = this.keys, it = this.items;
13555         for(var i = 0, len = it.length; i < len; i++){
13556             r.add(k[i], it[i]);
13557         }
13558         r.getKey = this.getKey;
13559         return r;
13560     }
13561 });
13562 /**
13563  * Returns the item associated with the passed key or index.
13564  * @method
13565  * @param {String/Number} key The key or index of the item.
13566  * @return {Object} The item associated with the passed key.
13567  */
13568 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13569  * Based on:
13570  * Ext JS Library 1.1.1
13571  * Copyright(c) 2006-2007, Ext JS, LLC.
13572  *
13573  * Originally Released Under LGPL - original licence link has changed is not relivant.
13574  *
13575  * Fork - LGPL
13576  * <script type="text/javascript">
13577  */
13578 /**
13579  * @class Roo.util.JSON
13580  * Modified version of Douglas Crockford"s json.js that doesn"t
13581  * mess with the Object prototype 
13582  * http://www.json.org/js.html
13583  * @singleton
13584  */
13585 Roo.util.JSON = new (function(){
13586     var useHasOwn = {}.hasOwnProperty ? true : false;
13587     
13588     // crashes Safari in some instances
13589     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13590     
13591     var pad = function(n) {
13592         return n < 10 ? "0" + n : n;
13593     };
13594     
13595     var m = {
13596         "\b": '\\b',
13597         "\t": '\\t',
13598         "\n": '\\n',
13599         "\f": '\\f',
13600         "\r": '\\r',
13601         '"' : '\\"',
13602         "\\": '\\\\'
13603     };
13604
13605     var encodeString = function(s){
13606         if (/["\\\x00-\x1f]/.test(s)) {
13607             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13608                 var c = m[b];
13609                 if(c){
13610                     return c;
13611                 }
13612                 c = b.charCodeAt();
13613                 return "\\u00" +
13614                     Math.floor(c / 16).toString(16) +
13615                     (c % 16).toString(16);
13616             }) + '"';
13617         }
13618         return '"' + s + '"';
13619     };
13620     
13621     var encodeArray = function(o){
13622         var a = ["["], b, i, l = o.length, v;
13623             for (i = 0; i < l; i += 1) {
13624                 v = o[i];
13625                 switch (typeof v) {
13626                     case "undefined":
13627                     case "function":
13628                     case "unknown":
13629                         break;
13630                     default:
13631                         if (b) {
13632                             a.push(',');
13633                         }
13634                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13635                         b = true;
13636                 }
13637             }
13638             a.push("]");
13639             return a.join("");
13640     };
13641     
13642     var encodeDate = function(o){
13643         return '"' + o.getFullYear() + "-" +
13644                 pad(o.getMonth() + 1) + "-" +
13645                 pad(o.getDate()) + "T" +
13646                 pad(o.getHours()) + ":" +
13647                 pad(o.getMinutes()) + ":" +
13648                 pad(o.getSeconds()) + '"';
13649     };
13650     
13651     /**
13652      * Encodes an Object, Array or other value
13653      * @param {Mixed} o The variable to encode
13654      * @return {String} The JSON string
13655      */
13656     this.encode = function(o)
13657     {
13658         // should this be extended to fully wrap stringify..
13659         
13660         if(typeof o == "undefined" || o === null){
13661             return "null";
13662         }else if(o instanceof Array){
13663             return encodeArray(o);
13664         }else if(o instanceof Date){
13665             return encodeDate(o);
13666         }else if(typeof o == "string"){
13667             return encodeString(o);
13668         }else if(typeof o == "number"){
13669             return isFinite(o) ? String(o) : "null";
13670         }else if(typeof o == "boolean"){
13671             return String(o);
13672         }else {
13673             var a = ["{"], b, i, v;
13674             for (i in o) {
13675                 if(!useHasOwn || o.hasOwnProperty(i)) {
13676                     v = o[i];
13677                     switch (typeof v) {
13678                     case "undefined":
13679                     case "function":
13680                     case "unknown":
13681                         break;
13682                     default:
13683                         if(b){
13684                             a.push(',');
13685                         }
13686                         a.push(this.encode(i), ":",
13687                                 v === null ? "null" : this.encode(v));
13688                         b = true;
13689                     }
13690                 }
13691             }
13692             a.push("}");
13693             return a.join("");
13694         }
13695     };
13696     
13697     /**
13698      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13699      * @param {String} json The JSON string
13700      * @return {Object} The resulting object
13701      */
13702     this.decode = function(json){
13703         
13704         return  /** eval:var:json */ eval("(" + json + ')');
13705     };
13706 })();
13707 /** 
13708  * Shorthand for {@link Roo.util.JSON#encode}
13709  * @member Roo encode 
13710  * @method */
13711 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13712 /** 
13713  * Shorthand for {@link Roo.util.JSON#decode}
13714  * @member Roo decode 
13715  * @method */
13716 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13717 /*
13718  * Based on:
13719  * Ext JS Library 1.1.1
13720  * Copyright(c) 2006-2007, Ext JS, LLC.
13721  *
13722  * Originally Released Under LGPL - original licence link has changed is not relivant.
13723  *
13724  * Fork - LGPL
13725  * <script type="text/javascript">
13726  */
13727  
13728 /**
13729  * @class Roo.util.Format
13730  * Reusable data formatting functions
13731  * @singleton
13732  */
13733 Roo.util.Format = function(){
13734     var trimRe = /^\s+|\s+$/g;
13735     return {
13736         /**
13737          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13738          * @param {String} value The string to truncate
13739          * @param {Number} length The maximum length to allow before truncating
13740          * @return {String} The converted text
13741          */
13742         ellipsis : function(value, len){
13743             if(value && value.length > len){
13744                 return value.substr(0, len-3)+"...";
13745             }
13746             return value;
13747         },
13748
13749         /**
13750          * Checks a reference and converts it to empty string if it is undefined
13751          * @param {Mixed} value Reference to check
13752          * @return {Mixed} Empty string if converted, otherwise the original value
13753          */
13754         undef : function(value){
13755             return typeof value != "undefined" ? value : "";
13756         },
13757
13758         /**
13759          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13760          * @param {String} value The string to encode
13761          * @return {String} The encoded text
13762          */
13763         htmlEncode : function(value){
13764             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13765         },
13766
13767         /**
13768          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13769          * @param {String} value The string to decode
13770          * @return {String} The decoded text
13771          */
13772         htmlDecode : function(value){
13773             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13774         },
13775
13776         /**
13777          * Trims any whitespace from either side of a string
13778          * @param {String} value The text to trim
13779          * @return {String} The trimmed text
13780          */
13781         trim : function(value){
13782             return String(value).replace(trimRe, "");
13783         },
13784
13785         /**
13786          * Returns a substring from within an original string
13787          * @param {String} value The original text
13788          * @param {Number} start The start index of the substring
13789          * @param {Number} length The length of the substring
13790          * @return {String} The substring
13791          */
13792         substr : function(value, start, length){
13793             return String(value).substr(start, length);
13794         },
13795
13796         /**
13797          * Converts a string to all lower case letters
13798          * @param {String} value The text to convert
13799          * @return {String} The converted text
13800          */
13801         lowercase : function(value){
13802             return String(value).toLowerCase();
13803         },
13804
13805         /**
13806          * Converts a string to all upper case letters
13807          * @param {String} value The text to convert
13808          * @return {String} The converted text
13809          */
13810         uppercase : function(value){
13811             return String(value).toUpperCase();
13812         },
13813
13814         /**
13815          * Converts the first character only of a string to upper case
13816          * @param {String} value The text to convert
13817          * @return {String} The converted text
13818          */
13819         capitalize : function(value){
13820             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13821         },
13822
13823         // private
13824         call : function(value, fn){
13825             if(arguments.length > 2){
13826                 var args = Array.prototype.slice.call(arguments, 2);
13827                 args.unshift(value);
13828                  
13829                 return /** eval:var:value */  eval(fn).apply(window, args);
13830             }else{
13831                 /** eval:var:value */
13832                 return /** eval:var:value */ eval(fn).call(window, value);
13833             }
13834         },
13835
13836        
13837         /**
13838          * safer version of Math.toFixed..??/
13839          * @param {Number/String} value The numeric value to format
13840          * @param {Number/String} value Decimal places 
13841          * @return {String} The formatted currency string
13842          */
13843         toFixed : function(v, n)
13844         {
13845             // why not use to fixed - precision is buggered???
13846             if (!n) {
13847                 return Math.round(v-0);
13848             }
13849             var fact = Math.pow(10,n+1);
13850             v = (Math.round((v-0)*fact))/fact;
13851             var z = (''+fact).substring(2);
13852             if (v == Math.floor(v)) {
13853                 return Math.floor(v) + '.' + z;
13854             }
13855             
13856             // now just padd decimals..
13857             var ps = String(v).split('.');
13858             var fd = (ps[1] + z);
13859             var r = fd.substring(0,n); 
13860             var rm = fd.substring(n); 
13861             if (rm < 5) {
13862                 return ps[0] + '.' + r;
13863             }
13864             r*=1; // turn it into a number;
13865             r++;
13866             if (String(r).length != n) {
13867                 ps[0]*=1;
13868                 ps[0]++;
13869                 r = String(r).substring(1); // chop the end off.
13870             }
13871             
13872             return ps[0] + '.' + r;
13873              
13874         },
13875         
13876         /**
13877          * Format a number as US currency
13878          * @param {Number/String} value The numeric value to format
13879          * @return {String} The formatted currency string
13880          */
13881         usMoney : function(v){
13882             return '$' + Roo.util.Format.number(v);
13883         },
13884         
13885         /**
13886          * Format a number
13887          * eventually this should probably emulate php's number_format
13888          * @param {Number/String} value The numeric value to format
13889          * @param {Number} decimals number of decimal places
13890          * @param {String} delimiter for thousands (default comma)
13891          * @return {String} The formatted currency string
13892          */
13893         number : function(v, decimals, thousandsDelimiter)
13894         {
13895             // multiply and round.
13896             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13897             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13898             
13899             var mul = Math.pow(10, decimals);
13900             var zero = String(mul).substring(1);
13901             v = (Math.round((v-0)*mul))/mul;
13902             
13903             // if it's '0' number.. then
13904             
13905             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13906             v = String(v);
13907             var ps = v.split('.');
13908             var whole = ps[0];
13909             
13910             var r = /(\d+)(\d{3})/;
13911             // add comma's
13912             
13913             if(thousandsDelimiter.length != 0) {
13914                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13915             } 
13916             
13917             var sub = ps[1] ?
13918                     // has decimals..
13919                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13920                     // does not have decimals
13921                     (decimals ? ('.' + zero) : '');
13922             
13923             
13924             return whole + sub ;
13925         },
13926         
13927         /**
13928          * Parse a value into a formatted date using the specified format pattern.
13929          * @param {Mixed} value The value to format
13930          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13931          * @return {String} The formatted date string
13932          */
13933         date : function(v, format){
13934             if(!v){
13935                 return "";
13936             }
13937             if(!(v instanceof Date)){
13938                 v = new Date(Date.parse(v));
13939             }
13940             return v.dateFormat(format || Roo.util.Format.defaults.date);
13941         },
13942
13943         /**
13944          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13945          * @param {String} format Any valid date format string
13946          * @return {Function} The date formatting function
13947          */
13948         dateRenderer : function(format){
13949             return function(v){
13950                 return Roo.util.Format.date(v, format);  
13951             };
13952         },
13953
13954         // private
13955         stripTagsRE : /<\/?[^>]+>/gi,
13956         
13957         /**
13958          * Strips all HTML tags
13959          * @param {Mixed} value The text from which to strip tags
13960          * @return {String} The stripped text
13961          */
13962         stripTags : function(v){
13963             return !v ? v : String(v).replace(this.stripTagsRE, "");
13964         }
13965     };
13966 }();
13967 Roo.util.Format.defaults = {
13968     date : 'd/M/Y'
13969 };/*
13970  * Based on:
13971  * Ext JS Library 1.1.1
13972  * Copyright(c) 2006-2007, Ext JS, LLC.
13973  *
13974  * Originally Released Under LGPL - original licence link has changed is not relivant.
13975  *
13976  * Fork - LGPL
13977  * <script type="text/javascript">
13978  */
13979
13980
13981  
13982
13983 /**
13984  * @class Roo.MasterTemplate
13985  * @extends Roo.Template
13986  * Provides a template that can have child templates. The syntax is:
13987 <pre><code>
13988 var t = new Roo.MasterTemplate(
13989         '&lt;select name="{name}"&gt;',
13990                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13991         '&lt;/select&gt;'
13992 );
13993 t.add('options', {value: 'foo', text: 'bar'});
13994 // or you can add multiple child elements in one shot
13995 t.addAll('options', [
13996     {value: 'foo', text: 'bar'},
13997     {value: 'foo2', text: 'bar2'},
13998     {value: 'foo3', text: 'bar3'}
13999 ]);
14000 // then append, applying the master template values
14001 t.append('my-form', {name: 'my-select'});
14002 </code></pre>
14003 * A name attribute for the child template is not required if you have only one child
14004 * template or you want to refer to them by index.
14005  */
14006 Roo.MasterTemplate = function(){
14007     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14008     this.originalHtml = this.html;
14009     var st = {};
14010     var m, re = this.subTemplateRe;
14011     re.lastIndex = 0;
14012     var subIndex = 0;
14013     while(m = re.exec(this.html)){
14014         var name = m[1], content = m[2];
14015         st[subIndex] = {
14016             name: name,
14017             index: subIndex,
14018             buffer: [],
14019             tpl : new Roo.Template(content)
14020         };
14021         if(name){
14022             st[name] = st[subIndex];
14023         }
14024         st[subIndex].tpl.compile();
14025         st[subIndex].tpl.call = this.call.createDelegate(this);
14026         subIndex++;
14027     }
14028     this.subCount = subIndex;
14029     this.subs = st;
14030 };
14031 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14032     /**
14033     * The regular expression used to match sub templates
14034     * @type RegExp
14035     * @property
14036     */
14037     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14038
14039     /**
14040      * Applies the passed values to a child template.
14041      * @param {String/Number} name (optional) The name or index of the child template
14042      * @param {Array/Object} values The values to be applied to the template
14043      * @return {MasterTemplate} this
14044      */
14045      add : function(name, values){
14046         if(arguments.length == 1){
14047             values = arguments[0];
14048             name = 0;
14049         }
14050         var s = this.subs[name];
14051         s.buffer[s.buffer.length] = s.tpl.apply(values);
14052         return this;
14053     },
14054
14055     /**
14056      * Applies all the passed values to a child template.
14057      * @param {String/Number} name (optional) The name or index of the child template
14058      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14059      * @param {Boolean} reset (optional) True to reset the template first
14060      * @return {MasterTemplate} this
14061      */
14062     fill : function(name, values, reset){
14063         var a = arguments;
14064         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14065             values = a[0];
14066             name = 0;
14067             reset = a[1];
14068         }
14069         if(reset){
14070             this.reset();
14071         }
14072         for(var i = 0, len = values.length; i < len; i++){
14073             this.add(name, values[i]);
14074         }
14075         return this;
14076     },
14077
14078     /**
14079      * Resets the template for reuse
14080      * @return {MasterTemplate} this
14081      */
14082      reset : function(){
14083         var s = this.subs;
14084         for(var i = 0; i < this.subCount; i++){
14085             s[i].buffer = [];
14086         }
14087         return this;
14088     },
14089
14090     applyTemplate : function(values){
14091         var s = this.subs;
14092         var replaceIndex = -1;
14093         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14094             return s[++replaceIndex].buffer.join("");
14095         });
14096         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14097     },
14098
14099     apply : function(){
14100         return this.applyTemplate.apply(this, arguments);
14101     },
14102
14103     compile : function(){return this;}
14104 });
14105
14106 /**
14107  * Alias for fill().
14108  * @method
14109  */
14110 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14111  /**
14112  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14113  * var tpl = Roo.MasterTemplate.from('element-id');
14114  * @param {String/HTMLElement} el
14115  * @param {Object} config
14116  * @static
14117  */
14118 Roo.MasterTemplate.from = function(el, config){
14119     el = Roo.getDom(el);
14120     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14121 };/*
14122  * Based on:
14123  * Ext JS Library 1.1.1
14124  * Copyright(c) 2006-2007, Ext JS, LLC.
14125  *
14126  * Originally Released Under LGPL - original licence link has changed is not relivant.
14127  *
14128  * Fork - LGPL
14129  * <script type="text/javascript">
14130  */
14131
14132  
14133 /**
14134  * @class Roo.util.CSS
14135  * Utility class for manipulating CSS rules
14136  * @singleton
14137  */
14138 Roo.util.CSS = function(){
14139         var rules = null;
14140         var doc = document;
14141
14142     var camelRe = /(-[a-z])/gi;
14143     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14144
14145    return {
14146    /**
14147     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14148     * tag and appended to the HEAD of the document.
14149     * @param {String|Object} cssText The text containing the css rules
14150     * @param {String} id An id to add to the stylesheet for later removal
14151     * @return {StyleSheet}
14152     */
14153     createStyleSheet : function(cssText, id){
14154         var ss;
14155         var head = doc.getElementsByTagName("head")[0];
14156         var nrules = doc.createElement("style");
14157         nrules.setAttribute("type", "text/css");
14158         if(id){
14159             nrules.setAttribute("id", id);
14160         }
14161         if (typeof(cssText) != 'string') {
14162             // support object maps..
14163             // not sure if this a good idea.. 
14164             // perhaps it should be merged with the general css handling
14165             // and handle js style props.
14166             var cssTextNew = [];
14167             for(var n in cssText) {
14168                 var citems = [];
14169                 for(var k in cssText[n]) {
14170                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14171                 }
14172                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14173                 
14174             }
14175             cssText = cssTextNew.join("\n");
14176             
14177         }
14178        
14179        
14180        if(Roo.isIE){
14181            head.appendChild(nrules);
14182            ss = nrules.styleSheet;
14183            ss.cssText = cssText;
14184        }else{
14185            try{
14186                 nrules.appendChild(doc.createTextNode(cssText));
14187            }catch(e){
14188                nrules.cssText = cssText; 
14189            }
14190            head.appendChild(nrules);
14191            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14192        }
14193        this.cacheStyleSheet(ss);
14194        return ss;
14195    },
14196
14197    /**
14198     * Removes a style or link tag by id
14199     * @param {String} id The id of the tag
14200     */
14201    removeStyleSheet : function(id){
14202        var existing = doc.getElementById(id);
14203        if(existing){
14204            existing.parentNode.removeChild(existing);
14205        }
14206    },
14207
14208    /**
14209     * Dynamically swaps an existing stylesheet reference for a new one
14210     * @param {String} id The id of an existing link tag to remove
14211     * @param {String} url The href of the new stylesheet to include
14212     */
14213    swapStyleSheet : function(id, url){
14214        this.removeStyleSheet(id);
14215        var ss = doc.createElement("link");
14216        ss.setAttribute("rel", "stylesheet");
14217        ss.setAttribute("type", "text/css");
14218        ss.setAttribute("id", id);
14219        ss.setAttribute("href", url);
14220        doc.getElementsByTagName("head")[0].appendChild(ss);
14221    },
14222    
14223    /**
14224     * Refresh the rule cache if you have dynamically added stylesheets
14225     * @return {Object} An object (hash) of rules indexed by selector
14226     */
14227    refreshCache : function(){
14228        return this.getRules(true);
14229    },
14230
14231    // private
14232    cacheStyleSheet : function(stylesheet){
14233        if(!rules){
14234            rules = {};
14235        }
14236        try{// try catch for cross domain access issue
14237            var ssRules = stylesheet.cssRules || stylesheet.rules;
14238            for(var j = ssRules.length-1; j >= 0; --j){
14239                rules[ssRules[j].selectorText] = ssRules[j];
14240            }
14241        }catch(e){}
14242    },
14243    
14244    /**
14245     * Gets all css rules for the document
14246     * @param {Boolean} refreshCache true to refresh the internal cache
14247     * @return {Object} An object (hash) of rules indexed by selector
14248     */
14249    getRules : function(refreshCache){
14250                 if(rules == null || refreshCache){
14251                         rules = {};
14252                         var ds = doc.styleSheets;
14253                         for(var i =0, len = ds.length; i < len; i++){
14254                             try{
14255                         this.cacheStyleSheet(ds[i]);
14256                     }catch(e){} 
14257                 }
14258                 }
14259                 return rules;
14260         },
14261         
14262         /**
14263     * Gets an an individual CSS rule by selector(s)
14264     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14265     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14266     * @return {CSSRule} The CSS rule or null if one is not found
14267     */
14268    getRule : function(selector, refreshCache){
14269                 var rs = this.getRules(refreshCache);
14270                 if(!(selector instanceof Array)){
14271                     return rs[selector];
14272                 }
14273                 for(var i = 0; i < selector.length; i++){
14274                         if(rs[selector[i]]){
14275                                 return rs[selector[i]];
14276                         }
14277                 }
14278                 return null;
14279         },
14280         
14281         
14282         /**
14283     * Updates a rule property
14284     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14285     * @param {String} property The css property
14286     * @param {String} value The new value for the property
14287     * @return {Boolean} true If a rule was found and updated
14288     */
14289    updateRule : function(selector, property, value){
14290                 if(!(selector instanceof Array)){
14291                         var rule = this.getRule(selector);
14292                         if(rule){
14293                                 rule.style[property.replace(camelRe, camelFn)] = value;
14294                                 return true;
14295                         }
14296                 }else{
14297                         for(var i = 0; i < selector.length; i++){
14298                                 if(this.updateRule(selector[i], property, value)){
14299                                         return true;
14300                                 }
14301                         }
14302                 }
14303                 return false;
14304         }
14305    };   
14306 }();/*
14307  * Based on:
14308  * Ext JS Library 1.1.1
14309  * Copyright(c) 2006-2007, Ext JS, LLC.
14310  *
14311  * Originally Released Under LGPL - original licence link has changed is not relivant.
14312  *
14313  * Fork - LGPL
14314  * <script type="text/javascript">
14315  */
14316
14317  
14318
14319 /**
14320  * @class Roo.util.ClickRepeater
14321  * @extends Roo.util.Observable
14322  * 
14323  * A wrapper class which can be applied to any element. Fires a "click" event while the
14324  * mouse is pressed. The interval between firings may be specified in the config but
14325  * defaults to 10 milliseconds.
14326  * 
14327  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14328  * 
14329  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14330  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14331  * Similar to an autorepeat key delay.
14332  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14333  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14334  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14335  *           "interval" and "delay" are ignored. "immediate" is honored.
14336  * @cfg {Boolean} preventDefault True to prevent the default click event
14337  * @cfg {Boolean} stopDefault True to stop the default click event
14338  * 
14339  * @history
14340  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14341  *     2007-02-02 jvs Renamed to ClickRepeater
14342  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14343  *
14344  *  @constructor
14345  * @param {String/HTMLElement/Element} el The element to listen on
14346  * @param {Object} config
14347  **/
14348 Roo.util.ClickRepeater = function(el, config)
14349 {
14350     this.el = Roo.get(el);
14351     this.el.unselectable();
14352
14353     Roo.apply(this, config);
14354
14355     this.addEvents({
14356     /**
14357      * @event mousedown
14358      * Fires when the mouse button is depressed.
14359      * @param {Roo.util.ClickRepeater} this
14360      */
14361         "mousedown" : true,
14362     /**
14363      * @event click
14364      * Fires on a specified interval during the time the element is pressed.
14365      * @param {Roo.util.ClickRepeater} this
14366      */
14367         "click" : true,
14368     /**
14369      * @event mouseup
14370      * Fires when the mouse key is released.
14371      * @param {Roo.util.ClickRepeater} this
14372      */
14373         "mouseup" : true
14374     });
14375
14376     this.el.on("mousedown", this.handleMouseDown, this);
14377     if(this.preventDefault || this.stopDefault){
14378         this.el.on("click", function(e){
14379             if(this.preventDefault){
14380                 e.preventDefault();
14381             }
14382             if(this.stopDefault){
14383                 e.stopEvent();
14384             }
14385         }, this);
14386     }
14387
14388     // allow inline handler
14389     if(this.handler){
14390         this.on("click", this.handler,  this.scope || this);
14391     }
14392
14393     Roo.util.ClickRepeater.superclass.constructor.call(this);
14394 };
14395
14396 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14397     interval : 20,
14398     delay: 250,
14399     preventDefault : true,
14400     stopDefault : false,
14401     timer : 0,
14402
14403     // private
14404     handleMouseDown : function(){
14405         clearTimeout(this.timer);
14406         this.el.blur();
14407         if(this.pressClass){
14408             this.el.addClass(this.pressClass);
14409         }
14410         this.mousedownTime = new Date();
14411
14412         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14413         this.el.on("mouseout", this.handleMouseOut, this);
14414
14415         this.fireEvent("mousedown", this);
14416         this.fireEvent("click", this);
14417         
14418         this.timer = this.click.defer(this.delay || this.interval, this);
14419     },
14420
14421     // private
14422     click : function(){
14423         this.fireEvent("click", this);
14424         this.timer = this.click.defer(this.getInterval(), this);
14425     },
14426
14427     // private
14428     getInterval: function(){
14429         if(!this.accelerate){
14430             return this.interval;
14431         }
14432         var pressTime = this.mousedownTime.getElapsed();
14433         if(pressTime < 500){
14434             return 400;
14435         }else if(pressTime < 1700){
14436             return 320;
14437         }else if(pressTime < 2600){
14438             return 250;
14439         }else if(pressTime < 3500){
14440             return 180;
14441         }else if(pressTime < 4400){
14442             return 140;
14443         }else if(pressTime < 5300){
14444             return 80;
14445         }else if(pressTime < 6200){
14446             return 50;
14447         }else{
14448             return 10;
14449         }
14450     },
14451
14452     // private
14453     handleMouseOut : function(){
14454         clearTimeout(this.timer);
14455         if(this.pressClass){
14456             this.el.removeClass(this.pressClass);
14457         }
14458         this.el.on("mouseover", this.handleMouseReturn, this);
14459     },
14460
14461     // private
14462     handleMouseReturn : function(){
14463         this.el.un("mouseover", this.handleMouseReturn);
14464         if(this.pressClass){
14465             this.el.addClass(this.pressClass);
14466         }
14467         this.click();
14468     },
14469
14470     // private
14471     handleMouseUp : function(){
14472         clearTimeout(this.timer);
14473         this.el.un("mouseover", this.handleMouseReturn);
14474         this.el.un("mouseout", this.handleMouseOut);
14475         Roo.get(document).un("mouseup", this.handleMouseUp);
14476         this.el.removeClass(this.pressClass);
14477         this.fireEvent("mouseup", this);
14478     }
14479 });/*
14480  * Based on:
14481  * Ext JS Library 1.1.1
14482  * Copyright(c) 2006-2007, Ext JS, LLC.
14483  *
14484  * Originally Released Under LGPL - original licence link has changed is not relivant.
14485  *
14486  * Fork - LGPL
14487  * <script type="text/javascript">
14488  */
14489
14490  
14491 /**
14492  * @class Roo.KeyNav
14493  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14494  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14495  * way to implement custom navigation schemes for any UI component.</p>
14496  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14497  * pageUp, pageDown, del, home, end.  Usage:</p>
14498  <pre><code>
14499 var nav = new Roo.KeyNav("my-element", {
14500     "left" : function(e){
14501         this.moveLeft(e.ctrlKey);
14502     },
14503     "right" : function(e){
14504         this.moveRight(e.ctrlKey);
14505     },
14506     "enter" : function(e){
14507         this.save();
14508     },
14509     scope : this
14510 });
14511 </code></pre>
14512  * @constructor
14513  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14514  * @param {Object} config The config
14515  */
14516 Roo.KeyNav = function(el, config){
14517     this.el = Roo.get(el);
14518     Roo.apply(this, config);
14519     if(!this.disabled){
14520         this.disabled = true;
14521         this.enable();
14522     }
14523 };
14524
14525 Roo.KeyNav.prototype = {
14526     /**
14527      * @cfg {Boolean} disabled
14528      * True to disable this KeyNav instance (defaults to false)
14529      */
14530     disabled : false,
14531     /**
14532      * @cfg {String} defaultEventAction
14533      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14534      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14535      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14536      */
14537     defaultEventAction: "stopEvent",
14538     /**
14539      * @cfg {Boolean} forceKeyDown
14540      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14541      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14542      * handle keydown instead of keypress.
14543      */
14544     forceKeyDown : false,
14545
14546     // private
14547     prepareEvent : function(e){
14548         var k = e.getKey();
14549         var h = this.keyToHandler[k];
14550         //if(h && this[h]){
14551         //    e.stopPropagation();
14552         //}
14553         if(Roo.isSafari && h && k >= 37 && k <= 40){
14554             e.stopEvent();
14555         }
14556     },
14557
14558     // private
14559     relay : function(e){
14560         var k = e.getKey();
14561         var h = this.keyToHandler[k];
14562         if(h && this[h]){
14563             if(this.doRelay(e, this[h], h) !== true){
14564                 e[this.defaultEventAction]();
14565             }
14566         }
14567     },
14568
14569     // private
14570     doRelay : function(e, h, hname){
14571         return h.call(this.scope || this, e);
14572     },
14573
14574     // possible handlers
14575     enter : false,
14576     left : false,
14577     right : false,
14578     up : false,
14579     down : false,
14580     tab : false,
14581     esc : false,
14582     pageUp : false,
14583     pageDown : false,
14584     del : false,
14585     home : false,
14586     end : false,
14587
14588     // quick lookup hash
14589     keyToHandler : {
14590         37 : "left",
14591         39 : "right",
14592         38 : "up",
14593         40 : "down",
14594         33 : "pageUp",
14595         34 : "pageDown",
14596         46 : "del",
14597         36 : "home",
14598         35 : "end",
14599         13 : "enter",
14600         27 : "esc",
14601         9  : "tab"
14602     },
14603
14604         /**
14605          * Enable this KeyNav
14606          */
14607         enable: function(){
14608                 if(this.disabled){
14609             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14610             // the EventObject will normalize Safari automatically
14611             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14612                 this.el.on("keydown", this.relay,  this);
14613             }else{
14614                 this.el.on("keydown", this.prepareEvent,  this);
14615                 this.el.on("keypress", this.relay,  this);
14616             }
14617                     this.disabled = false;
14618                 }
14619         },
14620
14621         /**
14622          * Disable this KeyNav
14623          */
14624         disable: function(){
14625                 if(!this.disabled){
14626                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14627                 this.el.un("keydown", this.relay);
14628             }else{
14629                 this.el.un("keydown", this.prepareEvent);
14630                 this.el.un("keypress", this.relay);
14631             }
14632                     this.disabled = true;
14633                 }
14634         }
14635 };/*
14636  * Based on:
14637  * Ext JS Library 1.1.1
14638  * Copyright(c) 2006-2007, Ext JS, LLC.
14639  *
14640  * Originally Released Under LGPL - original licence link has changed is not relivant.
14641  *
14642  * Fork - LGPL
14643  * <script type="text/javascript">
14644  */
14645
14646  
14647 /**
14648  * @class Roo.KeyMap
14649  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14650  * The constructor accepts the same config object as defined by {@link #addBinding}.
14651  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14652  * combination it will call the function with this signature (if the match is a multi-key
14653  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14654  * A KeyMap can also handle a string representation of keys.<br />
14655  * Usage:
14656  <pre><code>
14657 // map one key by key code
14658 var map = new Roo.KeyMap("my-element", {
14659     key: 13, // or Roo.EventObject.ENTER
14660     fn: myHandler,
14661     scope: myObject
14662 });
14663
14664 // map multiple keys to one action by string
14665 var map = new Roo.KeyMap("my-element", {
14666     key: "a\r\n\t",
14667     fn: myHandler,
14668     scope: myObject
14669 });
14670
14671 // map multiple keys to multiple actions by strings and array of codes
14672 var map = new Roo.KeyMap("my-element", [
14673     {
14674         key: [10,13],
14675         fn: function(){ alert("Return was pressed"); }
14676     }, {
14677         key: "abc",
14678         fn: function(){ alert('a, b or c was pressed'); }
14679     }, {
14680         key: "\t",
14681         ctrl:true,
14682         shift:true,
14683         fn: function(){ alert('Control + shift + tab was pressed.'); }
14684     }
14685 ]);
14686 </code></pre>
14687  * <b>Note: A KeyMap starts enabled</b>
14688  * @constructor
14689  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14690  * @param {Object} config The config (see {@link #addBinding})
14691  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14692  */
14693 Roo.KeyMap = function(el, config, eventName){
14694     this.el  = Roo.get(el);
14695     this.eventName = eventName || "keydown";
14696     this.bindings = [];
14697     if(config){
14698         this.addBinding(config);
14699     }
14700     this.enable();
14701 };
14702
14703 Roo.KeyMap.prototype = {
14704     /**
14705      * True to stop the event from bubbling and prevent the default browser action if the
14706      * key was handled by the KeyMap (defaults to false)
14707      * @type Boolean
14708      */
14709     stopEvent : false,
14710
14711     /**
14712      * Add a new binding to this KeyMap. The following config object properties are supported:
14713      * <pre>
14714 Property    Type             Description
14715 ----------  ---------------  ----------------------------------------------------------------------
14716 key         String/Array     A single keycode or an array of keycodes to handle
14717 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14718 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14719 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14720 fn          Function         The function to call when KeyMap finds the expected key combination
14721 scope       Object           The scope of the callback function
14722 </pre>
14723      *
14724      * Usage:
14725      * <pre><code>
14726 // Create a KeyMap
14727 var map = new Roo.KeyMap(document, {
14728     key: Roo.EventObject.ENTER,
14729     fn: handleKey,
14730     scope: this
14731 });
14732
14733 //Add a new binding to the existing KeyMap later
14734 map.addBinding({
14735     key: 'abc',
14736     shift: true,
14737     fn: handleKey,
14738     scope: this
14739 });
14740 </code></pre>
14741      * @param {Object/Array} config A single KeyMap config or an array of configs
14742      */
14743         addBinding : function(config){
14744         if(config instanceof Array){
14745             for(var i = 0, len = config.length; i < len; i++){
14746                 this.addBinding(config[i]);
14747             }
14748             return;
14749         }
14750         var keyCode = config.key,
14751             shift = config.shift, 
14752             ctrl = config.ctrl, 
14753             alt = config.alt,
14754             fn = config.fn,
14755             scope = config.scope;
14756         if(typeof keyCode == "string"){
14757             var ks = [];
14758             var keyString = keyCode.toUpperCase();
14759             for(var j = 0, len = keyString.length; j < len; j++){
14760                 ks.push(keyString.charCodeAt(j));
14761             }
14762             keyCode = ks;
14763         }
14764         var keyArray = keyCode instanceof Array;
14765         var handler = function(e){
14766             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14767                 var k = e.getKey();
14768                 if(keyArray){
14769                     for(var i = 0, len = keyCode.length; i < len; i++){
14770                         if(keyCode[i] == k){
14771                           if(this.stopEvent){
14772                               e.stopEvent();
14773                           }
14774                           fn.call(scope || window, k, e);
14775                           return;
14776                         }
14777                     }
14778                 }else{
14779                     if(k == keyCode){
14780                         if(this.stopEvent){
14781                            e.stopEvent();
14782                         }
14783                         fn.call(scope || window, k, e);
14784                     }
14785                 }
14786             }
14787         };
14788         this.bindings.push(handler);  
14789         },
14790
14791     /**
14792      * Shorthand for adding a single key listener
14793      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14794      * following options:
14795      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14796      * @param {Function} fn The function to call
14797      * @param {Object} scope (optional) The scope of the function
14798      */
14799     on : function(key, fn, scope){
14800         var keyCode, shift, ctrl, alt;
14801         if(typeof key == "object" && !(key instanceof Array)){
14802             keyCode = key.key;
14803             shift = key.shift;
14804             ctrl = key.ctrl;
14805             alt = key.alt;
14806         }else{
14807             keyCode = key;
14808         }
14809         this.addBinding({
14810             key: keyCode,
14811             shift: shift,
14812             ctrl: ctrl,
14813             alt: alt,
14814             fn: fn,
14815             scope: scope
14816         })
14817     },
14818
14819     // private
14820     handleKeyDown : function(e){
14821             if(this.enabled){ //just in case
14822             var b = this.bindings;
14823             for(var i = 0, len = b.length; i < len; i++){
14824                 b[i].call(this, e);
14825             }
14826             }
14827         },
14828         
14829         /**
14830          * Returns true if this KeyMap is enabled
14831          * @return {Boolean} 
14832          */
14833         isEnabled : function(){
14834             return this.enabled;  
14835         },
14836         
14837         /**
14838          * Enables this KeyMap
14839          */
14840         enable: function(){
14841                 if(!this.enabled){
14842                     this.el.on(this.eventName, this.handleKeyDown, this);
14843                     this.enabled = true;
14844                 }
14845         },
14846
14847         /**
14848          * Disable this KeyMap
14849          */
14850         disable: function(){
14851                 if(this.enabled){
14852                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14853                     this.enabled = false;
14854                 }
14855         }
14856 };/*
14857  * Based on:
14858  * Ext JS Library 1.1.1
14859  * Copyright(c) 2006-2007, Ext JS, LLC.
14860  *
14861  * Originally Released Under LGPL - original licence link has changed is not relivant.
14862  *
14863  * Fork - LGPL
14864  * <script type="text/javascript">
14865  */
14866
14867  
14868 /**
14869  * @class Roo.util.TextMetrics
14870  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14871  * wide, in pixels, a given block of text will be.
14872  * @singleton
14873  */
14874 Roo.util.TextMetrics = function(){
14875     var shared;
14876     return {
14877         /**
14878          * Measures the size of the specified text
14879          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14880          * that can affect the size of the rendered text
14881          * @param {String} text The text to measure
14882          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14883          * in order to accurately measure the text height
14884          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14885          */
14886         measure : function(el, text, fixedWidth){
14887             if(!shared){
14888                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14889             }
14890             shared.bind(el);
14891             shared.setFixedWidth(fixedWidth || 'auto');
14892             return shared.getSize(text);
14893         },
14894
14895         /**
14896          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14897          * the overhead of multiple calls to initialize the style properties on each measurement.
14898          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14899          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14900          * in order to accurately measure the text height
14901          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14902          */
14903         createInstance : function(el, fixedWidth){
14904             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14905         }
14906     };
14907 }();
14908
14909  
14910
14911 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14912     var ml = new Roo.Element(document.createElement('div'));
14913     document.body.appendChild(ml.dom);
14914     ml.position('absolute');
14915     ml.setLeftTop(-1000, -1000);
14916     ml.hide();
14917
14918     if(fixedWidth){
14919         ml.setWidth(fixedWidth);
14920     }
14921      
14922     var instance = {
14923         /**
14924          * Returns the size of the specified text based on the internal element's style and width properties
14925          * @memberOf Roo.util.TextMetrics.Instance#
14926          * @param {String} text The text to measure
14927          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14928          */
14929         getSize : function(text){
14930             ml.update(text);
14931             var s = ml.getSize();
14932             ml.update('');
14933             return s;
14934         },
14935
14936         /**
14937          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14938          * that can affect the size of the rendered text
14939          * @memberOf Roo.util.TextMetrics.Instance#
14940          * @param {String/HTMLElement} el The element, dom node or id
14941          */
14942         bind : function(el){
14943             ml.setStyle(
14944                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14945             );
14946         },
14947
14948         /**
14949          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14950          * to set a fixed width in order to accurately measure the text height.
14951          * @memberOf Roo.util.TextMetrics.Instance#
14952          * @param {Number} width The width to set on the element
14953          */
14954         setFixedWidth : function(width){
14955             ml.setWidth(width);
14956         },
14957
14958         /**
14959          * Returns the measured width of the specified text
14960          * @memberOf Roo.util.TextMetrics.Instance#
14961          * @param {String} text The text to measure
14962          * @return {Number} width The width in pixels
14963          */
14964         getWidth : function(text){
14965             ml.dom.style.width = 'auto';
14966             return this.getSize(text).width;
14967         },
14968
14969         /**
14970          * Returns the measured height of the specified text.  For multiline text, be sure to call
14971          * {@link #setFixedWidth} if necessary.
14972          * @memberOf Roo.util.TextMetrics.Instance#
14973          * @param {String} text The text to measure
14974          * @return {Number} height The height in pixels
14975          */
14976         getHeight : function(text){
14977             return this.getSize(text).height;
14978         }
14979     };
14980
14981     instance.bind(bindTo);
14982
14983     return instance;
14984 };
14985
14986 // backwards compat
14987 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14988  * Based on:
14989  * Ext JS Library 1.1.1
14990  * Copyright(c) 2006-2007, Ext JS, LLC.
14991  *
14992  * Originally Released Under LGPL - original licence link has changed is not relivant.
14993  *
14994  * Fork - LGPL
14995  * <script type="text/javascript">
14996  */
14997
14998 /**
14999  * @class Roo.state.Provider
15000  * Abstract base class for state provider implementations. This class provides methods
15001  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15002  * Provider interface.
15003  */
15004 Roo.state.Provider = function(){
15005     /**
15006      * @event statechange
15007      * Fires when a state change occurs.
15008      * @param {Provider} this This state provider
15009      * @param {String} key The state key which was changed
15010      * @param {String} value The encoded value for the state
15011      */
15012     this.addEvents({
15013         "statechange": true
15014     });
15015     this.state = {};
15016     Roo.state.Provider.superclass.constructor.call(this);
15017 };
15018 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15019     /**
15020      * Returns the current value for a key
15021      * @param {String} name The key name
15022      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15023      * @return {Mixed} The state data
15024      */
15025     get : function(name, defaultValue){
15026         return typeof this.state[name] == "undefined" ?
15027             defaultValue : this.state[name];
15028     },
15029     
15030     /**
15031      * Clears a value from the state
15032      * @param {String} name The key name
15033      */
15034     clear : function(name){
15035         delete this.state[name];
15036         this.fireEvent("statechange", this, name, null);
15037     },
15038     
15039     /**
15040      * Sets the value for a key
15041      * @param {String} name The key name
15042      * @param {Mixed} value The value to set
15043      */
15044     set : function(name, value){
15045         this.state[name] = value;
15046         this.fireEvent("statechange", this, name, value);
15047     },
15048     
15049     /**
15050      * Decodes a string previously encoded with {@link #encodeValue}.
15051      * @param {String} value The value to decode
15052      * @return {Mixed} The decoded value
15053      */
15054     decodeValue : function(cookie){
15055         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15056         var matches = re.exec(unescape(cookie));
15057         if(!matches || !matches[1]) {
15058             return; // non state cookie
15059         }
15060         var type = matches[1];
15061         var v = matches[2];
15062         switch(type){
15063             case "n":
15064                 return parseFloat(v);
15065             case "d":
15066                 return new Date(Date.parse(v));
15067             case "b":
15068                 return (v == "1");
15069             case "a":
15070                 var all = [];
15071                 var values = v.split("^");
15072                 for(var i = 0, len = values.length; i < len; i++){
15073                     all.push(this.decodeValue(values[i]));
15074                 }
15075                 return all;
15076            case "o":
15077                 var all = {};
15078                 var values = v.split("^");
15079                 for(var i = 0, len = values.length; i < len; i++){
15080                     var kv = values[i].split("=");
15081                     all[kv[0]] = this.decodeValue(kv[1]);
15082                 }
15083                 return all;
15084            default:
15085                 return v;
15086         }
15087     },
15088     
15089     /**
15090      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15091      * @param {Mixed} value The value to encode
15092      * @return {String} The encoded value
15093      */
15094     encodeValue : function(v){
15095         var enc;
15096         if(typeof v == "number"){
15097             enc = "n:" + v;
15098         }else if(typeof v == "boolean"){
15099             enc = "b:" + (v ? "1" : "0");
15100         }else if(v instanceof Date){
15101             enc = "d:" + v.toGMTString();
15102         }else if(v instanceof Array){
15103             var flat = "";
15104             for(var i = 0, len = v.length; i < len; i++){
15105                 flat += this.encodeValue(v[i]);
15106                 if(i != len-1) {
15107                     flat += "^";
15108                 }
15109             }
15110             enc = "a:" + flat;
15111         }else if(typeof v == "object"){
15112             var flat = "";
15113             for(var key in v){
15114                 if(typeof v[key] != "function"){
15115                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15116                 }
15117             }
15118             enc = "o:" + flat.substring(0, flat.length-1);
15119         }else{
15120             enc = "s:" + v;
15121         }
15122         return escape(enc);        
15123     }
15124 });
15125
15126 /*
15127  * Based on:
15128  * Ext JS Library 1.1.1
15129  * Copyright(c) 2006-2007, Ext JS, LLC.
15130  *
15131  * Originally Released Under LGPL - original licence link has changed is not relivant.
15132  *
15133  * Fork - LGPL
15134  * <script type="text/javascript">
15135  */
15136 /**
15137  * @class Roo.state.Manager
15138  * This is the global state manager. By default all components that are "state aware" check this class
15139  * for state information if you don't pass them a custom state provider. In order for this class
15140  * to be useful, it must be initialized with a provider when your application initializes.
15141  <pre><code>
15142 // in your initialization function
15143 init : function(){
15144    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15145    ...
15146    // supposed you have a {@link Roo.BorderLayout}
15147    var layout = new Roo.BorderLayout(...);
15148    layout.restoreState();
15149    // or a {Roo.BasicDialog}
15150    var dialog = new Roo.BasicDialog(...);
15151    dialog.restoreState();
15152  </code></pre>
15153  * @singleton
15154  */
15155 Roo.state.Manager = function(){
15156     var provider = new Roo.state.Provider();
15157     
15158     return {
15159         /**
15160          * Configures the default state provider for your application
15161          * @param {Provider} stateProvider The state provider to set
15162          */
15163         setProvider : function(stateProvider){
15164             provider = stateProvider;
15165         },
15166         
15167         /**
15168          * Returns the current value for a key
15169          * @param {String} name The key name
15170          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15171          * @return {Mixed} The state data
15172          */
15173         get : function(key, defaultValue){
15174             return provider.get(key, defaultValue);
15175         },
15176         
15177         /**
15178          * Sets the value for a key
15179          * @param {String} name The key name
15180          * @param {Mixed} value The state data
15181          */
15182          set : function(key, value){
15183             provider.set(key, value);
15184         },
15185         
15186         /**
15187          * Clears a value from the state
15188          * @param {String} name The key name
15189          */
15190         clear : function(key){
15191             provider.clear(key);
15192         },
15193         
15194         /**
15195          * Gets the currently configured state provider
15196          * @return {Provider} The state provider
15197          */
15198         getProvider : function(){
15199             return provider;
15200         }
15201     };
15202 }();
15203 /*
15204  * Based on:
15205  * Ext JS Library 1.1.1
15206  * Copyright(c) 2006-2007, Ext JS, LLC.
15207  *
15208  * Originally Released Under LGPL - original licence link has changed is not relivant.
15209  *
15210  * Fork - LGPL
15211  * <script type="text/javascript">
15212  */
15213 /**
15214  * @class Roo.state.CookieProvider
15215  * @extends Roo.state.Provider
15216  * The default Provider implementation which saves state via cookies.
15217  * <br />Usage:
15218  <pre><code>
15219    var cp = new Roo.state.CookieProvider({
15220        path: "/cgi-bin/",
15221        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15222        domain: "roojs.com"
15223    })
15224    Roo.state.Manager.setProvider(cp);
15225  </code></pre>
15226  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15227  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15228  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15229  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15230  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15231  * domain the page is running on including the 'www' like 'www.roojs.com')
15232  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15233  * @constructor
15234  * Create a new CookieProvider
15235  * @param {Object} config The configuration object
15236  */
15237 Roo.state.CookieProvider = function(config){
15238     Roo.state.CookieProvider.superclass.constructor.call(this);
15239     this.path = "/";
15240     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15241     this.domain = null;
15242     this.secure = false;
15243     Roo.apply(this, config);
15244     this.state = this.readCookies();
15245 };
15246
15247 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15248     // private
15249     set : function(name, value){
15250         if(typeof value == "undefined" || value === null){
15251             this.clear(name);
15252             return;
15253         }
15254         this.setCookie(name, value);
15255         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15256     },
15257
15258     // private
15259     clear : function(name){
15260         this.clearCookie(name);
15261         Roo.state.CookieProvider.superclass.clear.call(this, name);
15262     },
15263
15264     // private
15265     readCookies : function(){
15266         var cookies = {};
15267         var c = document.cookie + ";";
15268         var re = /\s?(.*?)=(.*?);/g;
15269         var matches;
15270         while((matches = re.exec(c)) != null){
15271             var name = matches[1];
15272             var value = matches[2];
15273             if(name && name.substring(0,3) == "ys-"){
15274                 cookies[name.substr(3)] = this.decodeValue(value);
15275             }
15276         }
15277         return cookies;
15278     },
15279
15280     // private
15281     setCookie : function(name, value){
15282         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15283            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15284            ((this.path == null) ? "" : ("; path=" + this.path)) +
15285            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15286            ((this.secure == true) ? "; secure" : "");
15287     },
15288
15289     // private
15290     clearCookie : function(name){
15291         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15292            ((this.path == null) ? "" : ("; path=" + this.path)) +
15293            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15294            ((this.secure == true) ? "; secure" : "");
15295     }
15296 });/*
15297  * Based on:
15298  * Ext JS Library 1.1.1
15299  * Copyright(c) 2006-2007, Ext JS, LLC.
15300  *
15301  * Originally Released Under LGPL - original licence link has changed is not relivant.
15302  *
15303  * Fork - LGPL
15304  * <script type="text/javascript">
15305  */
15306  
15307
15308 /**
15309  * @class Roo.ComponentMgr
15310  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15311  * @singleton
15312  */
15313 Roo.ComponentMgr = function(){
15314     var all = new Roo.util.MixedCollection();
15315
15316     return {
15317         /**
15318          * Registers a component.
15319          * @param {Roo.Component} c The component
15320          */
15321         register : function(c){
15322             all.add(c);
15323         },
15324
15325         /**
15326          * Unregisters a component.
15327          * @param {Roo.Component} c The component
15328          */
15329         unregister : function(c){
15330             all.remove(c);
15331         },
15332
15333         /**
15334          * Returns a component by id
15335          * @param {String} id The component id
15336          */
15337         get : function(id){
15338             return all.get(id);
15339         },
15340
15341         /**
15342          * Registers a function that will be called when a specified component is added to ComponentMgr
15343          * @param {String} id The component id
15344          * @param {Funtction} fn The callback function
15345          * @param {Object} scope The scope of the callback
15346          */
15347         onAvailable : function(id, fn, scope){
15348             all.on("add", function(index, o){
15349                 if(o.id == id){
15350                     fn.call(scope || o, o);
15351                     all.un("add", fn, scope);
15352                 }
15353             });
15354         }
15355     };
15356 }();/*
15357  * Based on:
15358  * Ext JS Library 1.1.1
15359  * Copyright(c) 2006-2007, Ext JS, LLC.
15360  *
15361  * Originally Released Under LGPL - original licence link has changed is not relivant.
15362  *
15363  * Fork - LGPL
15364  * <script type="text/javascript">
15365  */
15366  
15367 /**
15368  * @class Roo.Component
15369  * @extends Roo.util.Observable
15370  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15371  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15372  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15373  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15374  * All visual components (widgets) that require rendering into a layout should subclass Component.
15375  * @constructor
15376  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15377  * 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
15378  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15379  */
15380 Roo.Component = function(config){
15381     config = config || {};
15382     if(config.tagName || config.dom || typeof config == "string"){ // element object
15383         config = {el: config, id: config.id || config};
15384     }
15385     this.initialConfig = config;
15386
15387     Roo.apply(this, config);
15388     this.addEvents({
15389         /**
15390          * @event disable
15391          * Fires after the component is disabled.
15392              * @param {Roo.Component} this
15393              */
15394         disable : true,
15395         /**
15396          * @event enable
15397          * Fires after the component is enabled.
15398              * @param {Roo.Component} this
15399              */
15400         enable : true,
15401         /**
15402          * @event beforeshow
15403          * Fires before the component is shown.  Return false to stop the show.
15404              * @param {Roo.Component} this
15405              */
15406         beforeshow : true,
15407         /**
15408          * @event show
15409          * Fires after the component is shown.
15410              * @param {Roo.Component} this
15411              */
15412         show : true,
15413         /**
15414          * @event beforehide
15415          * Fires before the component is hidden. Return false to stop the hide.
15416              * @param {Roo.Component} this
15417              */
15418         beforehide : true,
15419         /**
15420          * @event hide
15421          * Fires after the component is hidden.
15422              * @param {Roo.Component} this
15423              */
15424         hide : true,
15425         /**
15426          * @event beforerender
15427          * Fires before the component is rendered. Return false to stop the render.
15428              * @param {Roo.Component} this
15429              */
15430         beforerender : true,
15431         /**
15432          * @event render
15433          * Fires after the component is rendered.
15434              * @param {Roo.Component} this
15435              */
15436         render : true,
15437         /**
15438          * @event beforedestroy
15439          * Fires before the component is destroyed. Return false to stop the destroy.
15440              * @param {Roo.Component} this
15441              */
15442         beforedestroy : true,
15443         /**
15444          * @event destroy
15445          * Fires after the component is destroyed.
15446              * @param {Roo.Component} this
15447              */
15448         destroy : true
15449     });
15450     if(!this.id){
15451         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15452     }
15453     Roo.ComponentMgr.register(this);
15454     Roo.Component.superclass.constructor.call(this);
15455     this.initComponent();
15456     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15457         this.render(this.renderTo);
15458         delete this.renderTo;
15459     }
15460 };
15461
15462 /** @private */
15463 Roo.Component.AUTO_ID = 1000;
15464
15465 Roo.extend(Roo.Component, Roo.util.Observable, {
15466     /**
15467      * @scope Roo.Component.prototype
15468      * @type {Boolean}
15469      * true if this component is hidden. Read-only.
15470      */
15471     hidden : false,
15472     /**
15473      * @type {Boolean}
15474      * true if this component is disabled. Read-only.
15475      */
15476     disabled : false,
15477     /**
15478      * @type {Boolean}
15479      * true if this component has been rendered. Read-only.
15480      */
15481     rendered : false,
15482     
15483     /** @cfg {String} disableClass
15484      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15485      */
15486     disabledClass : "x-item-disabled",
15487         /** @cfg {Boolean} allowDomMove
15488          * Whether the component can move the Dom node when rendering (defaults to true).
15489          */
15490     allowDomMove : true,
15491     /** @cfg {String} hideMode (display|visibility)
15492      * How this component should hidden. Supported values are
15493      * "visibility" (css visibility), "offsets" (negative offset position) and
15494      * "display" (css display) - defaults to "display".
15495      */
15496     hideMode: 'display',
15497
15498     /** @private */
15499     ctype : "Roo.Component",
15500
15501     /**
15502      * @cfg {String} actionMode 
15503      * which property holds the element that used for  hide() / show() / disable() / enable()
15504      * default is 'el' for forms you probably want to set this to fieldEl 
15505      */
15506     actionMode : "el",
15507
15508     /** @private */
15509     getActionEl : function(){
15510         return this[this.actionMode];
15511     },
15512
15513     initComponent : Roo.emptyFn,
15514     /**
15515      * If this is a lazy rendering component, render it to its container element.
15516      * @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.
15517      */
15518     render : function(container, position){
15519         
15520         if(this.rendered){
15521             return this;
15522         }
15523         
15524         if(this.fireEvent("beforerender", this) === false){
15525             return false;
15526         }
15527         
15528         if(!container && this.el){
15529             this.el = Roo.get(this.el);
15530             container = this.el.dom.parentNode;
15531             this.allowDomMove = false;
15532         }
15533         this.container = Roo.get(container);
15534         this.rendered = true;
15535         if(position !== undefined){
15536             if(typeof position == 'number'){
15537                 position = this.container.dom.childNodes[position];
15538             }else{
15539                 position = Roo.getDom(position);
15540             }
15541         }
15542         this.onRender(this.container, position || null);
15543         if(this.cls){
15544             this.el.addClass(this.cls);
15545             delete this.cls;
15546         }
15547         if(this.style){
15548             this.el.applyStyles(this.style);
15549             delete this.style;
15550         }
15551         this.fireEvent("render", this);
15552         this.afterRender(this.container);
15553         if(this.hidden){
15554             this.hide();
15555         }
15556         if(this.disabled){
15557             this.disable();
15558         }
15559
15560         return this;
15561         
15562     },
15563
15564     /** @private */
15565     // default function is not really useful
15566     onRender : function(ct, position){
15567         if(this.el){
15568             this.el = Roo.get(this.el);
15569             if(this.allowDomMove !== false){
15570                 ct.dom.insertBefore(this.el.dom, position);
15571             }
15572         }
15573     },
15574
15575     /** @private */
15576     getAutoCreate : function(){
15577         var cfg = typeof this.autoCreate == "object" ?
15578                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15579         if(this.id && !cfg.id){
15580             cfg.id = this.id;
15581         }
15582         return cfg;
15583     },
15584
15585     /** @private */
15586     afterRender : Roo.emptyFn,
15587
15588     /**
15589      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15590      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15591      */
15592     destroy : function(){
15593         if(this.fireEvent("beforedestroy", this) !== false){
15594             this.purgeListeners();
15595             this.beforeDestroy();
15596             if(this.rendered){
15597                 this.el.removeAllListeners();
15598                 this.el.remove();
15599                 if(this.actionMode == "container"){
15600                     this.container.remove();
15601                 }
15602             }
15603             this.onDestroy();
15604             Roo.ComponentMgr.unregister(this);
15605             this.fireEvent("destroy", this);
15606         }
15607     },
15608
15609         /** @private */
15610     beforeDestroy : function(){
15611
15612     },
15613
15614         /** @private */
15615         onDestroy : function(){
15616
15617     },
15618
15619     /**
15620      * Returns the underlying {@link Roo.Element}.
15621      * @return {Roo.Element} The element
15622      */
15623     getEl : function(){
15624         return this.el;
15625     },
15626
15627     /**
15628      * Returns the id of this component.
15629      * @return {String}
15630      */
15631     getId : function(){
15632         return this.id;
15633     },
15634
15635     /**
15636      * Try to focus this component.
15637      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15638      * @return {Roo.Component} this
15639      */
15640     focus : function(selectText){
15641         if(this.rendered){
15642             this.el.focus();
15643             if(selectText === true){
15644                 this.el.dom.select();
15645             }
15646         }
15647         return this;
15648     },
15649
15650     /** @private */
15651     blur : function(){
15652         if(this.rendered){
15653             this.el.blur();
15654         }
15655         return this;
15656     },
15657
15658     /**
15659      * Disable this component.
15660      * @return {Roo.Component} this
15661      */
15662     disable : function(){
15663         if(this.rendered){
15664             this.onDisable();
15665         }
15666         this.disabled = true;
15667         this.fireEvent("disable", this);
15668         return this;
15669     },
15670
15671         // private
15672     onDisable : function(){
15673         this.getActionEl().addClass(this.disabledClass);
15674         this.el.dom.disabled = true;
15675     },
15676
15677     /**
15678      * Enable this component.
15679      * @return {Roo.Component} this
15680      */
15681     enable : function(){
15682         if(this.rendered){
15683             this.onEnable();
15684         }
15685         this.disabled = false;
15686         this.fireEvent("enable", this);
15687         return this;
15688     },
15689
15690         // private
15691     onEnable : function(){
15692         this.getActionEl().removeClass(this.disabledClass);
15693         this.el.dom.disabled = false;
15694     },
15695
15696     /**
15697      * Convenience function for setting disabled/enabled by boolean.
15698      * @param {Boolean} disabled
15699      */
15700     setDisabled : function(disabled){
15701         this[disabled ? "disable" : "enable"]();
15702     },
15703
15704     /**
15705      * Show this component.
15706      * @return {Roo.Component} this
15707      */
15708     show: function(){
15709         if(this.fireEvent("beforeshow", this) !== false){
15710             this.hidden = false;
15711             if(this.rendered){
15712                 this.onShow();
15713             }
15714             this.fireEvent("show", this);
15715         }
15716         return this;
15717     },
15718
15719     // private
15720     onShow : function(){
15721         var ae = this.getActionEl();
15722         if(this.hideMode == 'visibility'){
15723             ae.dom.style.visibility = "visible";
15724         }else if(this.hideMode == 'offsets'){
15725             ae.removeClass('x-hidden');
15726         }else{
15727             ae.dom.style.display = "";
15728         }
15729     },
15730
15731     /**
15732      * Hide this component.
15733      * @return {Roo.Component} this
15734      */
15735     hide: function(){
15736         if(this.fireEvent("beforehide", this) !== false){
15737             this.hidden = true;
15738             if(this.rendered){
15739                 this.onHide();
15740             }
15741             this.fireEvent("hide", this);
15742         }
15743         return this;
15744     },
15745
15746     // private
15747     onHide : function(){
15748         var ae = this.getActionEl();
15749         if(this.hideMode == 'visibility'){
15750             ae.dom.style.visibility = "hidden";
15751         }else if(this.hideMode == 'offsets'){
15752             ae.addClass('x-hidden');
15753         }else{
15754             ae.dom.style.display = "none";
15755         }
15756     },
15757
15758     /**
15759      * Convenience function to hide or show this component by boolean.
15760      * @param {Boolean} visible True to show, false to hide
15761      * @return {Roo.Component} this
15762      */
15763     setVisible: function(visible){
15764         if(visible) {
15765             this.show();
15766         }else{
15767             this.hide();
15768         }
15769         return this;
15770     },
15771
15772     /**
15773      * Returns true if this component is visible.
15774      */
15775     isVisible : function(){
15776         return this.getActionEl().isVisible();
15777     },
15778
15779     cloneConfig : function(overrides){
15780         overrides = overrides || {};
15781         var id = overrides.id || Roo.id();
15782         var cfg = Roo.applyIf(overrides, this.initialConfig);
15783         cfg.id = id; // prevent dup id
15784         return new this.constructor(cfg);
15785     }
15786 });/*
15787  * Based on:
15788  * Ext JS Library 1.1.1
15789  * Copyright(c) 2006-2007, Ext JS, LLC.
15790  *
15791  * Originally Released Under LGPL - original licence link has changed is not relivant.
15792  *
15793  * Fork - LGPL
15794  * <script type="text/javascript">
15795  */
15796
15797 /**
15798  * @class Roo.BoxComponent
15799  * @extends Roo.Component
15800  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15801  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15802  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15803  * layout containers.
15804  * @constructor
15805  * @param {Roo.Element/String/Object} config The configuration options.
15806  */
15807 Roo.BoxComponent = function(config){
15808     Roo.Component.call(this, config);
15809     this.addEvents({
15810         /**
15811          * @event resize
15812          * Fires after the component is resized.
15813              * @param {Roo.Component} this
15814              * @param {Number} adjWidth The box-adjusted width that was set
15815              * @param {Number} adjHeight The box-adjusted height that was set
15816              * @param {Number} rawWidth The width that was originally specified
15817              * @param {Number} rawHeight The height that was originally specified
15818              */
15819         resize : true,
15820         /**
15821          * @event move
15822          * Fires after the component is moved.
15823              * @param {Roo.Component} this
15824              * @param {Number} x The new x position
15825              * @param {Number} y The new y position
15826              */
15827         move : true
15828     });
15829 };
15830
15831 Roo.extend(Roo.BoxComponent, Roo.Component, {
15832     // private, set in afterRender to signify that the component has been rendered
15833     boxReady : false,
15834     // private, used to defer height settings to subclasses
15835     deferHeight: false,
15836     /** @cfg {Number} width
15837      * width (optional) size of component
15838      */
15839      /** @cfg {Number} height
15840      * height (optional) size of component
15841      */
15842      
15843     /**
15844      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15845      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15846      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15847      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15848      * @return {Roo.BoxComponent} this
15849      */
15850     setSize : function(w, h){
15851         // support for standard size objects
15852         if(typeof w == 'object'){
15853             h = w.height;
15854             w = w.width;
15855         }
15856         // not rendered
15857         if(!this.boxReady){
15858             this.width = w;
15859             this.height = h;
15860             return this;
15861         }
15862
15863         // prevent recalcs when not needed
15864         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15865             return this;
15866         }
15867         this.lastSize = {width: w, height: h};
15868
15869         var adj = this.adjustSize(w, h);
15870         var aw = adj.width, ah = adj.height;
15871         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15872             var rz = this.getResizeEl();
15873             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15874                 rz.setSize(aw, ah);
15875             }else if(!this.deferHeight && ah !== undefined){
15876                 rz.setHeight(ah);
15877             }else if(aw !== undefined){
15878                 rz.setWidth(aw);
15879             }
15880             this.onResize(aw, ah, w, h);
15881             this.fireEvent('resize', this, aw, ah, w, h);
15882         }
15883         return this;
15884     },
15885
15886     /**
15887      * Gets the current size of the component's underlying element.
15888      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15889      */
15890     getSize : function(){
15891         return this.el.getSize();
15892     },
15893
15894     /**
15895      * Gets the current XY position of the component's underlying element.
15896      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15897      * @return {Array} The XY position of the element (e.g., [100, 200])
15898      */
15899     getPosition : function(local){
15900         if(local === true){
15901             return [this.el.getLeft(true), this.el.getTop(true)];
15902         }
15903         return this.xy || this.el.getXY();
15904     },
15905
15906     /**
15907      * Gets the current box measurements of the component's underlying element.
15908      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15909      * @returns {Object} box An object in the format {x, y, width, height}
15910      */
15911     getBox : function(local){
15912         var s = this.el.getSize();
15913         if(local){
15914             s.x = this.el.getLeft(true);
15915             s.y = this.el.getTop(true);
15916         }else{
15917             var xy = this.xy || this.el.getXY();
15918             s.x = xy[0];
15919             s.y = xy[1];
15920         }
15921         return s;
15922     },
15923
15924     /**
15925      * Sets the current box measurements of the component's underlying element.
15926      * @param {Object} box An object in the format {x, y, width, height}
15927      * @returns {Roo.BoxComponent} this
15928      */
15929     updateBox : function(box){
15930         this.setSize(box.width, box.height);
15931         this.setPagePosition(box.x, box.y);
15932         return this;
15933     },
15934
15935     // protected
15936     getResizeEl : function(){
15937         return this.resizeEl || this.el;
15938     },
15939
15940     // protected
15941     getPositionEl : function(){
15942         return this.positionEl || this.el;
15943     },
15944
15945     /**
15946      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15947      * This method fires the move event.
15948      * @param {Number} left The new left
15949      * @param {Number} top The new top
15950      * @returns {Roo.BoxComponent} this
15951      */
15952     setPosition : function(x, y){
15953         this.x = x;
15954         this.y = y;
15955         if(!this.boxReady){
15956             return this;
15957         }
15958         var adj = this.adjustPosition(x, y);
15959         var ax = adj.x, ay = adj.y;
15960
15961         var el = this.getPositionEl();
15962         if(ax !== undefined || ay !== undefined){
15963             if(ax !== undefined && ay !== undefined){
15964                 el.setLeftTop(ax, ay);
15965             }else if(ax !== undefined){
15966                 el.setLeft(ax);
15967             }else if(ay !== undefined){
15968                 el.setTop(ay);
15969             }
15970             this.onPosition(ax, ay);
15971             this.fireEvent('move', this, ax, ay);
15972         }
15973         return this;
15974     },
15975
15976     /**
15977      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15978      * This method fires the move event.
15979      * @param {Number} x The new x position
15980      * @param {Number} y The new y position
15981      * @returns {Roo.BoxComponent} this
15982      */
15983     setPagePosition : function(x, y){
15984         this.pageX = x;
15985         this.pageY = y;
15986         if(!this.boxReady){
15987             return;
15988         }
15989         if(x === undefined || y === undefined){ // cannot translate undefined points
15990             return;
15991         }
15992         var p = this.el.translatePoints(x, y);
15993         this.setPosition(p.left, p.top);
15994         return this;
15995     },
15996
15997     // private
15998     onRender : function(ct, position){
15999         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16000         if(this.resizeEl){
16001             this.resizeEl = Roo.get(this.resizeEl);
16002         }
16003         if(this.positionEl){
16004             this.positionEl = Roo.get(this.positionEl);
16005         }
16006     },
16007
16008     // private
16009     afterRender : function(){
16010         Roo.BoxComponent.superclass.afterRender.call(this);
16011         this.boxReady = true;
16012         this.setSize(this.width, this.height);
16013         if(this.x || this.y){
16014             this.setPosition(this.x, this.y);
16015         }
16016         if(this.pageX || this.pageY){
16017             this.setPagePosition(this.pageX, this.pageY);
16018         }
16019     },
16020
16021     /**
16022      * Force the component's size to recalculate based on the underlying element's current height and width.
16023      * @returns {Roo.BoxComponent} this
16024      */
16025     syncSize : function(){
16026         delete this.lastSize;
16027         this.setSize(this.el.getWidth(), this.el.getHeight());
16028         return this;
16029     },
16030
16031     /**
16032      * Called after the component is resized, this method is empty by default but can be implemented by any
16033      * subclass that needs to perform custom logic after a resize occurs.
16034      * @param {Number} adjWidth The box-adjusted width that was set
16035      * @param {Number} adjHeight The box-adjusted height that was set
16036      * @param {Number} rawWidth The width that was originally specified
16037      * @param {Number} rawHeight The height that was originally specified
16038      */
16039     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16040
16041     },
16042
16043     /**
16044      * Called after the component is moved, this method is empty by default but can be implemented by any
16045      * subclass that needs to perform custom logic after a move occurs.
16046      * @param {Number} x The new x position
16047      * @param {Number} y The new y position
16048      */
16049     onPosition : function(x, y){
16050
16051     },
16052
16053     // private
16054     adjustSize : function(w, h){
16055         if(this.autoWidth){
16056             w = 'auto';
16057         }
16058         if(this.autoHeight){
16059             h = 'auto';
16060         }
16061         return {width : w, height: h};
16062     },
16063
16064     // private
16065     adjustPosition : function(x, y){
16066         return {x : x, y: y};
16067     }
16068 });/*
16069  * Based on:
16070  * Ext JS Library 1.1.1
16071  * Copyright(c) 2006-2007, Ext JS, LLC.
16072  *
16073  * Originally Released Under LGPL - original licence link has changed is not relivant.
16074  *
16075  * Fork - LGPL
16076  * <script type="text/javascript">
16077  */
16078  (function(){ 
16079 /**
16080  * @class Roo.Layer
16081  * @extends Roo.Element
16082  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16083  * automatic maintaining of shadow/shim positions.
16084  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16085  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16086  * you can pass a string with a CSS class name. False turns off the shadow.
16087  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16088  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16089  * @cfg {String} cls CSS class to add to the element
16090  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16091  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16092  * @constructor
16093  * @param {Object} config An object with config options.
16094  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16095  */
16096
16097 Roo.Layer = function(config, existingEl){
16098     config = config || {};
16099     var dh = Roo.DomHelper;
16100     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16101     if(existingEl){
16102         this.dom = Roo.getDom(existingEl);
16103     }
16104     if(!this.dom){
16105         var o = config.dh || {tag: "div", cls: "x-layer"};
16106         this.dom = dh.append(pel, o);
16107     }
16108     if(config.cls){
16109         this.addClass(config.cls);
16110     }
16111     this.constrain = config.constrain !== false;
16112     this.visibilityMode = Roo.Element.VISIBILITY;
16113     if(config.id){
16114         this.id = this.dom.id = config.id;
16115     }else{
16116         this.id = Roo.id(this.dom);
16117     }
16118     this.zindex = config.zindex || this.getZIndex();
16119     this.position("absolute", this.zindex);
16120     if(config.shadow){
16121         this.shadowOffset = config.shadowOffset || 4;
16122         this.shadow = new Roo.Shadow({
16123             offset : this.shadowOffset,
16124             mode : config.shadow
16125         });
16126     }else{
16127         this.shadowOffset = 0;
16128     }
16129     this.useShim = config.shim !== false && Roo.useShims;
16130     this.useDisplay = config.useDisplay;
16131     this.hide();
16132 };
16133
16134 var supr = Roo.Element.prototype;
16135
16136 // shims are shared among layer to keep from having 100 iframes
16137 var shims = [];
16138
16139 Roo.extend(Roo.Layer, Roo.Element, {
16140
16141     getZIndex : function(){
16142         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16143     },
16144
16145     getShim : function(){
16146         if(!this.useShim){
16147             return null;
16148         }
16149         if(this.shim){
16150             return this.shim;
16151         }
16152         var shim = shims.shift();
16153         if(!shim){
16154             shim = this.createShim();
16155             shim.enableDisplayMode('block');
16156             shim.dom.style.display = 'none';
16157             shim.dom.style.visibility = 'visible';
16158         }
16159         var pn = this.dom.parentNode;
16160         if(shim.dom.parentNode != pn){
16161             pn.insertBefore(shim.dom, this.dom);
16162         }
16163         shim.setStyle('z-index', this.getZIndex()-2);
16164         this.shim = shim;
16165         return shim;
16166     },
16167
16168     hideShim : function(){
16169         if(this.shim){
16170             this.shim.setDisplayed(false);
16171             shims.push(this.shim);
16172             delete this.shim;
16173         }
16174     },
16175
16176     disableShadow : function(){
16177         if(this.shadow){
16178             this.shadowDisabled = true;
16179             this.shadow.hide();
16180             this.lastShadowOffset = this.shadowOffset;
16181             this.shadowOffset = 0;
16182         }
16183     },
16184
16185     enableShadow : function(show){
16186         if(this.shadow){
16187             this.shadowDisabled = false;
16188             this.shadowOffset = this.lastShadowOffset;
16189             delete this.lastShadowOffset;
16190             if(show){
16191                 this.sync(true);
16192             }
16193         }
16194     },
16195
16196     // private
16197     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16198     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16199     sync : function(doShow){
16200         var sw = this.shadow;
16201         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16202             var sh = this.getShim();
16203
16204             var w = this.getWidth(),
16205                 h = this.getHeight();
16206
16207             var l = this.getLeft(true),
16208                 t = this.getTop(true);
16209
16210             if(sw && !this.shadowDisabled){
16211                 if(doShow && !sw.isVisible()){
16212                     sw.show(this);
16213                 }else{
16214                     sw.realign(l, t, w, h);
16215                 }
16216                 if(sh){
16217                     if(doShow){
16218                        sh.show();
16219                     }
16220                     // fit the shim behind the shadow, so it is shimmed too
16221                     var a = sw.adjusts, s = sh.dom.style;
16222                     s.left = (Math.min(l, l+a.l))+"px";
16223                     s.top = (Math.min(t, t+a.t))+"px";
16224                     s.width = (w+a.w)+"px";
16225                     s.height = (h+a.h)+"px";
16226                 }
16227             }else if(sh){
16228                 if(doShow){
16229                    sh.show();
16230                 }
16231                 sh.setSize(w, h);
16232                 sh.setLeftTop(l, t);
16233             }
16234             
16235         }
16236     },
16237
16238     // private
16239     destroy : function(){
16240         this.hideShim();
16241         if(this.shadow){
16242             this.shadow.hide();
16243         }
16244         this.removeAllListeners();
16245         var pn = this.dom.parentNode;
16246         if(pn){
16247             pn.removeChild(this.dom);
16248         }
16249         Roo.Element.uncache(this.id);
16250     },
16251
16252     remove : function(){
16253         this.destroy();
16254     },
16255
16256     // private
16257     beginUpdate : function(){
16258         this.updating = true;
16259     },
16260
16261     // private
16262     endUpdate : function(){
16263         this.updating = false;
16264         this.sync(true);
16265     },
16266
16267     // private
16268     hideUnders : function(negOffset){
16269         if(this.shadow){
16270             this.shadow.hide();
16271         }
16272         this.hideShim();
16273     },
16274
16275     // private
16276     constrainXY : function(){
16277         if(this.constrain){
16278             var vw = Roo.lib.Dom.getViewWidth(),
16279                 vh = Roo.lib.Dom.getViewHeight();
16280             var s = Roo.get(document).getScroll();
16281
16282             var xy = this.getXY();
16283             var x = xy[0], y = xy[1];   
16284             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16285             // only move it if it needs it
16286             var moved = false;
16287             // first validate right/bottom
16288             if((x + w) > vw+s.left){
16289                 x = vw - w - this.shadowOffset;
16290                 moved = true;
16291             }
16292             if((y + h) > vh+s.top){
16293                 y = vh - h - this.shadowOffset;
16294                 moved = true;
16295             }
16296             // then make sure top/left isn't negative
16297             if(x < s.left){
16298                 x = s.left;
16299                 moved = true;
16300             }
16301             if(y < s.top){
16302                 y = s.top;
16303                 moved = true;
16304             }
16305             if(moved){
16306                 if(this.avoidY){
16307                     var ay = this.avoidY;
16308                     if(y <= ay && (y+h) >= ay){
16309                         y = ay-h-5;   
16310                     }
16311                 }
16312                 xy = [x, y];
16313                 this.storeXY(xy);
16314                 supr.setXY.call(this, xy);
16315                 this.sync();
16316             }
16317         }
16318     },
16319
16320     isVisible : function(){
16321         return this.visible;    
16322     },
16323
16324     // private
16325     showAction : function(){
16326         this.visible = true; // track visibility to prevent getStyle calls
16327         if(this.useDisplay === true){
16328             this.setDisplayed("");
16329         }else if(this.lastXY){
16330             supr.setXY.call(this, this.lastXY);
16331         }else if(this.lastLT){
16332             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16333         }
16334     },
16335
16336     // private
16337     hideAction : function(){
16338         this.visible = false;
16339         if(this.useDisplay === true){
16340             this.setDisplayed(false);
16341         }else{
16342             this.setLeftTop(-10000,-10000);
16343         }
16344     },
16345
16346     // overridden Element method
16347     setVisible : function(v, a, d, c, e){
16348         if(v){
16349             this.showAction();
16350         }
16351         if(a && v){
16352             var cb = function(){
16353                 this.sync(true);
16354                 if(c){
16355                     c();
16356                 }
16357             }.createDelegate(this);
16358             supr.setVisible.call(this, true, true, d, cb, e);
16359         }else{
16360             if(!v){
16361                 this.hideUnders(true);
16362             }
16363             var cb = c;
16364             if(a){
16365                 cb = function(){
16366                     this.hideAction();
16367                     if(c){
16368                         c();
16369                     }
16370                 }.createDelegate(this);
16371             }
16372             supr.setVisible.call(this, v, a, d, cb, e);
16373             if(v){
16374                 this.sync(true);
16375             }else if(!a){
16376                 this.hideAction();
16377             }
16378         }
16379     },
16380
16381     storeXY : function(xy){
16382         delete this.lastLT;
16383         this.lastXY = xy;
16384     },
16385
16386     storeLeftTop : function(left, top){
16387         delete this.lastXY;
16388         this.lastLT = [left, top];
16389     },
16390
16391     // private
16392     beforeFx : function(){
16393         this.beforeAction();
16394         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16395     },
16396
16397     // private
16398     afterFx : function(){
16399         Roo.Layer.superclass.afterFx.apply(this, arguments);
16400         this.sync(this.isVisible());
16401     },
16402
16403     // private
16404     beforeAction : function(){
16405         if(!this.updating && this.shadow){
16406             this.shadow.hide();
16407         }
16408     },
16409
16410     // overridden Element method
16411     setLeft : function(left){
16412         this.storeLeftTop(left, this.getTop(true));
16413         supr.setLeft.apply(this, arguments);
16414         this.sync();
16415     },
16416
16417     setTop : function(top){
16418         this.storeLeftTop(this.getLeft(true), top);
16419         supr.setTop.apply(this, arguments);
16420         this.sync();
16421     },
16422
16423     setLeftTop : function(left, top){
16424         this.storeLeftTop(left, top);
16425         supr.setLeftTop.apply(this, arguments);
16426         this.sync();
16427     },
16428
16429     setXY : function(xy, a, d, c, e){
16430         this.fixDisplay();
16431         this.beforeAction();
16432         this.storeXY(xy);
16433         var cb = this.createCB(c);
16434         supr.setXY.call(this, xy, a, d, cb, e);
16435         if(!a){
16436             cb();
16437         }
16438     },
16439
16440     // private
16441     createCB : function(c){
16442         var el = this;
16443         return function(){
16444             el.constrainXY();
16445             el.sync(true);
16446             if(c){
16447                 c();
16448             }
16449         };
16450     },
16451
16452     // overridden Element method
16453     setX : function(x, a, d, c, e){
16454         this.setXY([x, this.getY()], a, d, c, e);
16455     },
16456
16457     // overridden Element method
16458     setY : function(y, a, d, c, e){
16459         this.setXY([this.getX(), y], a, d, c, e);
16460     },
16461
16462     // overridden Element method
16463     setSize : function(w, h, a, d, c, e){
16464         this.beforeAction();
16465         var cb = this.createCB(c);
16466         supr.setSize.call(this, w, h, a, d, cb, e);
16467         if(!a){
16468             cb();
16469         }
16470     },
16471
16472     // overridden Element method
16473     setWidth : function(w, a, d, c, e){
16474         this.beforeAction();
16475         var cb = this.createCB(c);
16476         supr.setWidth.call(this, w, a, d, cb, e);
16477         if(!a){
16478             cb();
16479         }
16480     },
16481
16482     // overridden Element method
16483     setHeight : function(h, a, d, c, e){
16484         this.beforeAction();
16485         var cb = this.createCB(c);
16486         supr.setHeight.call(this, h, a, d, cb, e);
16487         if(!a){
16488             cb();
16489         }
16490     },
16491
16492     // overridden Element method
16493     setBounds : function(x, y, w, h, a, d, c, e){
16494         this.beforeAction();
16495         var cb = this.createCB(c);
16496         if(!a){
16497             this.storeXY([x, y]);
16498             supr.setXY.call(this, [x, y]);
16499             supr.setSize.call(this, w, h, a, d, cb, e);
16500             cb();
16501         }else{
16502             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16503         }
16504         return this;
16505     },
16506     
16507     /**
16508      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16509      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16510      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16511      * @param {Number} zindex The new z-index to set
16512      * @return {this} The Layer
16513      */
16514     setZIndex : function(zindex){
16515         this.zindex = zindex;
16516         this.setStyle("z-index", zindex + 2);
16517         if(this.shadow){
16518             this.shadow.setZIndex(zindex + 1);
16519         }
16520         if(this.shim){
16521             this.shim.setStyle("z-index", zindex);
16522         }
16523     }
16524 });
16525 })();/*
16526  * Original code for Roojs - LGPL
16527  * <script type="text/javascript">
16528  */
16529  
16530 /**
16531  * @class Roo.XComponent
16532  * A delayed Element creator...
16533  * Or a way to group chunks of interface together.
16534  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16535  *  used in conjunction with XComponent.build() it will create an instance of each element,
16536  *  then call addxtype() to build the User interface.
16537  * 
16538  * Mypart.xyx = new Roo.XComponent({
16539
16540     parent : 'Mypart.xyz', // empty == document.element.!!
16541     order : '001',
16542     name : 'xxxx'
16543     region : 'xxxx'
16544     disabled : function() {} 
16545      
16546     tree : function() { // return an tree of xtype declared components
16547         var MODULE = this;
16548         return 
16549         {
16550             xtype : 'NestedLayoutPanel',
16551             // technicall
16552         }
16553      ]
16554  *})
16555  *
16556  *
16557  * It can be used to build a big heiracy, with parent etc.
16558  * or you can just use this to render a single compoent to a dom element
16559  * MYPART.render(Roo.Element | String(id) | dom_element )
16560  *
16561  *
16562  * Usage patterns.
16563  *
16564  * Classic Roo
16565  *
16566  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16567  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16568  *
16569  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16570  *
16571  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16572  * - if mulitple topModules exist, the last one is defined as the top module.
16573  *
16574  * Embeded Roo
16575  * 
16576  * When the top level or multiple modules are to embedded into a existing HTML page,
16577  * the parent element can container '#id' of the element where the module will be drawn.
16578  *
16579  * Bootstrap Roo
16580  *
16581  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16582  * it relies more on a include mechanism, where sub modules are included into an outer page.
16583  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16584  * 
16585  * Bootstrap Roo Included elements
16586  *
16587  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16588  * hence confusing the component builder as it thinks there are multiple top level elements. 
16589  *
16590  * String Over-ride & Translations
16591  *
16592  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16593  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16594  * are needed. @see Roo.XComponent.overlayString  
16595  * 
16596  * 
16597  * 
16598  * @extends Roo.util.Observable
16599  * @constructor
16600  * @param cfg {Object} configuration of component
16601  * 
16602  */
16603 Roo.XComponent = function(cfg) {
16604     Roo.apply(this, cfg);
16605     this.addEvents({ 
16606         /**
16607              * @event built
16608              * Fires when this the componnt is built
16609              * @param {Roo.XComponent} c the component
16610              */
16611         'built' : true
16612         
16613     });
16614     this.region = this.region || 'center'; // default..
16615     Roo.XComponent.register(this);
16616     this.modules = false;
16617     this.el = false; // where the layout goes..
16618     
16619     
16620 }
16621 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16622     /**
16623      * @property el
16624      * The created element (with Roo.factory())
16625      * @type {Roo.Layout}
16626      */
16627     el  : false,
16628     
16629     /**
16630      * @property el
16631      * for BC  - use el in new code
16632      * @type {Roo.Layout}
16633      */
16634     panel : false,
16635     
16636     /**
16637      * @property layout
16638      * for BC  - use el in new code
16639      * @type {Roo.Layout}
16640      */
16641     layout : false,
16642     
16643      /**
16644      * @cfg {Function|boolean} disabled
16645      * If this module is disabled by some rule, return true from the funtion
16646      */
16647     disabled : false,
16648     
16649     /**
16650      * @cfg {String} parent 
16651      * Name of parent element which it get xtype added to..
16652      */
16653     parent: false,
16654     
16655     /**
16656      * @cfg {String} order
16657      * Used to set the order in which elements are created (usefull for multiple tabs)
16658      */
16659     
16660     order : false,
16661     /**
16662      * @cfg {String} name
16663      * String to display while loading.
16664      */
16665     name : false,
16666     /**
16667      * @cfg {String} region
16668      * Region to render component to (defaults to center)
16669      */
16670     region : 'center',
16671     
16672     /**
16673      * @cfg {Array} items
16674      * A single item array - the first element is the root of the tree..
16675      * It's done this way to stay compatible with the Xtype system...
16676      */
16677     items : false,
16678     
16679     /**
16680      * @property _tree
16681      * The method that retuns the tree of parts that make up this compoennt 
16682      * @type {function}
16683      */
16684     _tree  : false,
16685     
16686      /**
16687      * render
16688      * render element to dom or tree
16689      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16690      */
16691     
16692     render : function(el)
16693     {
16694         
16695         el = el || false;
16696         var hp = this.parent ? 1 : 0;
16697         Roo.debug &&  Roo.log(this);
16698         
16699         var tree = this._tree ? this._tree() : this.tree();
16700
16701         
16702         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16703             // if parent is a '#.....' string, then let's use that..
16704             var ename = this.parent.substr(1);
16705             this.parent = false;
16706             Roo.debug && Roo.log(ename);
16707             switch (ename) {
16708                 case 'bootstrap-body':
16709                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16710                         // this is the BorderLayout standard?
16711                        this.parent = { el : true };
16712                        break;
16713                     }
16714                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16715                         // need to insert stuff...
16716                         this.parent =  {
16717                              el : new Roo.bootstrap.layout.Border({
16718                                  el : document.body, 
16719                      
16720                                  center: {
16721                                     titlebar: false,
16722                                     autoScroll:false,
16723                                     closeOnTab: true,
16724                                     tabPosition: 'top',
16725                                       //resizeTabs: true,
16726                                     alwaysShowTabs: true,
16727                                     hideTabs: false
16728                                      //minTabWidth: 140
16729                                  }
16730                              })
16731                         
16732                          };
16733                          break;
16734                     }
16735                          
16736                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16737                         this.parent = { el :  new  Roo.bootstrap.Body() };
16738                         Roo.debug && Roo.log("setting el to doc body");
16739                          
16740                     } else {
16741                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16742                     }
16743                     break;
16744                 case 'bootstrap':
16745                     this.parent = { el : true};
16746                     // fall through
16747                 default:
16748                     el = Roo.get(ename);
16749                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16750                         this.parent = { el : true};
16751                     }
16752                     
16753                     break;
16754             }
16755                 
16756             
16757             if (!el && !this.parent) {
16758                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16759                 return;
16760             }
16761         }
16762         
16763         Roo.debug && Roo.log("EL:");
16764         Roo.debug && Roo.log(el);
16765         Roo.debug && Roo.log("this.parent.el:");
16766         Roo.debug && Roo.log(this.parent.el);
16767         
16768
16769         // altertive root elements ??? - we need a better way to indicate these.
16770         var is_alt = Roo.XComponent.is_alt ||
16771                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16772                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16773                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16774         
16775         
16776         
16777         if (!this.parent && is_alt) {
16778             //el = Roo.get(document.body);
16779             this.parent = { el : true };
16780         }
16781             
16782             
16783         
16784         if (!this.parent) {
16785             
16786             Roo.debug && Roo.log("no parent - creating one");
16787             
16788             el = el ? Roo.get(el) : false;      
16789             
16790             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16791                 
16792                 this.parent =  {
16793                     el : new Roo.bootstrap.layout.Border({
16794                         el: el || document.body,
16795                     
16796                         center: {
16797                             titlebar: false,
16798                             autoScroll:false,
16799                             closeOnTab: true,
16800                             tabPosition: 'top',
16801                              //resizeTabs: true,
16802                             alwaysShowTabs: false,
16803                             hideTabs: true,
16804                             minTabWidth: 140,
16805                             overflow: 'visible'
16806                          }
16807                      })
16808                 };
16809             } else {
16810             
16811                 // it's a top level one..
16812                 this.parent =  {
16813                     el : new Roo.BorderLayout(el || document.body, {
16814                         center: {
16815                             titlebar: false,
16816                             autoScroll:false,
16817                             closeOnTab: true,
16818                             tabPosition: 'top',
16819                              //resizeTabs: true,
16820                             alwaysShowTabs: el && hp? false :  true,
16821                             hideTabs: el || !hp ? true :  false,
16822                             minTabWidth: 140
16823                          }
16824                     })
16825                 };
16826             }
16827         }
16828         
16829         if (!this.parent.el) {
16830                 // probably an old style ctor, which has been disabled.
16831                 return;
16832
16833         }
16834                 // The 'tree' method is  '_tree now' 
16835             
16836         tree.region = tree.region || this.region;
16837         var is_body = false;
16838         if (this.parent.el === true) {
16839             // bootstrap... - body..
16840             if (el) {
16841                 tree.el = el;
16842             }
16843             this.parent.el = Roo.factory(tree);
16844             is_body = true;
16845         }
16846         
16847         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16848         this.fireEvent('built', this);
16849         
16850         this.panel = this.el;
16851         this.layout = this.panel.layout;
16852         this.parentLayout = this.parent.layout  || false;  
16853          
16854     }
16855     
16856 });
16857
16858 Roo.apply(Roo.XComponent, {
16859     /**
16860      * @property  hideProgress
16861      * true to disable the building progress bar.. usefull on single page renders.
16862      * @type Boolean
16863      */
16864     hideProgress : false,
16865     /**
16866      * @property  buildCompleted
16867      * True when the builder has completed building the interface.
16868      * @type Boolean
16869      */
16870     buildCompleted : false,
16871      
16872     /**
16873      * @property  topModule
16874      * the upper most module - uses document.element as it's constructor.
16875      * @type Object
16876      */
16877      
16878     topModule  : false,
16879       
16880     /**
16881      * @property  modules
16882      * array of modules to be created by registration system.
16883      * @type {Array} of Roo.XComponent
16884      */
16885     
16886     modules : [],
16887     /**
16888      * @property  elmodules
16889      * array of modules to be created by which use #ID 
16890      * @type {Array} of Roo.XComponent
16891      */
16892      
16893     elmodules : [],
16894
16895      /**
16896      * @property  is_alt
16897      * Is an alternative Root - normally used by bootstrap or other systems,
16898      *    where the top element in the tree can wrap 'body' 
16899      * @type {boolean}  (default false)
16900      */
16901      
16902     is_alt : false,
16903     /**
16904      * @property  build_from_html
16905      * Build elements from html - used by bootstrap HTML stuff 
16906      *    - this is cleared after build is completed
16907      * @type {boolean}    (default false)
16908      */
16909      
16910     build_from_html : false,
16911     /**
16912      * Register components to be built later.
16913      *
16914      * This solves the following issues
16915      * - Building is not done on page load, but after an authentication process has occured.
16916      * - Interface elements are registered on page load
16917      * - Parent Interface elements may not be loaded before child, so this handles that..
16918      * 
16919      *
16920      * example:
16921      * 
16922      * MyApp.register({
16923           order : '000001',
16924           module : 'Pman.Tab.projectMgr',
16925           region : 'center',
16926           parent : 'Pman.layout',
16927           disabled : false,  // or use a function..
16928         })
16929      
16930      * * @param {Object} details about module
16931      */
16932     register : function(obj) {
16933                 
16934         Roo.XComponent.event.fireEvent('register', obj);
16935         switch(typeof(obj.disabled) ) {
16936                 
16937             case 'undefined':
16938                 break;
16939             
16940             case 'function':
16941                 if ( obj.disabled() ) {
16942                         return;
16943                 }
16944                 break;
16945             
16946             default:
16947                 if (obj.disabled || obj.region == '#disabled') {
16948                         return;
16949                 }
16950                 break;
16951         }
16952                 
16953         this.modules.push(obj);
16954          
16955     },
16956     /**
16957      * convert a string to an object..
16958      * eg. 'AAA.BBB' -> finds AAA.BBB
16959
16960      */
16961     
16962     toObject : function(str)
16963     {
16964         if (!str || typeof(str) == 'object') {
16965             return str;
16966         }
16967         if (str.substring(0,1) == '#') {
16968             return str;
16969         }
16970
16971         var ar = str.split('.');
16972         var rt, o;
16973         rt = ar.shift();
16974             /** eval:var:o */
16975         try {
16976             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16977         } catch (e) {
16978             throw "Module not found : " + str;
16979         }
16980         
16981         if (o === false) {
16982             throw "Module not found : " + str;
16983         }
16984         Roo.each(ar, function(e) {
16985             if (typeof(o[e]) == 'undefined') {
16986                 throw "Module not found : " + str;
16987             }
16988             o = o[e];
16989         });
16990         
16991         return o;
16992         
16993     },
16994     
16995     
16996     /**
16997      * move modules into their correct place in the tree..
16998      * 
16999      */
17000     preBuild : function ()
17001     {
17002         var _t = this;
17003         Roo.each(this.modules , function (obj)
17004         {
17005             Roo.XComponent.event.fireEvent('beforebuild', obj);
17006             
17007             var opar = obj.parent;
17008             try { 
17009                 obj.parent = this.toObject(opar);
17010             } catch(e) {
17011                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17012                 return;
17013             }
17014             
17015             if (!obj.parent) {
17016                 Roo.debug && Roo.log("GOT top level module");
17017                 Roo.debug && Roo.log(obj);
17018                 obj.modules = new Roo.util.MixedCollection(false, 
17019                     function(o) { return o.order + '' }
17020                 );
17021                 this.topModule = obj;
17022                 return;
17023             }
17024                         // parent is a string (usually a dom element name..)
17025             if (typeof(obj.parent) == 'string') {
17026                 this.elmodules.push(obj);
17027                 return;
17028             }
17029             if (obj.parent.constructor != Roo.XComponent) {
17030                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17031             }
17032             if (!obj.parent.modules) {
17033                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17034                     function(o) { return o.order + '' }
17035                 );
17036             }
17037             if (obj.parent.disabled) {
17038                 obj.disabled = true;
17039             }
17040             obj.parent.modules.add(obj);
17041         }, this);
17042     },
17043     
17044      /**
17045      * make a list of modules to build.
17046      * @return {Array} list of modules. 
17047      */ 
17048     
17049     buildOrder : function()
17050     {
17051         var _this = this;
17052         var cmp = function(a,b) {   
17053             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17054         };
17055         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17056             throw "No top level modules to build";
17057         }
17058         
17059         // make a flat list in order of modules to build.
17060         var mods = this.topModule ? [ this.topModule ] : [];
17061                 
17062         
17063         // elmodules (is a list of DOM based modules )
17064         Roo.each(this.elmodules, function(e) {
17065             mods.push(e);
17066             if (!this.topModule &&
17067                 typeof(e.parent) == 'string' &&
17068                 e.parent.substring(0,1) == '#' &&
17069                 Roo.get(e.parent.substr(1))
17070                ) {
17071                 
17072                 _this.topModule = e;
17073             }
17074             
17075         });
17076
17077         
17078         // add modules to their parents..
17079         var addMod = function(m) {
17080             Roo.debug && Roo.log("build Order: add: " + m.name);
17081                 
17082             mods.push(m);
17083             if (m.modules && !m.disabled) {
17084                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17085                 m.modules.keySort('ASC',  cmp );
17086                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17087     
17088                 m.modules.each(addMod);
17089             } else {
17090                 Roo.debug && Roo.log("build Order: no child modules");
17091             }
17092             // not sure if this is used any more..
17093             if (m.finalize) {
17094                 m.finalize.name = m.name + " (clean up) ";
17095                 mods.push(m.finalize);
17096             }
17097             
17098         }
17099         if (this.topModule && this.topModule.modules) { 
17100             this.topModule.modules.keySort('ASC',  cmp );
17101             this.topModule.modules.each(addMod);
17102         } 
17103         return mods;
17104     },
17105     
17106      /**
17107      * Build the registered modules.
17108      * @param {Object} parent element.
17109      * @param {Function} optional method to call after module has been added.
17110      * 
17111      */ 
17112    
17113     build : function(opts) 
17114     {
17115         
17116         if (typeof(opts) != 'undefined') {
17117             Roo.apply(this,opts);
17118         }
17119         
17120         this.preBuild();
17121         var mods = this.buildOrder();
17122       
17123         //this.allmods = mods;
17124         //Roo.debug && Roo.log(mods);
17125         //return;
17126         if (!mods.length) { // should not happen
17127             throw "NO modules!!!";
17128         }
17129         
17130         
17131         var msg = "Building Interface...";
17132         // flash it up as modal - so we store the mask!?
17133         if (!this.hideProgress && Roo.MessageBox) {
17134             Roo.MessageBox.show({ title: 'loading' });
17135             Roo.MessageBox.show({
17136                title: "Please wait...",
17137                msg: msg,
17138                width:450,
17139                progress:true,
17140                buttons : false,
17141                closable:false,
17142                modal: false
17143               
17144             });
17145         }
17146         var total = mods.length;
17147         
17148         var _this = this;
17149         var progressRun = function() {
17150             if (!mods.length) {
17151                 Roo.debug && Roo.log('hide?');
17152                 if (!this.hideProgress && Roo.MessageBox) {
17153                     Roo.MessageBox.hide();
17154                 }
17155                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17156                 
17157                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17158                 
17159                 // THE END...
17160                 return false;   
17161             }
17162             
17163             var m = mods.shift();
17164             
17165             
17166             Roo.debug && Roo.log(m);
17167             // not sure if this is supported any more.. - modules that are are just function
17168             if (typeof(m) == 'function') { 
17169                 m.call(this);
17170                 return progressRun.defer(10, _this);
17171             } 
17172             
17173             
17174             msg = "Building Interface " + (total  - mods.length) + 
17175                     " of " + total + 
17176                     (m.name ? (' - ' + m.name) : '');
17177                         Roo.debug && Roo.log(msg);
17178             if (!_this.hideProgress &&  Roo.MessageBox) { 
17179                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17180             }
17181             
17182          
17183             // is the module disabled?
17184             var disabled = (typeof(m.disabled) == 'function') ?
17185                 m.disabled.call(m.module.disabled) : m.disabled;    
17186             
17187             
17188             if (disabled) {
17189                 return progressRun(); // we do not update the display!
17190             }
17191             
17192             // now build 
17193             
17194                         
17195                         
17196             m.render();
17197             // it's 10 on top level, and 1 on others??? why...
17198             return progressRun.defer(10, _this);
17199              
17200         }
17201         progressRun.defer(1, _this);
17202      
17203         
17204         
17205     },
17206     /**
17207      * Overlay a set of modified strings onto a component
17208      * This is dependant on our builder exporting the strings and 'named strings' elements.
17209      * 
17210      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17211      * @param {Object} associative array of 'named' string and it's new value.
17212      * 
17213      */
17214         overlayStrings : function( component, strings )
17215     {
17216         if (typeof(component['_named_strings']) == 'undefined') {
17217             throw "ERROR: component does not have _named_strings";
17218         }
17219         for ( var k in strings ) {
17220             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17221             if (md !== false) {
17222                 component['_strings'][md] = strings[k];
17223             } else {
17224                 Roo.log('could not find named string: ' + k + ' in');
17225                 Roo.log(component);
17226             }
17227             
17228         }
17229         
17230     },
17231     
17232         
17233         /**
17234          * Event Object.
17235          *
17236          *
17237          */
17238         event: false, 
17239     /**
17240          * wrapper for event.on - aliased later..  
17241          * Typically use to register a event handler for register:
17242          *
17243          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17244          *
17245          */
17246     on : false
17247    
17248     
17249     
17250 });
17251
17252 Roo.XComponent.event = new Roo.util.Observable({
17253                 events : { 
17254                         /**
17255                          * @event register
17256                          * Fires when an Component is registered,
17257                          * set the disable property on the Component to stop registration.
17258                          * @param {Roo.XComponent} c the component being registerd.
17259                          * 
17260                          */
17261                         'register' : true,
17262             /**
17263                          * @event beforebuild
17264                          * Fires before each Component is built
17265                          * can be used to apply permissions.
17266                          * @param {Roo.XComponent} c the component being registerd.
17267                          * 
17268                          */
17269                         'beforebuild' : true,
17270                         /**
17271                          * @event buildcomplete
17272                          * Fires on the top level element when all elements have been built
17273                          * @param {Roo.XComponent} the top level component.
17274                          */
17275                         'buildcomplete' : true
17276                         
17277                 }
17278 });
17279
17280 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17281  //
17282  /**
17283  * marked - a markdown parser
17284  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17285  * https://github.com/chjj/marked
17286  */
17287
17288
17289 /**
17290  *
17291  * Roo.Markdown - is a very crude wrapper around marked..
17292  *
17293  * usage:
17294  * 
17295  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17296  * 
17297  * Note: move the sample code to the bottom of this
17298  * file before uncommenting it.
17299  *
17300  */
17301
17302 Roo.Markdown = {};
17303 Roo.Markdown.toHtml = function(text) {
17304     
17305     var c = new Roo.Markdown.marked.setOptions({
17306             renderer: new Roo.Markdown.marked.Renderer(),
17307             gfm: true,
17308             tables: true,
17309             breaks: false,
17310             pedantic: false,
17311             sanitize: false,
17312             smartLists: true,
17313             smartypants: false
17314           });
17315     // A FEW HACKS!!?
17316     
17317     text = text.replace(/\\\n/g,' ');
17318     return Roo.Markdown.marked(text);
17319 };
17320 //
17321 // converter
17322 //
17323 // Wraps all "globals" so that the only thing
17324 // exposed is makeHtml().
17325 //
17326 (function() {
17327     
17328      /**
17329          * eval:var:escape
17330          * eval:var:unescape
17331          * eval:var:replace
17332          */
17333       
17334     /**
17335      * Helpers
17336      */
17337     
17338     var escape = function (html, encode) {
17339       return html
17340         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17341         .replace(/</g, '&lt;')
17342         .replace(/>/g, '&gt;')
17343         .replace(/"/g, '&quot;')
17344         .replace(/'/g, '&#39;');
17345     }
17346     
17347     var unescape = function (html) {
17348         // explicitly match decimal, hex, and named HTML entities 
17349       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17350         n = n.toLowerCase();
17351         if (n === 'colon') { return ':'; }
17352         if (n.charAt(0) === '#') {
17353           return n.charAt(1) === 'x'
17354             ? String.fromCharCode(parseInt(n.substring(2), 16))
17355             : String.fromCharCode(+n.substring(1));
17356         }
17357         return '';
17358       });
17359     }
17360     
17361     var replace = function (regex, opt) {
17362       regex = regex.source;
17363       opt = opt || '';
17364       return function self(name, val) {
17365         if (!name) { return new RegExp(regex, opt); }
17366         val = val.source || val;
17367         val = val.replace(/(^|[^\[])\^/g, '$1');
17368         regex = regex.replace(name, val);
17369         return self;
17370       };
17371     }
17372
17373
17374          /**
17375          * eval:var:noop
17376     */
17377     var noop = function () {}
17378     noop.exec = noop;
17379     
17380          /**
17381          * eval:var:merge
17382     */
17383     var merge = function (obj) {
17384       var i = 1
17385         , target
17386         , key;
17387     
17388       for (; i < arguments.length; i++) {
17389         target = arguments[i];
17390         for (key in target) {
17391           if (Object.prototype.hasOwnProperty.call(target, key)) {
17392             obj[key] = target[key];
17393           }
17394         }
17395       }
17396     
17397       return obj;
17398     }
17399     
17400     
17401     /**
17402      * Block-Level Grammar
17403      */
17404     
17405     
17406     
17407     
17408     var block = {
17409       newline: /^\n+/,
17410       code: /^( {4}[^\n]+\n*)+/,
17411       fences: noop,
17412       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17413       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17414       nptable: noop,
17415       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17416       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17417       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17418       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17419       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17420       table: noop,
17421       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17422       text: /^[^\n]+/
17423     };
17424     
17425     block.bullet = /(?:[*+-]|\d+\.)/;
17426     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17427     block.item = replace(block.item, 'gm')
17428       (/bull/g, block.bullet)
17429       ();
17430     
17431     block.list = replace(block.list)
17432       (/bull/g, block.bullet)
17433       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17434       ('def', '\\n+(?=' + block.def.source + ')')
17435       ();
17436     
17437     block.blockquote = replace(block.blockquote)
17438       ('def', block.def)
17439       ();
17440     
17441     block._tag = '(?!(?:'
17442       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17443       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17444       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17445     
17446     block.html = replace(block.html)
17447       ('comment', /<!--[\s\S]*?-->/)
17448       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17449       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17450       (/tag/g, block._tag)
17451       ();
17452     
17453     block.paragraph = replace(block.paragraph)
17454       ('hr', block.hr)
17455       ('heading', block.heading)
17456       ('lheading', block.lheading)
17457       ('blockquote', block.blockquote)
17458       ('tag', '<' + block._tag)
17459       ('def', block.def)
17460       ();
17461     
17462     /**
17463      * Normal Block Grammar
17464      */
17465     
17466     block.normal = merge({}, block);
17467     
17468     /**
17469      * GFM Block Grammar
17470      */
17471     
17472     block.gfm = merge({}, block.normal, {
17473       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17474       paragraph: /^/,
17475       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17476     });
17477     
17478     block.gfm.paragraph = replace(block.paragraph)
17479       ('(?!', '(?!'
17480         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17481         + block.list.source.replace('\\1', '\\3') + '|')
17482       ();
17483     
17484     /**
17485      * GFM + Tables Block Grammar
17486      */
17487     
17488     block.tables = merge({}, block.gfm, {
17489       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17490       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17491     });
17492     
17493     /**
17494      * Block Lexer
17495      */
17496     
17497     var Lexer = function (options) {
17498       this.tokens = [];
17499       this.tokens.links = {};
17500       this.options = options || marked.defaults;
17501       this.rules = block.normal;
17502     
17503       if (this.options.gfm) {
17504         if (this.options.tables) {
17505           this.rules = block.tables;
17506         } else {
17507           this.rules = block.gfm;
17508         }
17509       }
17510     }
17511     
17512     /**
17513      * Expose Block Rules
17514      */
17515     
17516     Lexer.rules = block;
17517     
17518     /**
17519      * Static Lex Method
17520      */
17521     
17522     Lexer.lex = function(src, options) {
17523       var lexer = new Lexer(options);
17524       return lexer.lex(src);
17525     };
17526     
17527     /**
17528      * Preprocessing
17529      */
17530     
17531     Lexer.prototype.lex = function(src) {
17532       src = src
17533         .replace(/\r\n|\r/g, '\n')
17534         .replace(/\t/g, '    ')
17535         .replace(/\u00a0/g, ' ')
17536         .replace(/\u2424/g, '\n');
17537     
17538       return this.token(src, true);
17539     };
17540     
17541     /**
17542      * Lexing
17543      */
17544     
17545     Lexer.prototype.token = function(src, top, bq) {
17546       var src = src.replace(/^ +$/gm, '')
17547         , next
17548         , loose
17549         , cap
17550         , bull
17551         , b
17552         , item
17553         , space
17554         , i
17555         , l;
17556     
17557       while (src) {
17558         // newline
17559         if (cap = this.rules.newline.exec(src)) {
17560           src = src.substring(cap[0].length);
17561           if (cap[0].length > 1) {
17562             this.tokens.push({
17563               type: 'space'
17564             });
17565           }
17566         }
17567     
17568         // code
17569         if (cap = this.rules.code.exec(src)) {
17570           src = src.substring(cap[0].length);
17571           cap = cap[0].replace(/^ {4}/gm, '');
17572           this.tokens.push({
17573             type: 'code',
17574             text: !this.options.pedantic
17575               ? cap.replace(/\n+$/, '')
17576               : cap
17577           });
17578           continue;
17579         }
17580     
17581         // fences (gfm)
17582         if (cap = this.rules.fences.exec(src)) {
17583           src = src.substring(cap[0].length);
17584           this.tokens.push({
17585             type: 'code',
17586             lang: cap[2],
17587             text: cap[3] || ''
17588           });
17589           continue;
17590         }
17591     
17592         // heading
17593         if (cap = this.rules.heading.exec(src)) {
17594           src = src.substring(cap[0].length);
17595           this.tokens.push({
17596             type: 'heading',
17597             depth: cap[1].length,
17598             text: cap[2]
17599           });
17600           continue;
17601         }
17602     
17603         // table no leading pipe (gfm)
17604         if (top && (cap = this.rules.nptable.exec(src))) {
17605           src = src.substring(cap[0].length);
17606     
17607           item = {
17608             type: 'table',
17609             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17610             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17611             cells: cap[3].replace(/\n$/, '').split('\n')
17612           };
17613     
17614           for (i = 0; i < item.align.length; i++) {
17615             if (/^ *-+: *$/.test(item.align[i])) {
17616               item.align[i] = 'right';
17617             } else if (/^ *:-+: *$/.test(item.align[i])) {
17618               item.align[i] = 'center';
17619             } else if (/^ *:-+ *$/.test(item.align[i])) {
17620               item.align[i] = 'left';
17621             } else {
17622               item.align[i] = null;
17623             }
17624           }
17625     
17626           for (i = 0; i < item.cells.length; i++) {
17627             item.cells[i] = item.cells[i].split(/ *\| */);
17628           }
17629     
17630           this.tokens.push(item);
17631     
17632           continue;
17633         }
17634     
17635         // lheading
17636         if (cap = this.rules.lheading.exec(src)) {
17637           src = src.substring(cap[0].length);
17638           this.tokens.push({
17639             type: 'heading',
17640             depth: cap[2] === '=' ? 1 : 2,
17641             text: cap[1]
17642           });
17643           continue;
17644         }
17645     
17646         // hr
17647         if (cap = this.rules.hr.exec(src)) {
17648           src = src.substring(cap[0].length);
17649           this.tokens.push({
17650             type: 'hr'
17651           });
17652           continue;
17653         }
17654     
17655         // blockquote
17656         if (cap = this.rules.blockquote.exec(src)) {
17657           src = src.substring(cap[0].length);
17658     
17659           this.tokens.push({
17660             type: 'blockquote_start'
17661           });
17662     
17663           cap = cap[0].replace(/^ *> ?/gm, '');
17664     
17665           // Pass `top` to keep the current
17666           // "toplevel" state. This is exactly
17667           // how markdown.pl works.
17668           this.token(cap, top, true);
17669     
17670           this.tokens.push({
17671             type: 'blockquote_end'
17672           });
17673     
17674           continue;
17675         }
17676     
17677         // list
17678         if (cap = this.rules.list.exec(src)) {
17679           src = src.substring(cap[0].length);
17680           bull = cap[2];
17681     
17682           this.tokens.push({
17683             type: 'list_start',
17684             ordered: bull.length > 1
17685           });
17686     
17687           // Get each top-level item.
17688           cap = cap[0].match(this.rules.item);
17689     
17690           next = false;
17691           l = cap.length;
17692           i = 0;
17693     
17694           for (; i < l; i++) {
17695             item = cap[i];
17696     
17697             // Remove the list item's bullet
17698             // so it is seen as the next token.
17699             space = item.length;
17700             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17701     
17702             // Outdent whatever the
17703             // list item contains. Hacky.
17704             if (~item.indexOf('\n ')) {
17705               space -= item.length;
17706               item = !this.options.pedantic
17707                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17708                 : item.replace(/^ {1,4}/gm, '');
17709             }
17710     
17711             // Determine whether the next list item belongs here.
17712             // Backpedal if it does not belong in this list.
17713             if (this.options.smartLists && i !== l - 1) {
17714               b = block.bullet.exec(cap[i + 1])[0];
17715               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17716                 src = cap.slice(i + 1).join('\n') + src;
17717                 i = l - 1;
17718               }
17719             }
17720     
17721             // Determine whether item is loose or not.
17722             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17723             // for discount behavior.
17724             loose = next || /\n\n(?!\s*$)/.test(item);
17725             if (i !== l - 1) {
17726               next = item.charAt(item.length - 1) === '\n';
17727               if (!loose) { loose = next; }
17728             }
17729     
17730             this.tokens.push({
17731               type: loose
17732                 ? 'loose_item_start'
17733                 : 'list_item_start'
17734             });
17735     
17736             // Recurse.
17737             this.token(item, false, bq);
17738     
17739             this.tokens.push({
17740               type: 'list_item_end'
17741             });
17742           }
17743     
17744           this.tokens.push({
17745             type: 'list_end'
17746           });
17747     
17748           continue;
17749         }
17750     
17751         // html
17752         if (cap = this.rules.html.exec(src)) {
17753           src = src.substring(cap[0].length);
17754           this.tokens.push({
17755             type: this.options.sanitize
17756               ? 'paragraph'
17757               : 'html',
17758             pre: !this.options.sanitizer
17759               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17760             text: cap[0]
17761           });
17762           continue;
17763         }
17764     
17765         // def
17766         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17767           src = src.substring(cap[0].length);
17768           this.tokens.links[cap[1].toLowerCase()] = {
17769             href: cap[2],
17770             title: cap[3]
17771           };
17772           continue;
17773         }
17774     
17775         // table (gfm)
17776         if (top && (cap = this.rules.table.exec(src))) {
17777           src = src.substring(cap[0].length);
17778     
17779           item = {
17780             type: 'table',
17781             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17782             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17783             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17784           };
17785     
17786           for (i = 0; i < item.align.length; i++) {
17787             if (/^ *-+: *$/.test(item.align[i])) {
17788               item.align[i] = 'right';
17789             } else if (/^ *:-+: *$/.test(item.align[i])) {
17790               item.align[i] = 'center';
17791             } else if (/^ *:-+ *$/.test(item.align[i])) {
17792               item.align[i] = 'left';
17793             } else {
17794               item.align[i] = null;
17795             }
17796           }
17797     
17798           for (i = 0; i < item.cells.length; i++) {
17799             item.cells[i] = item.cells[i]
17800               .replace(/^ *\| *| *\| *$/g, '')
17801               .split(/ *\| */);
17802           }
17803     
17804           this.tokens.push(item);
17805     
17806           continue;
17807         }
17808     
17809         // top-level paragraph
17810         if (top && (cap = this.rules.paragraph.exec(src))) {
17811           src = src.substring(cap[0].length);
17812           this.tokens.push({
17813             type: 'paragraph',
17814             text: cap[1].charAt(cap[1].length - 1) === '\n'
17815               ? cap[1].slice(0, -1)
17816               : cap[1]
17817           });
17818           continue;
17819         }
17820     
17821         // text
17822         if (cap = this.rules.text.exec(src)) {
17823           // Top-level should never reach here.
17824           src = src.substring(cap[0].length);
17825           this.tokens.push({
17826             type: 'text',
17827             text: cap[0]
17828           });
17829           continue;
17830         }
17831     
17832         if (src) {
17833           throw new
17834             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17835         }
17836       }
17837     
17838       return this.tokens;
17839     };
17840     
17841     /**
17842      * Inline-Level Grammar
17843      */
17844     
17845     var inline = {
17846       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17847       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17848       url: noop,
17849       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17850       link: /^!?\[(inside)\]\(href\)/,
17851       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17852       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17853       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17854       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17855       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17856       br: /^ {2,}\n(?!\s*$)/,
17857       del: noop,
17858       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17859     };
17860     
17861     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17862     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17863     
17864     inline.link = replace(inline.link)
17865       ('inside', inline._inside)
17866       ('href', inline._href)
17867       ();
17868     
17869     inline.reflink = replace(inline.reflink)
17870       ('inside', inline._inside)
17871       ();
17872     
17873     /**
17874      * Normal Inline Grammar
17875      */
17876     
17877     inline.normal = merge({}, inline);
17878     
17879     /**
17880      * Pedantic Inline Grammar
17881      */
17882     
17883     inline.pedantic = merge({}, inline.normal, {
17884       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17885       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17886     });
17887     
17888     /**
17889      * GFM Inline Grammar
17890      */
17891     
17892     inline.gfm = merge({}, inline.normal, {
17893       escape: replace(inline.escape)('])', '~|])')(),
17894       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17895       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17896       text: replace(inline.text)
17897         (']|', '~]|')
17898         ('|', '|https?://|')
17899         ()
17900     });
17901     
17902     /**
17903      * GFM + Line Breaks Inline Grammar
17904      */
17905     
17906     inline.breaks = merge({}, inline.gfm, {
17907       br: replace(inline.br)('{2,}', '*')(),
17908       text: replace(inline.gfm.text)('{2,}', '*')()
17909     });
17910     
17911     /**
17912      * Inline Lexer & Compiler
17913      */
17914     
17915     var InlineLexer  = function (links, options) {
17916       this.options = options || marked.defaults;
17917       this.links = links;
17918       this.rules = inline.normal;
17919       this.renderer = this.options.renderer || new Renderer;
17920       this.renderer.options = this.options;
17921     
17922       if (!this.links) {
17923         throw new
17924           Error('Tokens array requires a `links` property.');
17925       }
17926     
17927       if (this.options.gfm) {
17928         if (this.options.breaks) {
17929           this.rules = inline.breaks;
17930         } else {
17931           this.rules = inline.gfm;
17932         }
17933       } else if (this.options.pedantic) {
17934         this.rules = inline.pedantic;
17935       }
17936     }
17937     
17938     /**
17939      * Expose Inline Rules
17940      */
17941     
17942     InlineLexer.rules = inline;
17943     
17944     /**
17945      * Static Lexing/Compiling Method
17946      */
17947     
17948     InlineLexer.output = function(src, links, options) {
17949       var inline = new InlineLexer(links, options);
17950       return inline.output(src);
17951     };
17952     
17953     /**
17954      * Lexing/Compiling
17955      */
17956     
17957     InlineLexer.prototype.output = function(src) {
17958       var out = ''
17959         , link
17960         , text
17961         , href
17962         , cap;
17963     
17964       while (src) {
17965         // escape
17966         if (cap = this.rules.escape.exec(src)) {
17967           src = src.substring(cap[0].length);
17968           out += cap[1];
17969           continue;
17970         }
17971     
17972         // autolink
17973         if (cap = this.rules.autolink.exec(src)) {
17974           src = src.substring(cap[0].length);
17975           if (cap[2] === '@') {
17976             text = cap[1].charAt(6) === ':'
17977               ? this.mangle(cap[1].substring(7))
17978               : this.mangle(cap[1]);
17979             href = this.mangle('mailto:') + text;
17980           } else {
17981             text = escape(cap[1]);
17982             href = text;
17983           }
17984           out += this.renderer.link(href, null, text);
17985           continue;
17986         }
17987     
17988         // url (gfm)
17989         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17990           src = src.substring(cap[0].length);
17991           text = escape(cap[1]);
17992           href = text;
17993           out += this.renderer.link(href, null, text);
17994           continue;
17995         }
17996     
17997         // tag
17998         if (cap = this.rules.tag.exec(src)) {
17999           if (!this.inLink && /^<a /i.test(cap[0])) {
18000             this.inLink = true;
18001           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18002             this.inLink = false;
18003           }
18004           src = src.substring(cap[0].length);
18005           out += this.options.sanitize
18006             ? this.options.sanitizer
18007               ? this.options.sanitizer(cap[0])
18008               : escape(cap[0])
18009             : cap[0];
18010           continue;
18011         }
18012     
18013         // link
18014         if (cap = this.rules.link.exec(src)) {
18015           src = src.substring(cap[0].length);
18016           this.inLink = true;
18017           out += this.outputLink(cap, {
18018             href: cap[2],
18019             title: cap[3]
18020           });
18021           this.inLink = false;
18022           continue;
18023         }
18024     
18025         // reflink, nolink
18026         if ((cap = this.rules.reflink.exec(src))
18027             || (cap = this.rules.nolink.exec(src))) {
18028           src = src.substring(cap[0].length);
18029           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18030           link = this.links[link.toLowerCase()];
18031           if (!link || !link.href) {
18032             out += cap[0].charAt(0);
18033             src = cap[0].substring(1) + src;
18034             continue;
18035           }
18036           this.inLink = true;
18037           out += this.outputLink(cap, link);
18038           this.inLink = false;
18039           continue;
18040         }
18041     
18042         // strong
18043         if (cap = this.rules.strong.exec(src)) {
18044           src = src.substring(cap[0].length);
18045           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18046           continue;
18047         }
18048     
18049         // em
18050         if (cap = this.rules.em.exec(src)) {
18051           src = src.substring(cap[0].length);
18052           out += this.renderer.em(this.output(cap[2] || cap[1]));
18053           continue;
18054         }
18055     
18056         // code
18057         if (cap = this.rules.code.exec(src)) {
18058           src = src.substring(cap[0].length);
18059           out += this.renderer.codespan(escape(cap[2], true));
18060           continue;
18061         }
18062     
18063         // br
18064         if (cap = this.rules.br.exec(src)) {
18065           src = src.substring(cap[0].length);
18066           out += this.renderer.br();
18067           continue;
18068         }
18069     
18070         // del (gfm)
18071         if (cap = this.rules.del.exec(src)) {
18072           src = src.substring(cap[0].length);
18073           out += this.renderer.del(this.output(cap[1]));
18074           continue;
18075         }
18076     
18077         // text
18078         if (cap = this.rules.text.exec(src)) {
18079           src = src.substring(cap[0].length);
18080           out += this.renderer.text(escape(this.smartypants(cap[0])));
18081           continue;
18082         }
18083     
18084         if (src) {
18085           throw new
18086             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18087         }
18088       }
18089     
18090       return out;
18091     };
18092     
18093     /**
18094      * Compile Link
18095      */
18096     
18097     InlineLexer.prototype.outputLink = function(cap, link) {
18098       var href = escape(link.href)
18099         , title = link.title ? escape(link.title) : null;
18100     
18101       return cap[0].charAt(0) !== '!'
18102         ? this.renderer.link(href, title, this.output(cap[1]))
18103         : this.renderer.image(href, title, escape(cap[1]));
18104     };
18105     
18106     /**
18107      * Smartypants Transformations
18108      */
18109     
18110     InlineLexer.prototype.smartypants = function(text) {
18111       if (!this.options.smartypants)  { return text; }
18112       return text
18113         // em-dashes
18114         .replace(/---/g, '\u2014')
18115         // en-dashes
18116         .replace(/--/g, '\u2013')
18117         // opening singles
18118         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18119         // closing singles & apostrophes
18120         .replace(/'/g, '\u2019')
18121         // opening doubles
18122         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18123         // closing doubles
18124         .replace(/"/g, '\u201d')
18125         // ellipses
18126         .replace(/\.{3}/g, '\u2026');
18127     };
18128     
18129     /**
18130      * Mangle Links
18131      */
18132     
18133     InlineLexer.prototype.mangle = function(text) {
18134       if (!this.options.mangle) { return text; }
18135       var out = ''
18136         , l = text.length
18137         , i = 0
18138         , ch;
18139     
18140       for (; i < l; i++) {
18141         ch = text.charCodeAt(i);
18142         if (Math.random() > 0.5) {
18143           ch = 'x' + ch.toString(16);
18144         }
18145         out += '&#' + ch + ';';
18146       }
18147     
18148       return out;
18149     };
18150     
18151     /**
18152      * Renderer
18153      */
18154     
18155      /**
18156          * eval:var:Renderer
18157     */
18158     
18159     var Renderer   = function (options) {
18160       this.options = options || {};
18161     }
18162     
18163     Renderer.prototype.code = function(code, lang, escaped) {
18164       if (this.options.highlight) {
18165         var out = this.options.highlight(code, lang);
18166         if (out != null && out !== code) {
18167           escaped = true;
18168           code = out;
18169         }
18170       } else {
18171             // hack!!! - it's already escapeD?
18172             escaped = true;
18173       }
18174     
18175       if (!lang) {
18176         return '<pre><code>'
18177           + (escaped ? code : escape(code, true))
18178           + '\n</code></pre>';
18179       }
18180     
18181       return '<pre><code class="'
18182         + this.options.langPrefix
18183         + escape(lang, true)
18184         + '">'
18185         + (escaped ? code : escape(code, true))
18186         + '\n</code></pre>\n';
18187     };
18188     
18189     Renderer.prototype.blockquote = function(quote) {
18190       return '<blockquote>\n' + quote + '</blockquote>\n';
18191     };
18192     
18193     Renderer.prototype.html = function(html) {
18194       return html;
18195     };
18196     
18197     Renderer.prototype.heading = function(text, level, raw) {
18198       return '<h'
18199         + level
18200         + ' id="'
18201         + this.options.headerPrefix
18202         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18203         + '">'
18204         + text
18205         + '</h'
18206         + level
18207         + '>\n';
18208     };
18209     
18210     Renderer.prototype.hr = function() {
18211       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18212     };
18213     
18214     Renderer.prototype.list = function(body, ordered) {
18215       var type = ordered ? 'ol' : 'ul';
18216       return '<' + type + '>\n' + body + '</' + type + '>\n';
18217     };
18218     
18219     Renderer.prototype.listitem = function(text) {
18220       return '<li>' + text + '</li>\n';
18221     };
18222     
18223     Renderer.prototype.paragraph = function(text) {
18224       return '<p>' + text + '</p>\n';
18225     };
18226     
18227     Renderer.prototype.table = function(header, body) {
18228       return '<table class="table table-striped">\n'
18229         + '<thead>\n'
18230         + header
18231         + '</thead>\n'
18232         + '<tbody>\n'
18233         + body
18234         + '</tbody>\n'
18235         + '</table>\n';
18236     };
18237     
18238     Renderer.prototype.tablerow = function(content) {
18239       return '<tr>\n' + content + '</tr>\n';
18240     };
18241     
18242     Renderer.prototype.tablecell = function(content, flags) {
18243       var type = flags.header ? 'th' : 'td';
18244       var tag = flags.align
18245         ? '<' + type + ' style="text-align:' + flags.align + '">'
18246         : '<' + type + '>';
18247       return tag + content + '</' + type + '>\n';
18248     };
18249     
18250     // span level renderer
18251     Renderer.prototype.strong = function(text) {
18252       return '<strong>' + text + '</strong>';
18253     };
18254     
18255     Renderer.prototype.em = function(text) {
18256       return '<em>' + text + '</em>';
18257     };
18258     
18259     Renderer.prototype.codespan = function(text) {
18260       return '<code>' + text + '</code>';
18261     };
18262     
18263     Renderer.prototype.br = function() {
18264       return this.options.xhtml ? '<br/>' : '<br>';
18265     };
18266     
18267     Renderer.prototype.del = function(text) {
18268       return '<del>' + text + '</del>';
18269     };
18270     
18271     Renderer.prototype.link = function(href, title, text) {
18272       if (this.options.sanitize) {
18273         try {
18274           var prot = decodeURIComponent(unescape(href))
18275             .replace(/[^\w:]/g, '')
18276             .toLowerCase();
18277         } catch (e) {
18278           return '';
18279         }
18280         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18281           return '';
18282         }
18283       }
18284       var out = '<a href="' + href + '"';
18285       if (title) {
18286         out += ' title="' + title + '"';
18287       }
18288       out += '>' + text + '</a>';
18289       return out;
18290     };
18291     
18292     Renderer.prototype.image = function(href, title, text) {
18293       var out = '<img src="' + href + '" alt="' + text + '"';
18294       if (title) {
18295         out += ' title="' + title + '"';
18296       }
18297       out += this.options.xhtml ? '/>' : '>';
18298       return out;
18299     };
18300     
18301     Renderer.prototype.text = function(text) {
18302       return text;
18303     };
18304     
18305     /**
18306      * Parsing & Compiling
18307      */
18308          /**
18309          * eval:var:Parser
18310     */
18311     
18312     var Parser= function (options) {
18313       this.tokens = [];
18314       this.token = null;
18315       this.options = options || marked.defaults;
18316       this.options.renderer = this.options.renderer || new Renderer;
18317       this.renderer = this.options.renderer;
18318       this.renderer.options = this.options;
18319     }
18320     
18321     /**
18322      * Static Parse Method
18323      */
18324     
18325     Parser.parse = function(src, options, renderer) {
18326       var parser = new Parser(options, renderer);
18327       return parser.parse(src);
18328     };
18329     
18330     /**
18331      * Parse Loop
18332      */
18333     
18334     Parser.prototype.parse = function(src) {
18335       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18336       this.tokens = src.reverse();
18337     
18338       var out = '';
18339       while (this.next()) {
18340         out += this.tok();
18341       }
18342     
18343       return out;
18344     };
18345     
18346     /**
18347      * Next Token
18348      */
18349     
18350     Parser.prototype.next = function() {
18351       return this.token = this.tokens.pop();
18352     };
18353     
18354     /**
18355      * Preview Next Token
18356      */
18357     
18358     Parser.prototype.peek = function() {
18359       return this.tokens[this.tokens.length - 1] || 0;
18360     };
18361     
18362     /**
18363      * Parse Text Tokens
18364      */
18365     
18366     Parser.prototype.parseText = function() {
18367       var body = this.token.text;
18368     
18369       while (this.peek().type === 'text') {
18370         body += '\n' + this.next().text;
18371       }
18372     
18373       return this.inline.output(body);
18374     };
18375     
18376     /**
18377      * Parse Current Token
18378      */
18379     
18380     Parser.prototype.tok = function() {
18381       switch (this.token.type) {
18382         case 'space': {
18383           return '';
18384         }
18385         case 'hr': {
18386           return this.renderer.hr();
18387         }
18388         case 'heading': {
18389           return this.renderer.heading(
18390             this.inline.output(this.token.text),
18391             this.token.depth,
18392             this.token.text);
18393         }
18394         case 'code': {
18395           return this.renderer.code(this.token.text,
18396             this.token.lang,
18397             this.token.escaped);
18398         }
18399         case 'table': {
18400           var header = ''
18401             , body = ''
18402             , i
18403             , row
18404             , cell
18405             , flags
18406             , j;
18407     
18408           // header
18409           cell = '';
18410           for (i = 0; i < this.token.header.length; i++) {
18411             flags = { header: true, align: this.token.align[i] };
18412             cell += this.renderer.tablecell(
18413               this.inline.output(this.token.header[i]),
18414               { header: true, align: this.token.align[i] }
18415             );
18416           }
18417           header += this.renderer.tablerow(cell);
18418     
18419           for (i = 0; i < this.token.cells.length; i++) {
18420             row = this.token.cells[i];
18421     
18422             cell = '';
18423             for (j = 0; j < row.length; j++) {
18424               cell += this.renderer.tablecell(
18425                 this.inline.output(row[j]),
18426                 { header: false, align: this.token.align[j] }
18427               );
18428             }
18429     
18430             body += this.renderer.tablerow(cell);
18431           }
18432           return this.renderer.table(header, body);
18433         }
18434         case 'blockquote_start': {
18435           var body = '';
18436     
18437           while (this.next().type !== 'blockquote_end') {
18438             body += this.tok();
18439           }
18440     
18441           return this.renderer.blockquote(body);
18442         }
18443         case 'list_start': {
18444           var body = ''
18445             , ordered = this.token.ordered;
18446     
18447           while (this.next().type !== 'list_end') {
18448             body += this.tok();
18449           }
18450     
18451           return this.renderer.list(body, ordered);
18452         }
18453         case 'list_item_start': {
18454           var body = '';
18455     
18456           while (this.next().type !== 'list_item_end') {
18457             body += this.token.type === 'text'
18458               ? this.parseText()
18459               : this.tok();
18460           }
18461     
18462           return this.renderer.listitem(body);
18463         }
18464         case 'loose_item_start': {
18465           var body = '';
18466     
18467           while (this.next().type !== 'list_item_end') {
18468             body += this.tok();
18469           }
18470     
18471           return this.renderer.listitem(body);
18472         }
18473         case 'html': {
18474           var html = !this.token.pre && !this.options.pedantic
18475             ? this.inline.output(this.token.text)
18476             : this.token.text;
18477           return this.renderer.html(html);
18478         }
18479         case 'paragraph': {
18480           return this.renderer.paragraph(this.inline.output(this.token.text));
18481         }
18482         case 'text': {
18483           return this.renderer.paragraph(this.parseText());
18484         }
18485       }
18486     };
18487   
18488     
18489     /**
18490      * Marked
18491      */
18492          /**
18493          * eval:var:marked
18494     */
18495     var marked = function (src, opt, callback) {
18496       if (callback || typeof opt === 'function') {
18497         if (!callback) {
18498           callback = opt;
18499           opt = null;
18500         }
18501     
18502         opt = merge({}, marked.defaults, opt || {});
18503     
18504         var highlight = opt.highlight
18505           , tokens
18506           , pending
18507           , i = 0;
18508     
18509         try {
18510           tokens = Lexer.lex(src, opt)
18511         } catch (e) {
18512           return callback(e);
18513         }
18514     
18515         pending = tokens.length;
18516          /**
18517          * eval:var:done
18518     */
18519         var done = function(err) {
18520           if (err) {
18521             opt.highlight = highlight;
18522             return callback(err);
18523           }
18524     
18525           var out;
18526     
18527           try {
18528             out = Parser.parse(tokens, opt);
18529           } catch (e) {
18530             err = e;
18531           }
18532     
18533           opt.highlight = highlight;
18534     
18535           return err
18536             ? callback(err)
18537             : callback(null, out);
18538         };
18539     
18540         if (!highlight || highlight.length < 3) {
18541           return done();
18542         }
18543     
18544         delete opt.highlight;
18545     
18546         if (!pending) { return done(); }
18547     
18548         for (; i < tokens.length; i++) {
18549           (function(token) {
18550             if (token.type !== 'code') {
18551               return --pending || done();
18552             }
18553             return highlight(token.text, token.lang, function(err, code) {
18554               if (err) { return done(err); }
18555               if (code == null || code === token.text) {
18556                 return --pending || done();
18557               }
18558               token.text = code;
18559               token.escaped = true;
18560               --pending || done();
18561             });
18562           })(tokens[i]);
18563         }
18564     
18565         return;
18566       }
18567       try {
18568         if (opt) { opt = merge({}, marked.defaults, opt); }
18569         return Parser.parse(Lexer.lex(src, opt), opt);
18570       } catch (e) {
18571         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18572         if ((opt || marked.defaults).silent) {
18573           return '<p>An error occured:</p><pre>'
18574             + escape(e.message + '', true)
18575             + '</pre>';
18576         }
18577         throw e;
18578       }
18579     }
18580     
18581     /**
18582      * Options
18583      */
18584     
18585     marked.options =
18586     marked.setOptions = function(opt) {
18587       merge(marked.defaults, opt);
18588       return marked;
18589     };
18590     
18591     marked.defaults = {
18592       gfm: true,
18593       tables: true,
18594       breaks: false,
18595       pedantic: false,
18596       sanitize: false,
18597       sanitizer: null,
18598       mangle: true,
18599       smartLists: false,
18600       silent: false,
18601       highlight: null,
18602       langPrefix: 'lang-',
18603       smartypants: false,
18604       headerPrefix: '',
18605       renderer: new Renderer,
18606       xhtml: false
18607     };
18608     
18609     /**
18610      * Expose
18611      */
18612     
18613     marked.Parser = Parser;
18614     marked.parser = Parser.parse;
18615     
18616     marked.Renderer = Renderer;
18617     
18618     marked.Lexer = Lexer;
18619     marked.lexer = Lexer.lex;
18620     
18621     marked.InlineLexer = InlineLexer;
18622     marked.inlineLexer = InlineLexer.output;
18623     
18624     marked.parse = marked;
18625     
18626     Roo.Markdown.marked = marked;
18627
18628 })();/*
18629  * Based on:
18630  * Ext JS Library 1.1.1
18631  * Copyright(c) 2006-2007, Ext JS, LLC.
18632  *
18633  * Originally Released Under LGPL - original licence link has changed is not relivant.
18634  *
18635  * Fork - LGPL
18636  * <script type="text/javascript">
18637  */
18638
18639
18640
18641 /*
18642  * These classes are derivatives of the similarly named classes in the YUI Library.
18643  * The original license:
18644  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18645  * Code licensed under the BSD License:
18646  * http://developer.yahoo.net/yui/license.txt
18647  */
18648
18649 (function() {
18650
18651 var Event=Roo.EventManager;
18652 var Dom=Roo.lib.Dom;
18653
18654 /**
18655  * @class Roo.dd.DragDrop
18656  * @extends Roo.util.Observable
18657  * Defines the interface and base operation of items that that can be
18658  * dragged or can be drop targets.  It was designed to be extended, overriding
18659  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18660  * Up to three html elements can be associated with a DragDrop instance:
18661  * <ul>
18662  * <li>linked element: the element that is passed into the constructor.
18663  * This is the element which defines the boundaries for interaction with
18664  * other DragDrop objects.</li>
18665  * <li>handle element(s): The drag operation only occurs if the element that
18666  * was clicked matches a handle element.  By default this is the linked
18667  * element, but there are times that you will want only a portion of the
18668  * linked element to initiate the drag operation, and the setHandleElId()
18669  * method provides a way to define this.</li>
18670  * <li>drag element: this represents the element that would be moved along
18671  * with the cursor during a drag operation.  By default, this is the linked
18672  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18673  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18674  * </li>
18675  * </ul>
18676  * This class should not be instantiated until the onload event to ensure that
18677  * the associated elements are available.
18678  * The following would define a DragDrop obj that would interact with any
18679  * other DragDrop obj in the "group1" group:
18680  * <pre>
18681  *  dd = new Roo.dd.DragDrop("div1", "group1");
18682  * </pre>
18683  * Since none of the event handlers have been implemented, nothing would
18684  * actually happen if you were to run the code above.  Normally you would
18685  * override this class or one of the default implementations, but you can
18686  * also override the methods you want on an instance of the class...
18687  * <pre>
18688  *  dd.onDragDrop = function(e, id) {
18689  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18690  *  }
18691  * </pre>
18692  * @constructor
18693  * @param {String} id of the element that is linked to this instance
18694  * @param {String} sGroup the group of related DragDrop objects
18695  * @param {object} config an object containing configurable attributes
18696  *                Valid properties for DragDrop:
18697  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18698  */
18699 Roo.dd.DragDrop = function(id, sGroup, config) {
18700     if (id) {
18701         this.init(id, sGroup, config);
18702     }
18703     
18704 };
18705
18706 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18707
18708     /**
18709      * The id of the element associated with this object.  This is what we
18710      * refer to as the "linked element" because the size and position of
18711      * this element is used to determine when the drag and drop objects have
18712      * interacted.
18713      * @property id
18714      * @type String
18715      */
18716     id: null,
18717
18718     /**
18719      * Configuration attributes passed into the constructor
18720      * @property config
18721      * @type object
18722      */
18723     config: null,
18724
18725     /**
18726      * The id of the element that will be dragged.  By default this is same
18727      * as the linked element , but could be changed to another element. Ex:
18728      * Roo.dd.DDProxy
18729      * @property dragElId
18730      * @type String
18731      * @private
18732      */
18733     dragElId: null,
18734
18735     /**
18736      * the id of the element that initiates the drag operation.  By default
18737      * this is the linked element, but could be changed to be a child of this
18738      * element.  This lets us do things like only starting the drag when the
18739      * header element within the linked html element is clicked.
18740      * @property handleElId
18741      * @type String
18742      * @private
18743      */
18744     handleElId: null,
18745
18746     /**
18747      * An associative array of HTML tags that will be ignored if clicked.
18748      * @property invalidHandleTypes
18749      * @type {string: string}
18750      */
18751     invalidHandleTypes: null,
18752
18753     /**
18754      * An associative array of ids for elements that will be ignored if clicked
18755      * @property invalidHandleIds
18756      * @type {string: string}
18757      */
18758     invalidHandleIds: null,
18759
18760     /**
18761      * An indexted array of css class names for elements that will be ignored
18762      * if clicked.
18763      * @property invalidHandleClasses
18764      * @type string[]
18765      */
18766     invalidHandleClasses: null,
18767
18768     /**
18769      * The linked element's absolute X position at the time the drag was
18770      * started
18771      * @property startPageX
18772      * @type int
18773      * @private
18774      */
18775     startPageX: 0,
18776
18777     /**
18778      * The linked element's absolute X position at the time the drag was
18779      * started
18780      * @property startPageY
18781      * @type int
18782      * @private
18783      */
18784     startPageY: 0,
18785
18786     /**
18787      * The group defines a logical collection of DragDrop objects that are
18788      * related.  Instances only get events when interacting with other
18789      * DragDrop object in the same group.  This lets us define multiple
18790      * groups using a single DragDrop subclass if we want.
18791      * @property groups
18792      * @type {string: string}
18793      */
18794     groups: null,
18795
18796     /**
18797      * Individual drag/drop instances can be locked.  This will prevent
18798      * onmousedown start drag.
18799      * @property locked
18800      * @type boolean
18801      * @private
18802      */
18803     locked: false,
18804
18805     /**
18806      * Lock this instance
18807      * @method lock
18808      */
18809     lock: function() { this.locked = true; },
18810
18811     /**
18812      * Unlock this instace
18813      * @method unlock
18814      */
18815     unlock: function() { this.locked = false; },
18816
18817     /**
18818      * By default, all insances can be a drop target.  This can be disabled by
18819      * setting isTarget to false.
18820      * @method isTarget
18821      * @type boolean
18822      */
18823     isTarget: true,
18824
18825     /**
18826      * The padding configured for this drag and drop object for calculating
18827      * the drop zone intersection with this object.
18828      * @method padding
18829      * @type int[]
18830      */
18831     padding: null,
18832
18833     /**
18834      * Cached reference to the linked element
18835      * @property _domRef
18836      * @private
18837      */
18838     _domRef: null,
18839
18840     /**
18841      * Internal typeof flag
18842      * @property __ygDragDrop
18843      * @private
18844      */
18845     __ygDragDrop: true,
18846
18847     /**
18848      * Set to true when horizontal contraints are applied
18849      * @property constrainX
18850      * @type boolean
18851      * @private
18852      */
18853     constrainX: false,
18854
18855     /**
18856      * Set to true when vertical contraints are applied
18857      * @property constrainY
18858      * @type boolean
18859      * @private
18860      */
18861     constrainY: false,
18862
18863     /**
18864      * The left constraint
18865      * @property minX
18866      * @type int
18867      * @private
18868      */
18869     minX: 0,
18870
18871     /**
18872      * The right constraint
18873      * @property maxX
18874      * @type int
18875      * @private
18876      */
18877     maxX: 0,
18878
18879     /**
18880      * The up constraint
18881      * @property minY
18882      * @type int
18883      * @type int
18884      * @private
18885      */
18886     minY: 0,
18887
18888     /**
18889      * The down constraint
18890      * @property maxY
18891      * @type int
18892      * @private
18893      */
18894     maxY: 0,
18895
18896     /**
18897      * Maintain offsets when we resetconstraints.  Set to true when you want
18898      * the position of the element relative to its parent to stay the same
18899      * when the page changes
18900      *
18901      * @property maintainOffset
18902      * @type boolean
18903      */
18904     maintainOffset: false,
18905
18906     /**
18907      * Array of pixel locations the element will snap to if we specified a
18908      * horizontal graduation/interval.  This array is generated automatically
18909      * when you define a tick interval.
18910      * @property xTicks
18911      * @type int[]
18912      */
18913     xTicks: null,
18914
18915     /**
18916      * Array of pixel locations the element will snap to if we specified a
18917      * vertical graduation/interval.  This array is generated automatically
18918      * when you define a tick interval.
18919      * @property yTicks
18920      * @type int[]
18921      */
18922     yTicks: null,
18923
18924     /**
18925      * By default the drag and drop instance will only respond to the primary
18926      * button click (left button for a right-handed mouse).  Set to true to
18927      * allow drag and drop to start with any mouse click that is propogated
18928      * by the browser
18929      * @property primaryButtonOnly
18930      * @type boolean
18931      */
18932     primaryButtonOnly: true,
18933
18934     /**
18935      * The availabe property is false until the linked dom element is accessible.
18936      * @property available
18937      * @type boolean
18938      */
18939     available: false,
18940
18941     /**
18942      * By default, drags can only be initiated if the mousedown occurs in the
18943      * region the linked element is.  This is done in part to work around a
18944      * bug in some browsers that mis-report the mousedown if the previous
18945      * mouseup happened outside of the window.  This property is set to true
18946      * if outer handles are defined.
18947      *
18948      * @property hasOuterHandles
18949      * @type boolean
18950      * @default false
18951      */
18952     hasOuterHandles: false,
18953
18954     /**
18955      * Code that executes immediately before the startDrag event
18956      * @method b4StartDrag
18957      * @private
18958      */
18959     b4StartDrag: function(x, y) { },
18960
18961     /**
18962      * Abstract method called after a drag/drop object is clicked
18963      * and the drag or mousedown time thresholds have beeen met.
18964      * @method startDrag
18965      * @param {int} X click location
18966      * @param {int} Y click location
18967      */
18968     startDrag: function(x, y) { /* override this */ },
18969
18970     /**
18971      * Code that executes immediately before the onDrag event
18972      * @method b4Drag
18973      * @private
18974      */
18975     b4Drag: function(e) { },
18976
18977     /**
18978      * Abstract method called during the onMouseMove event while dragging an
18979      * object.
18980      * @method onDrag
18981      * @param {Event} e the mousemove event
18982      */
18983     onDrag: function(e) { /* override this */ },
18984
18985     /**
18986      * Abstract method called when this element fist begins hovering over
18987      * another DragDrop obj
18988      * @method onDragEnter
18989      * @param {Event} e the mousemove event
18990      * @param {String|DragDrop[]} id In POINT mode, the element
18991      * id this is hovering over.  In INTERSECT mode, an array of one or more
18992      * dragdrop items being hovered over.
18993      */
18994     onDragEnter: function(e, id) { /* override this */ },
18995
18996     /**
18997      * Code that executes immediately before the onDragOver event
18998      * @method b4DragOver
18999      * @private
19000      */
19001     b4DragOver: function(e) { },
19002
19003     /**
19004      * Abstract method called when this element is hovering over another
19005      * DragDrop obj
19006      * @method onDragOver
19007      * @param {Event} e the mousemove event
19008      * @param {String|DragDrop[]} id In POINT mode, the element
19009      * id this is hovering over.  In INTERSECT mode, an array of dd items
19010      * being hovered over.
19011      */
19012     onDragOver: function(e, id) { /* override this */ },
19013
19014     /**
19015      * Code that executes immediately before the onDragOut event
19016      * @method b4DragOut
19017      * @private
19018      */
19019     b4DragOut: function(e) { },
19020
19021     /**
19022      * Abstract method called when we are no longer hovering over an element
19023      * @method onDragOut
19024      * @param {Event} e the mousemove event
19025      * @param {String|DragDrop[]} id In POINT mode, the element
19026      * id this was hovering over.  In INTERSECT mode, an array of dd items
19027      * that the mouse is no longer over.
19028      */
19029     onDragOut: function(e, id) { /* override this */ },
19030
19031     /**
19032      * Code that executes immediately before the onDragDrop event
19033      * @method b4DragDrop
19034      * @private
19035      */
19036     b4DragDrop: function(e) { },
19037
19038     /**
19039      * Abstract method called when this item is dropped on another DragDrop
19040      * obj
19041      * @method onDragDrop
19042      * @param {Event} e the mouseup event
19043      * @param {String|DragDrop[]} id In POINT mode, the element
19044      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19045      * was dropped on.
19046      */
19047     onDragDrop: function(e, id) { /* override this */ },
19048
19049     /**
19050      * Abstract method called when this item is dropped on an area with no
19051      * drop target
19052      * @method onInvalidDrop
19053      * @param {Event} e the mouseup event
19054      */
19055     onInvalidDrop: function(e) { /* override this */ },
19056
19057     /**
19058      * Code that executes immediately before the endDrag event
19059      * @method b4EndDrag
19060      * @private
19061      */
19062     b4EndDrag: function(e) { },
19063
19064     /**
19065      * Fired when we are done dragging the object
19066      * @method endDrag
19067      * @param {Event} e the mouseup event
19068      */
19069     endDrag: function(e) { /* override this */ },
19070
19071     /**
19072      * Code executed immediately before the onMouseDown event
19073      * @method b4MouseDown
19074      * @param {Event} e the mousedown event
19075      * @private
19076      */
19077     b4MouseDown: function(e) {  },
19078
19079     /**
19080      * Event handler that fires when a drag/drop obj gets a mousedown
19081      * @method onMouseDown
19082      * @param {Event} e the mousedown event
19083      */
19084     onMouseDown: function(e) { /* override this */ },
19085
19086     /**
19087      * Event handler that fires when a drag/drop obj gets a mouseup
19088      * @method onMouseUp
19089      * @param {Event} e the mouseup event
19090      */
19091     onMouseUp: function(e) { /* override this */ },
19092
19093     /**
19094      * Override the onAvailable method to do what is needed after the initial
19095      * position was determined.
19096      * @method onAvailable
19097      */
19098     onAvailable: function () {
19099     },
19100
19101     /*
19102      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19103      * @type Object
19104      */
19105     defaultPadding : {left:0, right:0, top:0, bottom:0},
19106
19107     /*
19108      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19109  *
19110  * Usage:
19111  <pre><code>
19112  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19113                 { dragElId: "existingProxyDiv" });
19114  dd.startDrag = function(){
19115      this.constrainTo("parent-id");
19116  };
19117  </code></pre>
19118  * Or you can initalize it using the {@link Roo.Element} object:
19119  <pre><code>
19120  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19121      startDrag : function(){
19122          this.constrainTo("parent-id");
19123      }
19124  });
19125  </code></pre>
19126      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19127      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19128      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19129      * an object containing the sides to pad. For example: {right:10, bottom:10}
19130      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19131      */
19132     constrainTo : function(constrainTo, pad, inContent){
19133         if(typeof pad == "number"){
19134             pad = {left: pad, right:pad, top:pad, bottom:pad};
19135         }
19136         pad = pad || this.defaultPadding;
19137         var b = Roo.get(this.getEl()).getBox();
19138         var ce = Roo.get(constrainTo);
19139         var s = ce.getScroll();
19140         var c, cd = ce.dom;
19141         if(cd == document.body){
19142             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19143         }else{
19144             xy = ce.getXY();
19145             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19146         }
19147
19148
19149         var topSpace = b.y - c.y;
19150         var leftSpace = b.x - c.x;
19151
19152         this.resetConstraints();
19153         this.setXConstraint(leftSpace - (pad.left||0), // left
19154                 c.width - leftSpace - b.width - (pad.right||0) //right
19155         );
19156         this.setYConstraint(topSpace - (pad.top||0), //top
19157                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19158         );
19159     },
19160
19161     /**
19162      * Returns a reference to the linked element
19163      * @method getEl
19164      * @return {HTMLElement} the html element
19165      */
19166     getEl: function() {
19167         if (!this._domRef) {
19168             this._domRef = Roo.getDom(this.id);
19169         }
19170
19171         return this._domRef;
19172     },
19173
19174     /**
19175      * Returns a reference to the actual element to drag.  By default this is
19176      * the same as the html element, but it can be assigned to another
19177      * element. An example of this can be found in Roo.dd.DDProxy
19178      * @method getDragEl
19179      * @return {HTMLElement} the html element
19180      */
19181     getDragEl: function() {
19182         return Roo.getDom(this.dragElId);
19183     },
19184
19185     /**
19186      * Sets up the DragDrop object.  Must be called in the constructor of any
19187      * Roo.dd.DragDrop subclass
19188      * @method init
19189      * @param id the id of the linked element
19190      * @param {String} sGroup the group of related items
19191      * @param {object} config configuration attributes
19192      */
19193     init: function(id, sGroup, config) {
19194         this.initTarget(id, sGroup, config);
19195         if (!Roo.isTouch) {
19196             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19197         }
19198         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19199         // Event.on(this.id, "selectstart", Event.preventDefault);
19200     },
19201
19202     /**
19203      * Initializes Targeting functionality only... the object does not
19204      * get a mousedown handler.
19205      * @method initTarget
19206      * @param id the id of the linked element
19207      * @param {String} sGroup the group of related items
19208      * @param {object} config configuration attributes
19209      */
19210     initTarget: function(id, sGroup, config) {
19211
19212         // configuration attributes
19213         this.config = config || {};
19214
19215         // create a local reference to the drag and drop manager
19216         this.DDM = Roo.dd.DDM;
19217         // initialize the groups array
19218         this.groups = {};
19219
19220         // assume that we have an element reference instead of an id if the
19221         // parameter is not a string
19222         if (typeof id !== "string") {
19223             id = Roo.id(id);
19224         }
19225
19226         // set the id
19227         this.id = id;
19228
19229         // add to an interaction group
19230         this.addToGroup((sGroup) ? sGroup : "default");
19231
19232         // We don't want to register this as the handle with the manager
19233         // so we just set the id rather than calling the setter.
19234         this.handleElId = id;
19235
19236         // the linked element is the element that gets dragged by default
19237         this.setDragElId(id);
19238
19239         // by default, clicked anchors will not start drag operations.
19240         this.invalidHandleTypes = { A: "A" };
19241         this.invalidHandleIds = {};
19242         this.invalidHandleClasses = [];
19243
19244         this.applyConfig();
19245
19246         this.handleOnAvailable();
19247     },
19248
19249     /**
19250      * Applies the configuration parameters that were passed into the constructor.
19251      * This is supposed to happen at each level through the inheritance chain.  So
19252      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19253      * DragDrop in order to get all of the parameters that are available in
19254      * each object.
19255      * @method applyConfig
19256      */
19257     applyConfig: function() {
19258
19259         // configurable properties:
19260         //    padding, isTarget, maintainOffset, primaryButtonOnly
19261         this.padding           = this.config.padding || [0, 0, 0, 0];
19262         this.isTarget          = (this.config.isTarget !== false);
19263         this.maintainOffset    = (this.config.maintainOffset);
19264         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19265
19266     },
19267
19268     /**
19269      * Executed when the linked element is available
19270      * @method handleOnAvailable
19271      * @private
19272      */
19273     handleOnAvailable: function() {
19274         this.available = true;
19275         this.resetConstraints();
19276         this.onAvailable();
19277     },
19278
19279      /**
19280      * Configures the padding for the target zone in px.  Effectively expands
19281      * (or reduces) the virtual object size for targeting calculations.
19282      * Supports css-style shorthand; if only one parameter is passed, all sides
19283      * will have that padding, and if only two are passed, the top and bottom
19284      * will have the first param, the left and right the second.
19285      * @method setPadding
19286      * @param {int} iTop    Top pad
19287      * @param {int} iRight  Right pad
19288      * @param {int} iBot    Bot pad
19289      * @param {int} iLeft   Left pad
19290      */
19291     setPadding: function(iTop, iRight, iBot, iLeft) {
19292         // this.padding = [iLeft, iRight, iTop, iBot];
19293         if (!iRight && 0 !== iRight) {
19294             this.padding = [iTop, iTop, iTop, iTop];
19295         } else if (!iBot && 0 !== iBot) {
19296             this.padding = [iTop, iRight, iTop, iRight];
19297         } else {
19298             this.padding = [iTop, iRight, iBot, iLeft];
19299         }
19300     },
19301
19302     /**
19303      * Stores the initial placement of the linked element.
19304      * @method setInitialPosition
19305      * @param {int} diffX   the X offset, default 0
19306      * @param {int} diffY   the Y offset, default 0
19307      */
19308     setInitPosition: function(diffX, diffY) {
19309         var el = this.getEl();
19310
19311         if (!this.DDM.verifyEl(el)) {
19312             return;
19313         }
19314
19315         var dx = diffX || 0;
19316         var dy = diffY || 0;
19317
19318         var p = Dom.getXY( el );
19319
19320         this.initPageX = p[0] - dx;
19321         this.initPageY = p[1] - dy;
19322
19323         this.lastPageX = p[0];
19324         this.lastPageY = p[1];
19325
19326
19327         this.setStartPosition(p);
19328     },
19329
19330     /**
19331      * Sets the start position of the element.  This is set when the obj
19332      * is initialized, the reset when a drag is started.
19333      * @method setStartPosition
19334      * @param pos current position (from previous lookup)
19335      * @private
19336      */
19337     setStartPosition: function(pos) {
19338         var p = pos || Dom.getXY( this.getEl() );
19339         this.deltaSetXY = null;
19340
19341         this.startPageX = p[0];
19342         this.startPageY = p[1];
19343     },
19344
19345     /**
19346      * Add this instance to a group of related drag/drop objects.  All
19347      * instances belong to at least one group, and can belong to as many
19348      * groups as needed.
19349      * @method addToGroup
19350      * @param sGroup {string} the name of the group
19351      */
19352     addToGroup: function(sGroup) {
19353         this.groups[sGroup] = true;
19354         this.DDM.regDragDrop(this, sGroup);
19355     },
19356
19357     /**
19358      * Remove's this instance from the supplied interaction group
19359      * @method removeFromGroup
19360      * @param {string}  sGroup  The group to drop
19361      */
19362     removeFromGroup: function(sGroup) {
19363         if (this.groups[sGroup]) {
19364             delete this.groups[sGroup];
19365         }
19366
19367         this.DDM.removeDDFromGroup(this, sGroup);
19368     },
19369
19370     /**
19371      * Allows you to specify that an element other than the linked element
19372      * will be moved with the cursor during a drag
19373      * @method setDragElId
19374      * @param id {string} the id of the element that will be used to initiate the drag
19375      */
19376     setDragElId: function(id) {
19377         this.dragElId = id;
19378     },
19379
19380     /**
19381      * Allows you to specify a child of the linked element that should be
19382      * used to initiate the drag operation.  An example of this would be if
19383      * you have a content div with text and links.  Clicking anywhere in the
19384      * content area would normally start the drag operation.  Use this method
19385      * to specify that an element inside of the content div is the element
19386      * that starts the drag operation.
19387      * @method setHandleElId
19388      * @param id {string} the id of the element that will be used to
19389      * initiate the drag.
19390      */
19391     setHandleElId: function(id) {
19392         if (typeof id !== "string") {
19393             id = Roo.id(id);
19394         }
19395         this.handleElId = id;
19396         this.DDM.regHandle(this.id, id);
19397     },
19398
19399     /**
19400      * Allows you to set an element outside of the linked element as a drag
19401      * handle
19402      * @method setOuterHandleElId
19403      * @param id the id of the element that will be used to initiate the drag
19404      */
19405     setOuterHandleElId: function(id) {
19406         if (typeof id !== "string") {
19407             id = Roo.id(id);
19408         }
19409         Event.on(id, "mousedown",
19410                 this.handleMouseDown, this);
19411         this.setHandleElId(id);
19412
19413         this.hasOuterHandles = true;
19414     },
19415
19416     /**
19417      * Remove all drag and drop hooks for this element
19418      * @method unreg
19419      */
19420     unreg: function() {
19421         Event.un(this.id, "mousedown",
19422                 this.handleMouseDown);
19423         Event.un(this.id, "touchstart",
19424                 this.handleMouseDown);
19425         this._domRef = null;
19426         this.DDM._remove(this);
19427     },
19428
19429     destroy : function(){
19430         this.unreg();
19431     },
19432
19433     /**
19434      * Returns true if this instance is locked, or the drag drop mgr is locked
19435      * (meaning that all drag/drop is disabled on the page.)
19436      * @method isLocked
19437      * @return {boolean} true if this obj or all drag/drop is locked, else
19438      * false
19439      */
19440     isLocked: function() {
19441         return (this.DDM.isLocked() || this.locked);
19442     },
19443
19444     /**
19445      * Fired when this object is clicked
19446      * @method handleMouseDown
19447      * @param {Event} e
19448      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19449      * @private
19450      */
19451     handleMouseDown: function(e, oDD){
19452      
19453         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19454             //Roo.log('not touch/ button !=0');
19455             return;
19456         }
19457         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19458             return; // double touch..
19459         }
19460         
19461
19462         if (this.isLocked()) {
19463             //Roo.log('locked');
19464             return;
19465         }
19466
19467         this.DDM.refreshCache(this.groups);
19468 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19469         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19470         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19471             //Roo.log('no outer handes or not over target');
19472                 // do nothing.
19473         } else {
19474 //            Roo.log('check validator');
19475             if (this.clickValidator(e)) {
19476 //                Roo.log('validate success');
19477                 // set the initial element position
19478                 this.setStartPosition();
19479
19480
19481                 this.b4MouseDown(e);
19482                 this.onMouseDown(e);
19483
19484                 this.DDM.handleMouseDown(e, this);
19485
19486                 this.DDM.stopEvent(e);
19487             } else {
19488
19489
19490             }
19491         }
19492     },
19493
19494     clickValidator: function(e) {
19495         var target = e.getTarget();
19496         return ( this.isValidHandleChild(target) &&
19497                     (this.id == this.handleElId ||
19498                         this.DDM.handleWasClicked(target, this.id)) );
19499     },
19500
19501     /**
19502      * Allows you to specify a tag name that should not start a drag operation
19503      * when clicked.  This is designed to facilitate embedding links within a
19504      * drag handle that do something other than start the drag.
19505      * @method addInvalidHandleType
19506      * @param {string} tagName the type of element to exclude
19507      */
19508     addInvalidHandleType: function(tagName) {
19509         var type = tagName.toUpperCase();
19510         this.invalidHandleTypes[type] = type;
19511     },
19512
19513     /**
19514      * Lets you to specify an element id for a child of a drag handle
19515      * that should not initiate a drag
19516      * @method addInvalidHandleId
19517      * @param {string} id the element id of the element you wish to ignore
19518      */
19519     addInvalidHandleId: function(id) {
19520         if (typeof id !== "string") {
19521             id = Roo.id(id);
19522         }
19523         this.invalidHandleIds[id] = id;
19524     },
19525
19526     /**
19527      * Lets you specify a css class of elements that will not initiate a drag
19528      * @method addInvalidHandleClass
19529      * @param {string} cssClass the class of the elements you wish to ignore
19530      */
19531     addInvalidHandleClass: function(cssClass) {
19532         this.invalidHandleClasses.push(cssClass);
19533     },
19534
19535     /**
19536      * Unsets an excluded tag name set by addInvalidHandleType
19537      * @method removeInvalidHandleType
19538      * @param {string} tagName the type of element to unexclude
19539      */
19540     removeInvalidHandleType: function(tagName) {
19541         var type = tagName.toUpperCase();
19542         // this.invalidHandleTypes[type] = null;
19543         delete this.invalidHandleTypes[type];
19544     },
19545
19546     /**
19547      * Unsets an invalid handle id
19548      * @method removeInvalidHandleId
19549      * @param {string} id the id of the element to re-enable
19550      */
19551     removeInvalidHandleId: function(id) {
19552         if (typeof id !== "string") {
19553             id = Roo.id(id);
19554         }
19555         delete this.invalidHandleIds[id];
19556     },
19557
19558     /**
19559      * Unsets an invalid css class
19560      * @method removeInvalidHandleClass
19561      * @param {string} cssClass the class of the element(s) you wish to
19562      * re-enable
19563      */
19564     removeInvalidHandleClass: function(cssClass) {
19565         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19566             if (this.invalidHandleClasses[i] == cssClass) {
19567                 delete this.invalidHandleClasses[i];
19568             }
19569         }
19570     },
19571
19572     /**
19573      * Checks the tag exclusion list to see if this click should be ignored
19574      * @method isValidHandleChild
19575      * @param {HTMLElement} node the HTMLElement to evaluate
19576      * @return {boolean} true if this is a valid tag type, false if not
19577      */
19578     isValidHandleChild: function(node) {
19579
19580         var valid = true;
19581         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19582         var nodeName;
19583         try {
19584             nodeName = node.nodeName.toUpperCase();
19585         } catch(e) {
19586             nodeName = node.nodeName;
19587         }
19588         valid = valid && !this.invalidHandleTypes[nodeName];
19589         valid = valid && !this.invalidHandleIds[node.id];
19590
19591         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19592             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19593         }
19594
19595
19596         return valid;
19597
19598     },
19599
19600     /**
19601      * Create the array of horizontal tick marks if an interval was specified
19602      * in setXConstraint().
19603      * @method setXTicks
19604      * @private
19605      */
19606     setXTicks: function(iStartX, iTickSize) {
19607         this.xTicks = [];
19608         this.xTickSize = iTickSize;
19609
19610         var tickMap = {};
19611
19612         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19613             if (!tickMap[i]) {
19614                 this.xTicks[this.xTicks.length] = i;
19615                 tickMap[i] = true;
19616             }
19617         }
19618
19619         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19620             if (!tickMap[i]) {
19621                 this.xTicks[this.xTicks.length] = i;
19622                 tickMap[i] = true;
19623             }
19624         }
19625
19626         this.xTicks.sort(this.DDM.numericSort) ;
19627     },
19628
19629     /**
19630      * Create the array of vertical tick marks if an interval was specified in
19631      * setYConstraint().
19632      * @method setYTicks
19633      * @private
19634      */
19635     setYTicks: function(iStartY, iTickSize) {
19636         this.yTicks = [];
19637         this.yTickSize = iTickSize;
19638
19639         var tickMap = {};
19640
19641         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19642             if (!tickMap[i]) {
19643                 this.yTicks[this.yTicks.length] = i;
19644                 tickMap[i] = true;
19645             }
19646         }
19647
19648         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19649             if (!tickMap[i]) {
19650                 this.yTicks[this.yTicks.length] = i;
19651                 tickMap[i] = true;
19652             }
19653         }
19654
19655         this.yTicks.sort(this.DDM.numericSort) ;
19656     },
19657
19658     /**
19659      * By default, the element can be dragged any place on the screen.  Use
19660      * this method to limit the horizontal travel of the element.  Pass in
19661      * 0,0 for the parameters if you want to lock the drag to the y axis.
19662      * @method setXConstraint
19663      * @param {int} iLeft the number of pixels the element can move to the left
19664      * @param {int} iRight the number of pixels the element can move to the
19665      * right
19666      * @param {int} iTickSize optional parameter for specifying that the
19667      * element
19668      * should move iTickSize pixels at a time.
19669      */
19670     setXConstraint: function(iLeft, iRight, iTickSize) {
19671         this.leftConstraint = iLeft;
19672         this.rightConstraint = iRight;
19673
19674         this.minX = this.initPageX - iLeft;
19675         this.maxX = this.initPageX + iRight;
19676         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19677
19678         this.constrainX = true;
19679     },
19680
19681     /**
19682      * Clears any constraints applied to this instance.  Also clears ticks
19683      * since they can't exist independent of a constraint at this time.
19684      * @method clearConstraints
19685      */
19686     clearConstraints: function() {
19687         this.constrainX = false;
19688         this.constrainY = false;
19689         this.clearTicks();
19690     },
19691
19692     /**
19693      * Clears any tick interval defined for this instance
19694      * @method clearTicks
19695      */
19696     clearTicks: function() {
19697         this.xTicks = null;
19698         this.yTicks = null;
19699         this.xTickSize = 0;
19700         this.yTickSize = 0;
19701     },
19702
19703     /**
19704      * By default, the element can be dragged any place on the screen.  Set
19705      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19706      * parameters if you want to lock the drag to the x axis.
19707      * @method setYConstraint
19708      * @param {int} iUp the number of pixels the element can move up
19709      * @param {int} iDown the number of pixels the element can move down
19710      * @param {int} iTickSize optional parameter for specifying that the
19711      * element should move iTickSize pixels at a time.
19712      */
19713     setYConstraint: function(iUp, iDown, iTickSize) {
19714         this.topConstraint = iUp;
19715         this.bottomConstraint = iDown;
19716
19717         this.minY = this.initPageY - iUp;
19718         this.maxY = this.initPageY + iDown;
19719         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19720
19721         this.constrainY = true;
19722
19723     },
19724
19725     /**
19726      * resetConstraints must be called if you manually reposition a dd element.
19727      * @method resetConstraints
19728      * @param {boolean} maintainOffset
19729      */
19730     resetConstraints: function() {
19731
19732
19733         // Maintain offsets if necessary
19734         if (this.initPageX || this.initPageX === 0) {
19735             // figure out how much this thing has moved
19736             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19737             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19738
19739             this.setInitPosition(dx, dy);
19740
19741         // This is the first time we have detected the element's position
19742         } else {
19743             this.setInitPosition();
19744         }
19745
19746         if (this.constrainX) {
19747             this.setXConstraint( this.leftConstraint,
19748                                  this.rightConstraint,
19749                                  this.xTickSize        );
19750         }
19751
19752         if (this.constrainY) {
19753             this.setYConstraint( this.topConstraint,
19754                                  this.bottomConstraint,
19755                                  this.yTickSize         );
19756         }
19757     },
19758
19759     /**
19760      * Normally the drag element is moved pixel by pixel, but we can specify
19761      * that it move a number of pixels at a time.  This method resolves the
19762      * location when we have it set up like this.
19763      * @method getTick
19764      * @param {int} val where we want to place the object
19765      * @param {int[]} tickArray sorted array of valid points
19766      * @return {int} the closest tick
19767      * @private
19768      */
19769     getTick: function(val, tickArray) {
19770
19771         if (!tickArray) {
19772             // If tick interval is not defined, it is effectively 1 pixel,
19773             // so we return the value passed to us.
19774             return val;
19775         } else if (tickArray[0] >= val) {
19776             // The value is lower than the first tick, so we return the first
19777             // tick.
19778             return tickArray[0];
19779         } else {
19780             for (var i=0, len=tickArray.length; i<len; ++i) {
19781                 var next = i + 1;
19782                 if (tickArray[next] && tickArray[next] >= val) {
19783                     var diff1 = val - tickArray[i];
19784                     var diff2 = tickArray[next] - val;
19785                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19786                 }
19787             }
19788
19789             // The value is larger than the last tick, so we return the last
19790             // tick.
19791             return tickArray[tickArray.length - 1];
19792         }
19793     },
19794
19795     /**
19796      * toString method
19797      * @method toString
19798      * @return {string} string representation of the dd obj
19799      */
19800     toString: function() {
19801         return ("DragDrop " + this.id);
19802     }
19803
19804 });
19805
19806 })();
19807 /*
19808  * Based on:
19809  * Ext JS Library 1.1.1
19810  * Copyright(c) 2006-2007, Ext JS, LLC.
19811  *
19812  * Originally Released Under LGPL - original licence link has changed is not relivant.
19813  *
19814  * Fork - LGPL
19815  * <script type="text/javascript">
19816  */
19817
19818
19819 /**
19820  * The drag and drop utility provides a framework for building drag and drop
19821  * applications.  In addition to enabling drag and drop for specific elements,
19822  * the drag and drop elements are tracked by the manager class, and the
19823  * interactions between the various elements are tracked during the drag and
19824  * the implementing code is notified about these important moments.
19825  */
19826
19827 // Only load the library once.  Rewriting the manager class would orphan
19828 // existing drag and drop instances.
19829 if (!Roo.dd.DragDropMgr) {
19830
19831 /**
19832  * @class Roo.dd.DragDropMgr
19833  * DragDropMgr is a singleton that tracks the element interaction for
19834  * all DragDrop items in the window.  Generally, you will not call
19835  * this class directly, but it does have helper methods that could
19836  * be useful in your DragDrop implementations.
19837  * @singleton
19838  */
19839 Roo.dd.DragDropMgr = function() {
19840
19841     var Event = Roo.EventManager;
19842
19843     return {
19844
19845         /**
19846          * Two dimensional Array of registered DragDrop objects.  The first
19847          * dimension is the DragDrop item group, the second the DragDrop
19848          * object.
19849          * @property ids
19850          * @type {string: string}
19851          * @private
19852          * @static
19853          */
19854         ids: {},
19855
19856         /**
19857          * Array of element ids defined as drag handles.  Used to determine
19858          * if the element that generated the mousedown event is actually the
19859          * handle and not the html element itself.
19860          * @property handleIds
19861          * @type {string: string}
19862          * @private
19863          * @static
19864          */
19865         handleIds: {},
19866
19867         /**
19868          * the DragDrop object that is currently being dragged
19869          * @property dragCurrent
19870          * @type DragDrop
19871          * @private
19872          * @static
19873          **/
19874         dragCurrent: null,
19875
19876         /**
19877          * the DragDrop object(s) that are being hovered over
19878          * @property dragOvers
19879          * @type Array
19880          * @private
19881          * @static
19882          */
19883         dragOvers: {},
19884
19885         /**
19886          * the X distance between the cursor and the object being dragged
19887          * @property deltaX
19888          * @type int
19889          * @private
19890          * @static
19891          */
19892         deltaX: 0,
19893
19894         /**
19895          * the Y distance between the cursor and the object being dragged
19896          * @property deltaY
19897          * @type int
19898          * @private
19899          * @static
19900          */
19901         deltaY: 0,
19902
19903         /**
19904          * Flag to determine if we should prevent the default behavior of the
19905          * events we define. By default this is true, but this can be set to
19906          * false if you need the default behavior (not recommended)
19907          * @property preventDefault
19908          * @type boolean
19909          * @static
19910          */
19911         preventDefault: true,
19912
19913         /**
19914          * Flag to determine if we should stop the propagation of the events
19915          * we generate. This is true by default but you may want to set it to
19916          * false if the html element contains other features that require the
19917          * mouse click.
19918          * @property stopPropagation
19919          * @type boolean
19920          * @static
19921          */
19922         stopPropagation: true,
19923
19924         /**
19925          * Internal flag that is set to true when drag and drop has been
19926          * intialized
19927          * @property initialized
19928          * @private
19929          * @static
19930          */
19931         initalized: false,
19932
19933         /**
19934          * All drag and drop can be disabled.
19935          * @property locked
19936          * @private
19937          * @static
19938          */
19939         locked: false,
19940
19941         /**
19942          * Called the first time an element is registered.
19943          * @method init
19944          * @private
19945          * @static
19946          */
19947         init: function() {
19948             this.initialized = true;
19949         },
19950
19951         /**
19952          * In point mode, drag and drop interaction is defined by the
19953          * location of the cursor during the drag/drop
19954          * @property POINT
19955          * @type int
19956          * @static
19957          */
19958         POINT: 0,
19959
19960         /**
19961          * In intersect mode, drag and drop interactio nis defined by the
19962          * overlap of two or more drag and drop objects.
19963          * @property INTERSECT
19964          * @type int
19965          * @static
19966          */
19967         INTERSECT: 1,
19968
19969         /**
19970          * The current drag and drop mode.  Default: POINT
19971          * @property mode
19972          * @type int
19973          * @static
19974          */
19975         mode: 0,
19976
19977         /**
19978          * Runs method on all drag and drop objects
19979          * @method _execOnAll
19980          * @private
19981          * @static
19982          */
19983         _execOnAll: function(sMethod, args) {
19984             for (var i in this.ids) {
19985                 for (var j in this.ids[i]) {
19986                     var oDD = this.ids[i][j];
19987                     if (! this.isTypeOfDD(oDD)) {
19988                         continue;
19989                     }
19990                     oDD[sMethod].apply(oDD, args);
19991                 }
19992             }
19993         },
19994
19995         /**
19996          * Drag and drop initialization.  Sets up the global event handlers
19997          * @method _onLoad
19998          * @private
19999          * @static
20000          */
20001         _onLoad: function() {
20002
20003             this.init();
20004
20005             if (!Roo.isTouch) {
20006                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20007                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20008             }
20009             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20010             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20011             
20012             Event.on(window,   "unload",    this._onUnload, this, true);
20013             Event.on(window,   "resize",    this._onResize, this, true);
20014             // Event.on(window,   "mouseout",    this._test);
20015
20016         },
20017
20018         /**
20019          * Reset constraints on all drag and drop objs
20020          * @method _onResize
20021          * @private
20022          * @static
20023          */
20024         _onResize: function(e) {
20025             this._execOnAll("resetConstraints", []);
20026         },
20027
20028         /**
20029          * Lock all drag and drop functionality
20030          * @method lock
20031          * @static
20032          */
20033         lock: function() { this.locked = true; },
20034
20035         /**
20036          * Unlock all drag and drop functionality
20037          * @method unlock
20038          * @static
20039          */
20040         unlock: function() { this.locked = false; },
20041
20042         /**
20043          * Is drag and drop locked?
20044          * @method isLocked
20045          * @return {boolean} True if drag and drop is locked, false otherwise.
20046          * @static
20047          */
20048         isLocked: function() { return this.locked; },
20049
20050         /**
20051          * Location cache that is set for all drag drop objects when a drag is
20052          * initiated, cleared when the drag is finished.
20053          * @property locationCache
20054          * @private
20055          * @static
20056          */
20057         locationCache: {},
20058
20059         /**
20060          * Set useCache to false if you want to force object the lookup of each
20061          * drag and drop linked element constantly during a drag.
20062          * @property useCache
20063          * @type boolean
20064          * @static
20065          */
20066         useCache: true,
20067
20068         /**
20069          * The number of pixels that the mouse needs to move after the
20070          * mousedown before the drag is initiated.  Default=3;
20071          * @property clickPixelThresh
20072          * @type int
20073          * @static
20074          */
20075         clickPixelThresh: 3,
20076
20077         /**
20078          * The number of milliseconds after the mousedown event to initiate the
20079          * drag if we don't get a mouseup event. Default=1000
20080          * @property clickTimeThresh
20081          * @type int
20082          * @static
20083          */
20084         clickTimeThresh: 350,
20085
20086         /**
20087          * Flag that indicates that either the drag pixel threshold or the
20088          * mousdown time threshold has been met
20089          * @property dragThreshMet
20090          * @type boolean
20091          * @private
20092          * @static
20093          */
20094         dragThreshMet: false,
20095
20096         /**
20097          * Timeout used for the click time threshold
20098          * @property clickTimeout
20099          * @type Object
20100          * @private
20101          * @static
20102          */
20103         clickTimeout: null,
20104
20105         /**
20106          * The X position of the mousedown event stored for later use when a
20107          * drag threshold is met.
20108          * @property startX
20109          * @type int
20110          * @private
20111          * @static
20112          */
20113         startX: 0,
20114
20115         /**
20116          * The Y position of the mousedown event stored for later use when a
20117          * drag threshold is met.
20118          * @property startY
20119          * @type int
20120          * @private
20121          * @static
20122          */
20123         startY: 0,
20124
20125         /**
20126          * Each DragDrop instance must be registered with the DragDropMgr.
20127          * This is executed in DragDrop.init()
20128          * @method regDragDrop
20129          * @param {DragDrop} oDD the DragDrop object to register
20130          * @param {String} sGroup the name of the group this element belongs to
20131          * @static
20132          */
20133         regDragDrop: function(oDD, sGroup) {
20134             if (!this.initialized) { this.init(); }
20135
20136             if (!this.ids[sGroup]) {
20137                 this.ids[sGroup] = {};
20138             }
20139             this.ids[sGroup][oDD.id] = oDD;
20140         },
20141
20142         /**
20143          * Removes the supplied dd instance from the supplied group. Executed
20144          * by DragDrop.removeFromGroup, so don't call this function directly.
20145          * @method removeDDFromGroup
20146          * @private
20147          * @static
20148          */
20149         removeDDFromGroup: function(oDD, sGroup) {
20150             if (!this.ids[sGroup]) {
20151                 this.ids[sGroup] = {};
20152             }
20153
20154             var obj = this.ids[sGroup];
20155             if (obj && obj[oDD.id]) {
20156                 delete obj[oDD.id];
20157             }
20158         },
20159
20160         /**
20161          * Unregisters a drag and drop item.  This is executed in
20162          * DragDrop.unreg, use that method instead of calling this directly.
20163          * @method _remove
20164          * @private
20165          * @static
20166          */
20167         _remove: function(oDD) {
20168             for (var g in oDD.groups) {
20169                 if (g && this.ids[g][oDD.id]) {
20170                     delete this.ids[g][oDD.id];
20171                 }
20172             }
20173             delete this.handleIds[oDD.id];
20174         },
20175
20176         /**
20177          * Each DragDrop handle element must be registered.  This is done
20178          * automatically when executing DragDrop.setHandleElId()
20179          * @method regHandle
20180          * @param {String} sDDId the DragDrop id this element is a handle for
20181          * @param {String} sHandleId the id of the element that is the drag
20182          * handle
20183          * @static
20184          */
20185         regHandle: function(sDDId, sHandleId) {
20186             if (!this.handleIds[sDDId]) {
20187                 this.handleIds[sDDId] = {};
20188             }
20189             this.handleIds[sDDId][sHandleId] = sHandleId;
20190         },
20191
20192         /**
20193          * Utility function to determine if a given element has been
20194          * registered as a drag drop item.
20195          * @method isDragDrop
20196          * @param {String} id the element id to check
20197          * @return {boolean} true if this element is a DragDrop item,
20198          * false otherwise
20199          * @static
20200          */
20201         isDragDrop: function(id) {
20202             return ( this.getDDById(id) ) ? true : false;
20203         },
20204
20205         /**
20206          * Returns the drag and drop instances that are in all groups the
20207          * passed in instance belongs to.
20208          * @method getRelated
20209          * @param {DragDrop} p_oDD the obj to get related data for
20210          * @param {boolean} bTargetsOnly if true, only return targetable objs
20211          * @return {DragDrop[]} the related instances
20212          * @static
20213          */
20214         getRelated: function(p_oDD, bTargetsOnly) {
20215             var oDDs = [];
20216             for (var i in p_oDD.groups) {
20217                 for (j in this.ids[i]) {
20218                     var dd = this.ids[i][j];
20219                     if (! this.isTypeOfDD(dd)) {
20220                         continue;
20221                     }
20222                     if (!bTargetsOnly || dd.isTarget) {
20223                         oDDs[oDDs.length] = dd;
20224                     }
20225                 }
20226             }
20227
20228             return oDDs;
20229         },
20230
20231         /**
20232          * Returns true if the specified dd target is a legal target for
20233          * the specifice drag obj
20234          * @method isLegalTarget
20235          * @param {DragDrop} the drag obj
20236          * @param {DragDrop} the target
20237          * @return {boolean} true if the target is a legal target for the
20238          * dd obj
20239          * @static
20240          */
20241         isLegalTarget: function (oDD, oTargetDD) {
20242             var targets = this.getRelated(oDD, true);
20243             for (var i=0, len=targets.length;i<len;++i) {
20244                 if (targets[i].id == oTargetDD.id) {
20245                     return true;
20246                 }
20247             }
20248
20249             return false;
20250         },
20251
20252         /**
20253          * My goal is to be able to transparently determine if an object is
20254          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20255          * returns "object", oDD.constructor.toString() always returns
20256          * "DragDrop" and not the name of the subclass.  So for now it just
20257          * evaluates a well-known variable in DragDrop.
20258          * @method isTypeOfDD
20259          * @param {Object} the object to evaluate
20260          * @return {boolean} true if typeof oDD = DragDrop
20261          * @static
20262          */
20263         isTypeOfDD: function (oDD) {
20264             return (oDD && oDD.__ygDragDrop);
20265         },
20266
20267         /**
20268          * Utility function to determine if a given element has been
20269          * registered as a drag drop handle for the given Drag Drop object.
20270          * @method isHandle
20271          * @param {String} id the element id to check
20272          * @return {boolean} true if this element is a DragDrop handle, false
20273          * otherwise
20274          * @static
20275          */
20276         isHandle: function(sDDId, sHandleId) {
20277             return ( this.handleIds[sDDId] &&
20278                             this.handleIds[sDDId][sHandleId] );
20279         },
20280
20281         /**
20282          * Returns the DragDrop instance for a given id
20283          * @method getDDById
20284          * @param {String} id the id of the DragDrop object
20285          * @return {DragDrop} the drag drop object, null if it is not found
20286          * @static
20287          */
20288         getDDById: function(id) {
20289             for (var i in this.ids) {
20290                 if (this.ids[i][id]) {
20291                     return this.ids[i][id];
20292                 }
20293             }
20294             return null;
20295         },
20296
20297         /**
20298          * Fired after a registered DragDrop object gets the mousedown event.
20299          * Sets up the events required to track the object being dragged
20300          * @method handleMouseDown
20301          * @param {Event} e the event
20302          * @param oDD the DragDrop object being dragged
20303          * @private
20304          * @static
20305          */
20306         handleMouseDown: function(e, oDD) {
20307             if(Roo.QuickTips){
20308                 Roo.QuickTips.disable();
20309             }
20310             this.currentTarget = e.getTarget();
20311
20312             this.dragCurrent = oDD;
20313
20314             var el = oDD.getEl();
20315
20316             // track start position
20317             this.startX = e.getPageX();
20318             this.startY = e.getPageY();
20319
20320             this.deltaX = this.startX - el.offsetLeft;
20321             this.deltaY = this.startY - el.offsetTop;
20322
20323             this.dragThreshMet = false;
20324
20325             this.clickTimeout = setTimeout(
20326                     function() {
20327                         var DDM = Roo.dd.DDM;
20328                         DDM.startDrag(DDM.startX, DDM.startY);
20329                     },
20330                     this.clickTimeThresh );
20331         },
20332
20333         /**
20334          * Fired when either the drag pixel threshol or the mousedown hold
20335          * time threshold has been met.
20336          * @method startDrag
20337          * @param x {int} the X position of the original mousedown
20338          * @param y {int} the Y position of the original mousedown
20339          * @static
20340          */
20341         startDrag: function(x, y) {
20342             clearTimeout(this.clickTimeout);
20343             if (this.dragCurrent) {
20344                 this.dragCurrent.b4StartDrag(x, y);
20345                 this.dragCurrent.startDrag(x, y);
20346             }
20347             this.dragThreshMet = true;
20348         },
20349
20350         /**
20351          * Internal function to handle the mouseup event.  Will be invoked
20352          * from the context of the document.
20353          * @method handleMouseUp
20354          * @param {Event} e the event
20355          * @private
20356          * @static
20357          */
20358         handleMouseUp: function(e) {
20359
20360             if(Roo.QuickTips){
20361                 Roo.QuickTips.enable();
20362             }
20363             if (! this.dragCurrent) {
20364                 return;
20365             }
20366
20367             clearTimeout(this.clickTimeout);
20368
20369             if (this.dragThreshMet) {
20370                 this.fireEvents(e, true);
20371             } else {
20372             }
20373
20374             this.stopDrag(e);
20375
20376             this.stopEvent(e);
20377         },
20378
20379         /**
20380          * Utility to stop event propagation and event default, if these
20381          * features are turned on.
20382          * @method stopEvent
20383          * @param {Event} e the event as returned by this.getEvent()
20384          * @static
20385          */
20386         stopEvent: function(e){
20387             if(this.stopPropagation) {
20388                 e.stopPropagation();
20389             }
20390
20391             if (this.preventDefault) {
20392                 e.preventDefault();
20393             }
20394         },
20395
20396         /**
20397          * Internal function to clean up event handlers after the drag
20398          * operation is complete
20399          * @method stopDrag
20400          * @param {Event} e the event
20401          * @private
20402          * @static
20403          */
20404         stopDrag: function(e) {
20405             // Fire the drag end event for the item that was dragged
20406             if (this.dragCurrent) {
20407                 if (this.dragThreshMet) {
20408                     this.dragCurrent.b4EndDrag(e);
20409                     this.dragCurrent.endDrag(e);
20410                 }
20411
20412                 this.dragCurrent.onMouseUp(e);
20413             }
20414
20415             this.dragCurrent = null;
20416             this.dragOvers = {};
20417         },
20418
20419         /**
20420          * Internal function to handle the mousemove event.  Will be invoked
20421          * from the context of the html element.
20422          *
20423          * @TODO figure out what we can do about mouse events lost when the
20424          * user drags objects beyond the window boundary.  Currently we can
20425          * detect this in internet explorer by verifying that the mouse is
20426          * down during the mousemove event.  Firefox doesn't give us the
20427          * button state on the mousemove event.
20428          * @method handleMouseMove
20429          * @param {Event} e the event
20430          * @private
20431          * @static
20432          */
20433         handleMouseMove: function(e) {
20434             if (! this.dragCurrent) {
20435                 return true;
20436             }
20437
20438             // var button = e.which || e.button;
20439
20440             // check for IE mouseup outside of page boundary
20441             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20442                 this.stopEvent(e);
20443                 return this.handleMouseUp(e);
20444             }
20445
20446             if (!this.dragThreshMet) {
20447                 var diffX = Math.abs(this.startX - e.getPageX());
20448                 var diffY = Math.abs(this.startY - e.getPageY());
20449                 if (diffX > this.clickPixelThresh ||
20450                             diffY > this.clickPixelThresh) {
20451                     this.startDrag(this.startX, this.startY);
20452                 }
20453             }
20454
20455             if (this.dragThreshMet) {
20456                 this.dragCurrent.b4Drag(e);
20457                 this.dragCurrent.onDrag(e);
20458                 if(!this.dragCurrent.moveOnly){
20459                     this.fireEvents(e, false);
20460                 }
20461             }
20462
20463             this.stopEvent(e);
20464
20465             return true;
20466         },
20467
20468         /**
20469          * Iterates over all of the DragDrop elements to find ones we are
20470          * hovering over or dropping on
20471          * @method fireEvents
20472          * @param {Event} e the event
20473          * @param {boolean} isDrop is this a drop op or a mouseover op?
20474          * @private
20475          * @static
20476          */
20477         fireEvents: function(e, isDrop) {
20478             var dc = this.dragCurrent;
20479
20480             // If the user did the mouse up outside of the window, we could
20481             // get here even though we have ended the drag.
20482             if (!dc || dc.isLocked()) {
20483                 return;
20484             }
20485
20486             var pt = e.getPoint();
20487
20488             // cache the previous dragOver array
20489             var oldOvers = [];
20490
20491             var outEvts   = [];
20492             var overEvts  = [];
20493             var dropEvts  = [];
20494             var enterEvts = [];
20495
20496             // Check to see if the object(s) we were hovering over is no longer
20497             // being hovered over so we can fire the onDragOut event
20498             for (var i in this.dragOvers) {
20499
20500                 var ddo = this.dragOvers[i];
20501
20502                 if (! this.isTypeOfDD(ddo)) {
20503                     continue;
20504                 }
20505
20506                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20507                     outEvts.push( ddo );
20508                 }
20509
20510                 oldOvers[i] = true;
20511                 delete this.dragOvers[i];
20512             }
20513
20514             for (var sGroup in dc.groups) {
20515
20516                 if ("string" != typeof sGroup) {
20517                     continue;
20518                 }
20519
20520                 for (i in this.ids[sGroup]) {
20521                     var oDD = this.ids[sGroup][i];
20522                     if (! this.isTypeOfDD(oDD)) {
20523                         continue;
20524                     }
20525
20526                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20527                         if (this.isOverTarget(pt, oDD, this.mode)) {
20528                             // look for drop interactions
20529                             if (isDrop) {
20530                                 dropEvts.push( oDD );
20531                             // look for drag enter and drag over interactions
20532                             } else {
20533
20534                                 // initial drag over: dragEnter fires
20535                                 if (!oldOvers[oDD.id]) {
20536                                     enterEvts.push( oDD );
20537                                 // subsequent drag overs: dragOver fires
20538                                 } else {
20539                                     overEvts.push( oDD );
20540                                 }
20541
20542                                 this.dragOvers[oDD.id] = oDD;
20543                             }
20544                         }
20545                     }
20546                 }
20547             }
20548
20549             if (this.mode) {
20550                 if (outEvts.length) {
20551                     dc.b4DragOut(e, outEvts);
20552                     dc.onDragOut(e, outEvts);
20553                 }
20554
20555                 if (enterEvts.length) {
20556                     dc.onDragEnter(e, enterEvts);
20557                 }
20558
20559                 if (overEvts.length) {
20560                     dc.b4DragOver(e, overEvts);
20561                     dc.onDragOver(e, overEvts);
20562                 }
20563
20564                 if (dropEvts.length) {
20565                     dc.b4DragDrop(e, dropEvts);
20566                     dc.onDragDrop(e, dropEvts);
20567                 }
20568
20569             } else {
20570                 // fire dragout events
20571                 var len = 0;
20572                 for (i=0, len=outEvts.length; i<len; ++i) {
20573                     dc.b4DragOut(e, outEvts[i].id);
20574                     dc.onDragOut(e, outEvts[i].id);
20575                 }
20576
20577                 // fire enter events
20578                 for (i=0,len=enterEvts.length; i<len; ++i) {
20579                     // dc.b4DragEnter(e, oDD.id);
20580                     dc.onDragEnter(e, enterEvts[i].id);
20581                 }
20582
20583                 // fire over events
20584                 for (i=0,len=overEvts.length; i<len; ++i) {
20585                     dc.b4DragOver(e, overEvts[i].id);
20586                     dc.onDragOver(e, overEvts[i].id);
20587                 }
20588
20589                 // fire drop events
20590                 for (i=0, len=dropEvts.length; i<len; ++i) {
20591                     dc.b4DragDrop(e, dropEvts[i].id);
20592                     dc.onDragDrop(e, dropEvts[i].id);
20593                 }
20594
20595             }
20596
20597             // notify about a drop that did not find a target
20598             if (isDrop && !dropEvts.length) {
20599                 dc.onInvalidDrop(e);
20600             }
20601
20602         },
20603
20604         /**
20605          * Helper function for getting the best match from the list of drag
20606          * and drop objects returned by the drag and drop events when we are
20607          * in INTERSECT mode.  It returns either the first object that the
20608          * cursor is over, or the object that has the greatest overlap with
20609          * the dragged element.
20610          * @method getBestMatch
20611          * @param  {DragDrop[]} dds The array of drag and drop objects
20612          * targeted
20613          * @return {DragDrop}       The best single match
20614          * @static
20615          */
20616         getBestMatch: function(dds) {
20617             var winner = null;
20618             // Return null if the input is not what we expect
20619             //if (!dds || !dds.length || dds.length == 0) {
20620                // winner = null;
20621             // If there is only one item, it wins
20622             //} else if (dds.length == 1) {
20623
20624             var len = dds.length;
20625
20626             if (len == 1) {
20627                 winner = dds[0];
20628             } else {
20629                 // Loop through the targeted items
20630                 for (var i=0; i<len; ++i) {
20631                     var dd = dds[i];
20632                     // If the cursor is over the object, it wins.  If the
20633                     // cursor is over multiple matches, the first one we come
20634                     // to wins.
20635                     if (dd.cursorIsOver) {
20636                         winner = dd;
20637                         break;
20638                     // Otherwise the object with the most overlap wins
20639                     } else {
20640                         if (!winner ||
20641                             winner.overlap.getArea() < dd.overlap.getArea()) {
20642                             winner = dd;
20643                         }
20644                     }
20645                 }
20646             }
20647
20648             return winner;
20649         },
20650
20651         /**
20652          * Refreshes the cache of the top-left and bottom-right points of the
20653          * drag and drop objects in the specified group(s).  This is in the
20654          * format that is stored in the drag and drop instance, so typical
20655          * usage is:
20656          * <code>
20657          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20658          * </code>
20659          * Alternatively:
20660          * <code>
20661          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20662          * </code>
20663          * @TODO this really should be an indexed array.  Alternatively this
20664          * method could accept both.
20665          * @method refreshCache
20666          * @param {Object} groups an associative array of groups to refresh
20667          * @static
20668          */
20669         refreshCache: function(groups) {
20670             for (var sGroup in groups) {
20671                 if ("string" != typeof sGroup) {
20672                     continue;
20673                 }
20674                 for (var i in this.ids[sGroup]) {
20675                     var oDD = this.ids[sGroup][i];
20676
20677                     if (this.isTypeOfDD(oDD)) {
20678                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20679                         var loc = this.getLocation(oDD);
20680                         if (loc) {
20681                             this.locationCache[oDD.id] = loc;
20682                         } else {
20683                             delete this.locationCache[oDD.id];
20684                             // this will unregister the drag and drop object if
20685                             // the element is not in a usable state
20686                             // oDD.unreg();
20687                         }
20688                     }
20689                 }
20690             }
20691         },
20692
20693         /**
20694          * This checks to make sure an element exists and is in the DOM.  The
20695          * main purpose is to handle cases where innerHTML is used to remove
20696          * drag and drop objects from the DOM.  IE provides an 'unspecified
20697          * error' when trying to access the offsetParent of such an element
20698          * @method verifyEl
20699          * @param {HTMLElement} el the element to check
20700          * @return {boolean} true if the element looks usable
20701          * @static
20702          */
20703         verifyEl: function(el) {
20704             if (el) {
20705                 var parent;
20706                 if(Roo.isIE){
20707                     try{
20708                         parent = el.offsetParent;
20709                     }catch(e){}
20710                 }else{
20711                     parent = el.offsetParent;
20712                 }
20713                 if (parent) {
20714                     return true;
20715                 }
20716             }
20717
20718             return false;
20719         },
20720
20721         /**
20722          * Returns a Region object containing the drag and drop element's position
20723          * and size, including the padding configured for it
20724          * @method getLocation
20725          * @param {DragDrop} oDD the drag and drop object to get the
20726          *                       location for
20727          * @return {Roo.lib.Region} a Region object representing the total area
20728          *                             the element occupies, including any padding
20729          *                             the instance is configured for.
20730          * @static
20731          */
20732         getLocation: function(oDD) {
20733             if (! this.isTypeOfDD(oDD)) {
20734                 return null;
20735             }
20736
20737             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20738
20739             try {
20740                 pos= Roo.lib.Dom.getXY(el);
20741             } catch (e) { }
20742
20743             if (!pos) {
20744                 return null;
20745             }
20746
20747             x1 = pos[0];
20748             x2 = x1 + el.offsetWidth;
20749             y1 = pos[1];
20750             y2 = y1 + el.offsetHeight;
20751
20752             t = y1 - oDD.padding[0];
20753             r = x2 + oDD.padding[1];
20754             b = y2 + oDD.padding[2];
20755             l = x1 - oDD.padding[3];
20756
20757             return new Roo.lib.Region( t, r, b, l );
20758         },
20759
20760         /**
20761          * Checks the cursor location to see if it over the target
20762          * @method isOverTarget
20763          * @param {Roo.lib.Point} pt The point to evaluate
20764          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20765          * @return {boolean} true if the mouse is over the target
20766          * @private
20767          * @static
20768          */
20769         isOverTarget: function(pt, oTarget, intersect) {
20770             // use cache if available
20771             var loc = this.locationCache[oTarget.id];
20772             if (!loc || !this.useCache) {
20773                 loc = this.getLocation(oTarget);
20774                 this.locationCache[oTarget.id] = loc;
20775
20776             }
20777
20778             if (!loc) {
20779                 return false;
20780             }
20781
20782             oTarget.cursorIsOver = loc.contains( pt );
20783
20784             // DragDrop is using this as a sanity check for the initial mousedown
20785             // in this case we are done.  In POINT mode, if the drag obj has no
20786             // contraints, we are also done. Otherwise we need to evaluate the
20787             // location of the target as related to the actual location of the
20788             // dragged element.
20789             var dc = this.dragCurrent;
20790             if (!dc || !dc.getTargetCoord ||
20791                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20792                 return oTarget.cursorIsOver;
20793             }
20794
20795             oTarget.overlap = null;
20796
20797             // Get the current location of the drag element, this is the
20798             // location of the mouse event less the delta that represents
20799             // where the original mousedown happened on the element.  We
20800             // need to consider constraints and ticks as well.
20801             var pos = dc.getTargetCoord(pt.x, pt.y);
20802
20803             var el = dc.getDragEl();
20804             var curRegion = new Roo.lib.Region( pos.y,
20805                                                    pos.x + el.offsetWidth,
20806                                                    pos.y + el.offsetHeight,
20807                                                    pos.x );
20808
20809             var overlap = curRegion.intersect(loc);
20810
20811             if (overlap) {
20812                 oTarget.overlap = overlap;
20813                 return (intersect) ? true : oTarget.cursorIsOver;
20814             } else {
20815                 return false;
20816             }
20817         },
20818
20819         /**
20820          * unload event handler
20821          * @method _onUnload
20822          * @private
20823          * @static
20824          */
20825         _onUnload: function(e, me) {
20826             Roo.dd.DragDropMgr.unregAll();
20827         },
20828
20829         /**
20830          * Cleans up the drag and drop events and objects.
20831          * @method unregAll
20832          * @private
20833          * @static
20834          */
20835         unregAll: function() {
20836
20837             if (this.dragCurrent) {
20838                 this.stopDrag();
20839                 this.dragCurrent = null;
20840             }
20841
20842             this._execOnAll("unreg", []);
20843
20844             for (i in this.elementCache) {
20845                 delete this.elementCache[i];
20846             }
20847
20848             this.elementCache = {};
20849             this.ids = {};
20850         },
20851
20852         /**
20853          * A cache of DOM elements
20854          * @property elementCache
20855          * @private
20856          * @static
20857          */
20858         elementCache: {},
20859
20860         /**
20861          * Get the wrapper for the DOM element specified
20862          * @method getElWrapper
20863          * @param {String} id the id of the element to get
20864          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20865          * @private
20866          * @deprecated This wrapper isn't that useful
20867          * @static
20868          */
20869         getElWrapper: function(id) {
20870             var oWrapper = this.elementCache[id];
20871             if (!oWrapper || !oWrapper.el) {
20872                 oWrapper = this.elementCache[id] =
20873                     new this.ElementWrapper(Roo.getDom(id));
20874             }
20875             return oWrapper;
20876         },
20877
20878         /**
20879          * Returns the actual DOM element
20880          * @method getElement
20881          * @param {String} id the id of the elment to get
20882          * @return {Object} The element
20883          * @deprecated use Roo.getDom instead
20884          * @static
20885          */
20886         getElement: function(id) {
20887             return Roo.getDom(id);
20888         },
20889
20890         /**
20891          * Returns the style property for the DOM element (i.e.,
20892          * document.getElById(id).style)
20893          * @method getCss
20894          * @param {String} id the id of the elment to get
20895          * @return {Object} The style property of the element
20896          * @deprecated use Roo.getDom instead
20897          * @static
20898          */
20899         getCss: function(id) {
20900             var el = Roo.getDom(id);
20901             return (el) ? el.style : null;
20902         },
20903
20904         /**
20905          * Inner class for cached elements
20906          * @class DragDropMgr.ElementWrapper
20907          * @for DragDropMgr
20908          * @private
20909          * @deprecated
20910          */
20911         ElementWrapper: function(el) {
20912                 /**
20913                  * The element
20914                  * @property el
20915                  */
20916                 this.el = el || null;
20917                 /**
20918                  * The element id
20919                  * @property id
20920                  */
20921                 this.id = this.el && el.id;
20922                 /**
20923                  * A reference to the style property
20924                  * @property css
20925                  */
20926                 this.css = this.el && el.style;
20927             },
20928
20929         /**
20930          * Returns the X position of an html element
20931          * @method getPosX
20932          * @param el the element for which to get the position
20933          * @return {int} the X coordinate
20934          * @for DragDropMgr
20935          * @deprecated use Roo.lib.Dom.getX instead
20936          * @static
20937          */
20938         getPosX: function(el) {
20939             return Roo.lib.Dom.getX(el);
20940         },
20941
20942         /**
20943          * Returns the Y position of an html element
20944          * @method getPosY
20945          * @param el the element for which to get the position
20946          * @return {int} the Y coordinate
20947          * @deprecated use Roo.lib.Dom.getY instead
20948          * @static
20949          */
20950         getPosY: function(el) {
20951             return Roo.lib.Dom.getY(el);
20952         },
20953
20954         /**
20955          * Swap two nodes.  In IE, we use the native method, for others we
20956          * emulate the IE behavior
20957          * @method swapNode
20958          * @param n1 the first node to swap
20959          * @param n2 the other node to swap
20960          * @static
20961          */
20962         swapNode: function(n1, n2) {
20963             if (n1.swapNode) {
20964                 n1.swapNode(n2);
20965             } else {
20966                 var p = n2.parentNode;
20967                 var s = n2.nextSibling;
20968
20969                 if (s == n1) {
20970                     p.insertBefore(n1, n2);
20971                 } else if (n2 == n1.nextSibling) {
20972                     p.insertBefore(n2, n1);
20973                 } else {
20974                     n1.parentNode.replaceChild(n2, n1);
20975                     p.insertBefore(n1, s);
20976                 }
20977             }
20978         },
20979
20980         /**
20981          * Returns the current scroll position
20982          * @method getScroll
20983          * @private
20984          * @static
20985          */
20986         getScroll: function () {
20987             var t, l, dde=document.documentElement, db=document.body;
20988             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20989                 t = dde.scrollTop;
20990                 l = dde.scrollLeft;
20991             } else if (db) {
20992                 t = db.scrollTop;
20993                 l = db.scrollLeft;
20994             } else {
20995
20996             }
20997             return { top: t, left: l };
20998         },
20999
21000         /**
21001          * Returns the specified element style property
21002          * @method getStyle
21003          * @param {HTMLElement} el          the element
21004          * @param {string}      styleProp   the style property
21005          * @return {string} The value of the style property
21006          * @deprecated use Roo.lib.Dom.getStyle
21007          * @static
21008          */
21009         getStyle: function(el, styleProp) {
21010             return Roo.fly(el).getStyle(styleProp);
21011         },
21012
21013         /**
21014          * Gets the scrollTop
21015          * @method getScrollTop
21016          * @return {int} the document's scrollTop
21017          * @static
21018          */
21019         getScrollTop: function () { return this.getScroll().top; },
21020
21021         /**
21022          * Gets the scrollLeft
21023          * @method getScrollLeft
21024          * @return {int} the document's scrollTop
21025          * @static
21026          */
21027         getScrollLeft: function () { return this.getScroll().left; },
21028
21029         /**
21030          * Sets the x/y position of an element to the location of the
21031          * target element.
21032          * @method moveToEl
21033          * @param {HTMLElement} moveEl      The element to move
21034          * @param {HTMLElement} targetEl    The position reference element
21035          * @static
21036          */
21037         moveToEl: function (moveEl, targetEl) {
21038             var aCoord = Roo.lib.Dom.getXY(targetEl);
21039             Roo.lib.Dom.setXY(moveEl, aCoord);
21040         },
21041
21042         /**
21043          * Numeric array sort function
21044          * @method numericSort
21045          * @static
21046          */
21047         numericSort: function(a, b) { return (a - b); },
21048
21049         /**
21050          * Internal counter
21051          * @property _timeoutCount
21052          * @private
21053          * @static
21054          */
21055         _timeoutCount: 0,
21056
21057         /**
21058          * Trying to make the load order less important.  Without this we get
21059          * an error if this file is loaded before the Event Utility.
21060          * @method _addListeners
21061          * @private
21062          * @static
21063          */
21064         _addListeners: function() {
21065             var DDM = Roo.dd.DDM;
21066             if ( Roo.lib.Event && document ) {
21067                 DDM._onLoad();
21068             } else {
21069                 if (DDM._timeoutCount > 2000) {
21070                 } else {
21071                     setTimeout(DDM._addListeners, 10);
21072                     if (document && document.body) {
21073                         DDM._timeoutCount += 1;
21074                     }
21075                 }
21076             }
21077         },
21078
21079         /**
21080          * Recursively searches the immediate parent and all child nodes for
21081          * the handle element in order to determine wheter or not it was
21082          * clicked.
21083          * @method handleWasClicked
21084          * @param node the html element to inspect
21085          * @static
21086          */
21087         handleWasClicked: function(node, id) {
21088             if (this.isHandle(id, node.id)) {
21089                 return true;
21090             } else {
21091                 // check to see if this is a text node child of the one we want
21092                 var p = node.parentNode;
21093
21094                 while (p) {
21095                     if (this.isHandle(id, p.id)) {
21096                         return true;
21097                     } else {
21098                         p = p.parentNode;
21099                     }
21100                 }
21101             }
21102
21103             return false;
21104         }
21105
21106     };
21107
21108 }();
21109
21110 // shorter alias, save a few bytes
21111 Roo.dd.DDM = Roo.dd.DragDropMgr;
21112 Roo.dd.DDM._addListeners();
21113
21114 }/*
21115  * Based on:
21116  * Ext JS Library 1.1.1
21117  * Copyright(c) 2006-2007, Ext JS, LLC.
21118  *
21119  * Originally Released Under LGPL - original licence link has changed is not relivant.
21120  *
21121  * Fork - LGPL
21122  * <script type="text/javascript">
21123  */
21124
21125 /**
21126  * @class Roo.dd.DD
21127  * A DragDrop implementation where the linked element follows the
21128  * mouse cursor during a drag.
21129  * @extends Roo.dd.DragDrop
21130  * @constructor
21131  * @param {String} id the id of the linked element
21132  * @param {String} sGroup the group of related DragDrop items
21133  * @param {object} config an object containing configurable attributes
21134  *                Valid properties for DD:
21135  *                    scroll
21136  */
21137 Roo.dd.DD = function(id, sGroup, config) {
21138     if (id) {
21139         this.init(id, sGroup, config);
21140     }
21141 };
21142
21143 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21144
21145     /**
21146      * When set to true, the utility automatically tries to scroll the browser
21147      * window wehn a drag and drop element is dragged near the viewport boundary.
21148      * Defaults to true.
21149      * @property scroll
21150      * @type boolean
21151      */
21152     scroll: true,
21153
21154     /**
21155      * Sets the pointer offset to the distance between the linked element's top
21156      * left corner and the location the element was clicked
21157      * @method autoOffset
21158      * @param {int} iPageX the X coordinate of the click
21159      * @param {int} iPageY the Y coordinate of the click
21160      */
21161     autoOffset: function(iPageX, iPageY) {
21162         var x = iPageX - this.startPageX;
21163         var y = iPageY - this.startPageY;
21164         this.setDelta(x, y);
21165     },
21166
21167     /**
21168      * Sets the pointer offset.  You can call this directly to force the
21169      * offset to be in a particular location (e.g., pass in 0,0 to set it
21170      * to the center of the object)
21171      * @method setDelta
21172      * @param {int} iDeltaX the distance from the left
21173      * @param {int} iDeltaY the distance from the top
21174      */
21175     setDelta: function(iDeltaX, iDeltaY) {
21176         this.deltaX = iDeltaX;
21177         this.deltaY = iDeltaY;
21178     },
21179
21180     /**
21181      * Sets the drag element to the location of the mousedown or click event,
21182      * maintaining the cursor location relative to the location on the element
21183      * that was clicked.  Override this if you want to place the element in a
21184      * location other than where the cursor is.
21185      * @method setDragElPos
21186      * @param {int} iPageX the X coordinate of the mousedown or drag event
21187      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21188      */
21189     setDragElPos: function(iPageX, iPageY) {
21190         // the first time we do this, we are going to check to make sure
21191         // the element has css positioning
21192
21193         var el = this.getDragEl();
21194         this.alignElWithMouse(el, iPageX, iPageY);
21195     },
21196
21197     /**
21198      * Sets the element to the location of the mousedown or click event,
21199      * maintaining the cursor location relative to the location on the element
21200      * that was clicked.  Override this if you want to place the element in a
21201      * location other than where the cursor is.
21202      * @method alignElWithMouse
21203      * @param {HTMLElement} el the element to move
21204      * @param {int} iPageX the X coordinate of the mousedown or drag event
21205      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21206      */
21207     alignElWithMouse: function(el, iPageX, iPageY) {
21208         var oCoord = this.getTargetCoord(iPageX, iPageY);
21209         var fly = el.dom ? el : Roo.fly(el);
21210         if (!this.deltaSetXY) {
21211             var aCoord = [oCoord.x, oCoord.y];
21212             fly.setXY(aCoord);
21213             var newLeft = fly.getLeft(true);
21214             var newTop  = fly.getTop(true);
21215             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21216         } else {
21217             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21218         }
21219
21220         this.cachePosition(oCoord.x, oCoord.y);
21221         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21222         return oCoord;
21223     },
21224
21225     /**
21226      * Saves the most recent position so that we can reset the constraints and
21227      * tick marks on-demand.  We need to know this so that we can calculate the
21228      * number of pixels the element is offset from its original position.
21229      * @method cachePosition
21230      * @param iPageX the current x position (optional, this just makes it so we
21231      * don't have to look it up again)
21232      * @param iPageY the current y position (optional, this just makes it so we
21233      * don't have to look it up again)
21234      */
21235     cachePosition: function(iPageX, iPageY) {
21236         if (iPageX) {
21237             this.lastPageX = iPageX;
21238             this.lastPageY = iPageY;
21239         } else {
21240             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21241             this.lastPageX = aCoord[0];
21242             this.lastPageY = aCoord[1];
21243         }
21244     },
21245
21246     /**
21247      * Auto-scroll the window if the dragged object has been moved beyond the
21248      * visible window boundary.
21249      * @method autoScroll
21250      * @param {int} x the drag element's x position
21251      * @param {int} y the drag element's y position
21252      * @param {int} h the height of the drag element
21253      * @param {int} w the width of the drag element
21254      * @private
21255      */
21256     autoScroll: function(x, y, h, w) {
21257
21258         if (this.scroll) {
21259             // The client height
21260             var clientH = Roo.lib.Dom.getViewWidth();
21261
21262             // The client width
21263             var clientW = Roo.lib.Dom.getViewHeight();
21264
21265             // The amt scrolled down
21266             var st = this.DDM.getScrollTop();
21267
21268             // The amt scrolled right
21269             var sl = this.DDM.getScrollLeft();
21270
21271             // Location of the bottom of the element
21272             var bot = h + y;
21273
21274             // Location of the right of the element
21275             var right = w + x;
21276
21277             // The distance from the cursor to the bottom of the visible area,
21278             // adjusted so that we don't scroll if the cursor is beyond the
21279             // element drag constraints
21280             var toBot = (clientH + st - y - this.deltaY);
21281
21282             // The distance from the cursor to the right of the visible area
21283             var toRight = (clientW + sl - x - this.deltaX);
21284
21285
21286             // How close to the edge the cursor must be before we scroll
21287             // var thresh = (document.all) ? 100 : 40;
21288             var thresh = 40;
21289
21290             // How many pixels to scroll per autoscroll op.  This helps to reduce
21291             // clunky scrolling. IE is more sensitive about this ... it needs this
21292             // value to be higher.
21293             var scrAmt = (document.all) ? 80 : 30;
21294
21295             // Scroll down if we are near the bottom of the visible page and the
21296             // obj extends below the crease
21297             if ( bot > clientH && toBot < thresh ) {
21298                 window.scrollTo(sl, st + scrAmt);
21299             }
21300
21301             // Scroll up if the window is scrolled down and the top of the object
21302             // goes above the top border
21303             if ( y < st && st > 0 && y - st < thresh ) {
21304                 window.scrollTo(sl, st - scrAmt);
21305             }
21306
21307             // Scroll right if the obj is beyond the right border and the cursor is
21308             // near the border.
21309             if ( right > clientW && toRight < thresh ) {
21310                 window.scrollTo(sl + scrAmt, st);
21311             }
21312
21313             // Scroll left if the window has been scrolled to the right and the obj
21314             // extends past the left border
21315             if ( x < sl && sl > 0 && x - sl < thresh ) {
21316                 window.scrollTo(sl - scrAmt, st);
21317             }
21318         }
21319     },
21320
21321     /**
21322      * Finds the location the element should be placed if we want to move
21323      * it to where the mouse location less the click offset would place us.
21324      * @method getTargetCoord
21325      * @param {int} iPageX the X coordinate of the click
21326      * @param {int} iPageY the Y coordinate of the click
21327      * @return an object that contains the coordinates (Object.x and Object.y)
21328      * @private
21329      */
21330     getTargetCoord: function(iPageX, iPageY) {
21331
21332
21333         var x = iPageX - this.deltaX;
21334         var y = iPageY - this.deltaY;
21335
21336         if (this.constrainX) {
21337             if (x < this.minX) { x = this.minX; }
21338             if (x > this.maxX) { x = this.maxX; }
21339         }
21340
21341         if (this.constrainY) {
21342             if (y < this.minY) { y = this.minY; }
21343             if (y > this.maxY) { y = this.maxY; }
21344         }
21345
21346         x = this.getTick(x, this.xTicks);
21347         y = this.getTick(y, this.yTicks);
21348
21349
21350         return {x:x, y:y};
21351     },
21352
21353     /*
21354      * Sets up config options specific to this class. Overrides
21355      * Roo.dd.DragDrop, but all versions of this method through the
21356      * inheritance chain are called
21357      */
21358     applyConfig: function() {
21359         Roo.dd.DD.superclass.applyConfig.call(this);
21360         this.scroll = (this.config.scroll !== false);
21361     },
21362
21363     /*
21364      * Event that fires prior to the onMouseDown event.  Overrides
21365      * Roo.dd.DragDrop.
21366      */
21367     b4MouseDown: function(e) {
21368         // this.resetConstraints();
21369         this.autoOffset(e.getPageX(),
21370                             e.getPageY());
21371     },
21372
21373     /*
21374      * Event that fires prior to the onDrag event.  Overrides
21375      * Roo.dd.DragDrop.
21376      */
21377     b4Drag: function(e) {
21378         this.setDragElPos(e.getPageX(),
21379                             e.getPageY());
21380     },
21381
21382     toString: function() {
21383         return ("DD " + this.id);
21384     }
21385
21386     //////////////////////////////////////////////////////////////////////////
21387     // Debugging ygDragDrop events that can be overridden
21388     //////////////////////////////////////////////////////////////////////////
21389     /*
21390     startDrag: function(x, y) {
21391     },
21392
21393     onDrag: function(e) {
21394     },
21395
21396     onDragEnter: function(e, id) {
21397     },
21398
21399     onDragOver: function(e, id) {
21400     },
21401
21402     onDragOut: function(e, id) {
21403     },
21404
21405     onDragDrop: function(e, id) {
21406     },
21407
21408     endDrag: function(e) {
21409     }
21410
21411     */
21412
21413 });/*
21414  * Based on:
21415  * Ext JS Library 1.1.1
21416  * Copyright(c) 2006-2007, Ext JS, LLC.
21417  *
21418  * Originally Released Under LGPL - original licence link has changed is not relivant.
21419  *
21420  * Fork - LGPL
21421  * <script type="text/javascript">
21422  */
21423
21424 /**
21425  * @class Roo.dd.DDProxy
21426  * A DragDrop implementation that inserts an empty, bordered div into
21427  * the document that follows the cursor during drag operations.  At the time of
21428  * the click, the frame div is resized to the dimensions of the linked html
21429  * element, and moved to the exact location of the linked element.
21430  *
21431  * References to the "frame" element refer to the single proxy element that
21432  * was created to be dragged in place of all DDProxy elements on the
21433  * page.
21434  *
21435  * @extends Roo.dd.DD
21436  * @constructor
21437  * @param {String} id the id of the linked html element
21438  * @param {String} sGroup the group of related DragDrop objects
21439  * @param {object} config an object containing configurable attributes
21440  *                Valid properties for DDProxy in addition to those in DragDrop:
21441  *                   resizeFrame, centerFrame, dragElId
21442  */
21443 Roo.dd.DDProxy = function(id, sGroup, config) {
21444     if (id) {
21445         this.init(id, sGroup, config);
21446         this.initFrame();
21447     }
21448 };
21449
21450 /**
21451  * The default drag frame div id
21452  * @property Roo.dd.DDProxy.dragElId
21453  * @type String
21454  * @static
21455  */
21456 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21457
21458 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21459
21460     /**
21461      * By default we resize the drag frame to be the same size as the element
21462      * we want to drag (this is to get the frame effect).  We can turn it off
21463      * if we want a different behavior.
21464      * @property resizeFrame
21465      * @type boolean
21466      */
21467     resizeFrame: true,
21468
21469     /**
21470      * By default the frame is positioned exactly where the drag element is, so
21471      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21472      * you do not have constraints on the obj is to have the drag frame centered
21473      * around the cursor.  Set centerFrame to true for this effect.
21474      * @property centerFrame
21475      * @type boolean
21476      */
21477     centerFrame: false,
21478
21479     /**
21480      * Creates the proxy element if it does not yet exist
21481      * @method createFrame
21482      */
21483     createFrame: function() {
21484         var self = this;
21485         var body = document.body;
21486
21487         if (!body || !body.firstChild) {
21488             setTimeout( function() { self.createFrame(); }, 50 );
21489             return;
21490         }
21491
21492         var div = this.getDragEl();
21493
21494         if (!div) {
21495             div    = document.createElement("div");
21496             div.id = this.dragElId;
21497             var s  = div.style;
21498
21499             s.position   = "absolute";
21500             s.visibility = "hidden";
21501             s.cursor     = "move";
21502             s.border     = "2px solid #aaa";
21503             s.zIndex     = 999;
21504
21505             // appendChild can blow up IE if invoked prior to the window load event
21506             // while rendering a table.  It is possible there are other scenarios
21507             // that would cause this to happen as well.
21508             body.insertBefore(div, body.firstChild);
21509         }
21510     },
21511
21512     /**
21513      * Initialization for the drag frame element.  Must be called in the
21514      * constructor of all subclasses
21515      * @method initFrame
21516      */
21517     initFrame: function() {
21518         this.createFrame();
21519     },
21520
21521     applyConfig: function() {
21522         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21523
21524         this.resizeFrame = (this.config.resizeFrame !== false);
21525         this.centerFrame = (this.config.centerFrame);
21526         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21527     },
21528
21529     /**
21530      * Resizes the drag frame to the dimensions of the clicked object, positions
21531      * it over the object, and finally displays it
21532      * @method showFrame
21533      * @param {int} iPageX X click position
21534      * @param {int} iPageY Y click position
21535      * @private
21536      */
21537     showFrame: function(iPageX, iPageY) {
21538         var el = this.getEl();
21539         var dragEl = this.getDragEl();
21540         var s = dragEl.style;
21541
21542         this._resizeProxy();
21543
21544         if (this.centerFrame) {
21545             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21546                            Math.round(parseInt(s.height, 10)/2) );
21547         }
21548
21549         this.setDragElPos(iPageX, iPageY);
21550
21551         Roo.fly(dragEl).show();
21552     },
21553
21554     /**
21555      * The proxy is automatically resized to the dimensions of the linked
21556      * element when a drag is initiated, unless resizeFrame is set to false
21557      * @method _resizeProxy
21558      * @private
21559      */
21560     _resizeProxy: function() {
21561         if (this.resizeFrame) {
21562             var el = this.getEl();
21563             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21564         }
21565     },
21566
21567     // overrides Roo.dd.DragDrop
21568     b4MouseDown: function(e) {
21569         var x = e.getPageX();
21570         var y = e.getPageY();
21571         this.autoOffset(x, y);
21572         this.setDragElPos(x, y);
21573     },
21574
21575     // overrides Roo.dd.DragDrop
21576     b4StartDrag: function(x, y) {
21577         // show the drag frame
21578         this.showFrame(x, y);
21579     },
21580
21581     // overrides Roo.dd.DragDrop
21582     b4EndDrag: function(e) {
21583         Roo.fly(this.getDragEl()).hide();
21584     },
21585
21586     // overrides Roo.dd.DragDrop
21587     // By default we try to move the element to the last location of the frame.
21588     // This is so that the default behavior mirrors that of Roo.dd.DD.
21589     endDrag: function(e) {
21590
21591         var lel = this.getEl();
21592         var del = this.getDragEl();
21593
21594         // Show the drag frame briefly so we can get its position
21595         del.style.visibility = "";
21596
21597         this.beforeMove();
21598         // Hide the linked element before the move to get around a Safari
21599         // rendering bug.
21600         lel.style.visibility = "hidden";
21601         Roo.dd.DDM.moveToEl(lel, del);
21602         del.style.visibility = "hidden";
21603         lel.style.visibility = "";
21604
21605         this.afterDrag();
21606     },
21607
21608     beforeMove : function(){
21609
21610     },
21611
21612     afterDrag : function(){
21613
21614     },
21615
21616     toString: function() {
21617         return ("DDProxy " + this.id);
21618     }
21619
21620 });
21621 /*
21622  * Based on:
21623  * Ext JS Library 1.1.1
21624  * Copyright(c) 2006-2007, Ext JS, LLC.
21625  *
21626  * Originally Released Under LGPL - original licence link has changed is not relivant.
21627  *
21628  * Fork - LGPL
21629  * <script type="text/javascript">
21630  */
21631
21632  /**
21633  * @class Roo.dd.DDTarget
21634  * A DragDrop implementation that does not move, but can be a drop
21635  * target.  You would get the same result by simply omitting implementation
21636  * for the event callbacks, but this way we reduce the processing cost of the
21637  * event listener and the callbacks.
21638  * @extends Roo.dd.DragDrop
21639  * @constructor
21640  * @param {String} id the id of the element that is a drop target
21641  * @param {String} sGroup the group of related DragDrop objects
21642  * @param {object} config an object containing configurable attributes
21643  *                 Valid properties for DDTarget in addition to those in
21644  *                 DragDrop:
21645  *                    none
21646  */
21647 Roo.dd.DDTarget = function(id, sGroup, config) {
21648     if (id) {
21649         this.initTarget(id, sGroup, config);
21650     }
21651     if (config && (config.listeners || config.events)) { 
21652         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21653             listeners : config.listeners || {}, 
21654             events : config.events || {} 
21655         });    
21656     }
21657 };
21658
21659 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21660 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21661     toString: function() {
21662         return ("DDTarget " + this.id);
21663     }
21664 });
21665 /*
21666  * Based on:
21667  * Ext JS Library 1.1.1
21668  * Copyright(c) 2006-2007, Ext JS, LLC.
21669  *
21670  * Originally Released Under LGPL - original licence link has changed is not relivant.
21671  *
21672  * Fork - LGPL
21673  * <script type="text/javascript">
21674  */
21675  
21676
21677 /**
21678  * @class Roo.dd.ScrollManager
21679  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21680  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21681  * @singleton
21682  */
21683 Roo.dd.ScrollManager = function(){
21684     var ddm = Roo.dd.DragDropMgr;
21685     var els = {};
21686     var dragEl = null;
21687     var proc = {};
21688     
21689     
21690     
21691     var onStop = function(e){
21692         dragEl = null;
21693         clearProc();
21694     };
21695     
21696     var triggerRefresh = function(){
21697         if(ddm.dragCurrent){
21698              ddm.refreshCache(ddm.dragCurrent.groups);
21699         }
21700     };
21701     
21702     var doScroll = function(){
21703         if(ddm.dragCurrent){
21704             var dds = Roo.dd.ScrollManager;
21705             if(!dds.animate){
21706                 if(proc.el.scroll(proc.dir, dds.increment)){
21707                     triggerRefresh();
21708                 }
21709             }else{
21710                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21711             }
21712         }
21713     };
21714     
21715     var clearProc = function(){
21716         if(proc.id){
21717             clearInterval(proc.id);
21718         }
21719         proc.id = 0;
21720         proc.el = null;
21721         proc.dir = "";
21722     };
21723     
21724     var startProc = function(el, dir){
21725          Roo.log('scroll startproc');
21726         clearProc();
21727         proc.el = el;
21728         proc.dir = dir;
21729         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21730     };
21731     
21732     var onFire = function(e, isDrop){
21733        
21734         if(isDrop || !ddm.dragCurrent){ return; }
21735         var dds = Roo.dd.ScrollManager;
21736         if(!dragEl || dragEl != ddm.dragCurrent){
21737             dragEl = ddm.dragCurrent;
21738             // refresh regions on drag start
21739             dds.refreshCache();
21740         }
21741         
21742         var xy = Roo.lib.Event.getXY(e);
21743         var pt = new Roo.lib.Point(xy[0], xy[1]);
21744         for(var id in els){
21745             var el = els[id], r = el._region;
21746             if(r && r.contains(pt) && el.isScrollable()){
21747                 if(r.bottom - pt.y <= dds.thresh){
21748                     if(proc.el != el){
21749                         startProc(el, "down");
21750                     }
21751                     return;
21752                 }else if(r.right - pt.x <= dds.thresh){
21753                     if(proc.el != el){
21754                         startProc(el, "left");
21755                     }
21756                     return;
21757                 }else if(pt.y - r.top <= dds.thresh){
21758                     if(proc.el != el){
21759                         startProc(el, "up");
21760                     }
21761                     return;
21762                 }else if(pt.x - r.left <= dds.thresh){
21763                     if(proc.el != el){
21764                         startProc(el, "right");
21765                     }
21766                     return;
21767                 }
21768             }
21769         }
21770         clearProc();
21771     };
21772     
21773     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21774     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21775     
21776     return {
21777         /**
21778          * Registers new overflow element(s) to auto scroll
21779          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21780          */
21781         register : function(el){
21782             if(el instanceof Array){
21783                 for(var i = 0, len = el.length; i < len; i++) {
21784                         this.register(el[i]);
21785                 }
21786             }else{
21787                 el = Roo.get(el);
21788                 els[el.id] = el;
21789             }
21790             Roo.dd.ScrollManager.els = els;
21791         },
21792         
21793         /**
21794          * Unregisters overflow element(s) so they are no longer scrolled
21795          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21796          */
21797         unregister : function(el){
21798             if(el instanceof Array){
21799                 for(var i = 0, len = el.length; i < len; i++) {
21800                         this.unregister(el[i]);
21801                 }
21802             }else{
21803                 el = Roo.get(el);
21804                 delete els[el.id];
21805             }
21806         },
21807         
21808         /**
21809          * The number of pixels from the edge of a container the pointer needs to be to 
21810          * trigger scrolling (defaults to 25)
21811          * @type Number
21812          */
21813         thresh : 25,
21814         
21815         /**
21816          * The number of pixels to scroll in each scroll increment (defaults to 50)
21817          * @type Number
21818          */
21819         increment : 100,
21820         
21821         /**
21822          * The frequency of scrolls in milliseconds (defaults to 500)
21823          * @type Number
21824          */
21825         frequency : 500,
21826         
21827         /**
21828          * True to animate the scroll (defaults to true)
21829          * @type Boolean
21830          */
21831         animate: true,
21832         
21833         /**
21834          * The animation duration in seconds - 
21835          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21836          * @type Number
21837          */
21838         animDuration: .4,
21839         
21840         /**
21841          * Manually trigger a cache refresh.
21842          */
21843         refreshCache : function(){
21844             for(var id in els){
21845                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21846                     els[id]._region = els[id].getRegion();
21847                 }
21848             }
21849         }
21850     };
21851 }();/*
21852  * Based on:
21853  * Ext JS Library 1.1.1
21854  * Copyright(c) 2006-2007, Ext JS, LLC.
21855  *
21856  * Originally Released Under LGPL - original licence link has changed is not relivant.
21857  *
21858  * Fork - LGPL
21859  * <script type="text/javascript">
21860  */
21861  
21862
21863 /**
21864  * @class Roo.dd.Registry
21865  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21866  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21867  * @singleton
21868  */
21869 Roo.dd.Registry = function(){
21870     var elements = {}; 
21871     var handles = {}; 
21872     var autoIdSeed = 0;
21873
21874     var getId = function(el, autogen){
21875         if(typeof el == "string"){
21876             return el;
21877         }
21878         var id = el.id;
21879         if(!id && autogen !== false){
21880             id = "roodd-" + (++autoIdSeed);
21881             el.id = id;
21882         }
21883         return id;
21884     };
21885     
21886     return {
21887     /**
21888      * Register a drag drop element
21889      * @param {String|HTMLElement} element The id or DOM node to register
21890      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21891      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21892      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21893      * populated in the data object (if applicable):
21894      * <pre>
21895 Value      Description<br />
21896 ---------  ------------------------------------------<br />
21897 handles    Array of DOM nodes that trigger dragging<br />
21898            for the element being registered<br />
21899 isHandle   True if the element passed in triggers<br />
21900            dragging itself, else false
21901 </pre>
21902      */
21903         register : function(el, data){
21904             data = data || {};
21905             if(typeof el == "string"){
21906                 el = document.getElementById(el);
21907             }
21908             data.ddel = el;
21909             elements[getId(el)] = data;
21910             if(data.isHandle !== false){
21911                 handles[data.ddel.id] = data;
21912             }
21913             if(data.handles){
21914                 var hs = data.handles;
21915                 for(var i = 0, len = hs.length; i < len; i++){
21916                         handles[getId(hs[i])] = data;
21917                 }
21918             }
21919         },
21920
21921     /**
21922      * Unregister a drag drop element
21923      * @param {String|HTMLElement}  element The id or DOM node to unregister
21924      */
21925         unregister : function(el){
21926             var id = getId(el, false);
21927             var data = elements[id];
21928             if(data){
21929                 delete elements[id];
21930                 if(data.handles){
21931                     var hs = data.handles;
21932                     for(var i = 0, len = hs.length; i < len; i++){
21933                         delete handles[getId(hs[i], false)];
21934                     }
21935                 }
21936             }
21937         },
21938
21939     /**
21940      * Returns the handle registered for a DOM Node by id
21941      * @param {String|HTMLElement} id The DOM node or id to look up
21942      * @return {Object} handle The custom handle data
21943      */
21944         getHandle : function(id){
21945             if(typeof id != "string"){ // must be element?
21946                 id = id.id;
21947             }
21948             return handles[id];
21949         },
21950
21951     /**
21952      * Returns the handle that is registered for the DOM node that is the target of the event
21953      * @param {Event} e The event
21954      * @return {Object} handle The custom handle data
21955      */
21956         getHandleFromEvent : function(e){
21957             var t = Roo.lib.Event.getTarget(e);
21958             return t ? handles[t.id] : null;
21959         },
21960
21961     /**
21962      * Returns a custom data object that is registered for a DOM node by id
21963      * @param {String|HTMLElement} id The DOM node or id to look up
21964      * @return {Object} data The custom data
21965      */
21966         getTarget : function(id){
21967             if(typeof id != "string"){ // must be element?
21968                 id = id.id;
21969             }
21970             return elements[id];
21971         },
21972
21973     /**
21974      * Returns a custom data object that is registered for the DOM node that is the target of the event
21975      * @param {Event} e The event
21976      * @return {Object} data The custom data
21977      */
21978         getTargetFromEvent : function(e){
21979             var t = Roo.lib.Event.getTarget(e);
21980             return t ? elements[t.id] || handles[t.id] : null;
21981         }
21982     };
21983 }();/*
21984  * Based on:
21985  * Ext JS Library 1.1.1
21986  * Copyright(c) 2006-2007, Ext JS, LLC.
21987  *
21988  * Originally Released Under LGPL - original licence link has changed is not relivant.
21989  *
21990  * Fork - LGPL
21991  * <script type="text/javascript">
21992  */
21993  
21994
21995 /**
21996  * @class Roo.dd.StatusProxy
21997  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21998  * default drag proxy used by all Roo.dd components.
21999  * @constructor
22000  * @param {Object} config
22001  */
22002 Roo.dd.StatusProxy = function(config){
22003     Roo.apply(this, config);
22004     this.id = this.id || Roo.id();
22005     this.el = new Roo.Layer({
22006         dh: {
22007             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22008                 {tag: "div", cls: "x-dd-drop-icon"},
22009                 {tag: "div", cls: "x-dd-drag-ghost"}
22010             ]
22011         }, 
22012         shadow: !config || config.shadow !== false
22013     });
22014     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22015     this.dropStatus = this.dropNotAllowed;
22016 };
22017
22018 Roo.dd.StatusProxy.prototype = {
22019     /**
22020      * @cfg {String} dropAllowed
22021      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22022      */
22023     dropAllowed : "x-dd-drop-ok",
22024     /**
22025      * @cfg {String} dropNotAllowed
22026      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22027      */
22028     dropNotAllowed : "x-dd-drop-nodrop",
22029
22030     /**
22031      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22032      * over the current target element.
22033      * @param {String} cssClass The css class for the new drop status indicator image
22034      */
22035     setStatus : function(cssClass){
22036         cssClass = cssClass || this.dropNotAllowed;
22037         if(this.dropStatus != cssClass){
22038             this.el.replaceClass(this.dropStatus, cssClass);
22039             this.dropStatus = cssClass;
22040         }
22041     },
22042
22043     /**
22044      * Resets the status indicator to the default dropNotAllowed value
22045      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22046      */
22047     reset : function(clearGhost){
22048         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22049         this.dropStatus = this.dropNotAllowed;
22050         if(clearGhost){
22051             this.ghost.update("");
22052         }
22053     },
22054
22055     /**
22056      * Updates the contents of the ghost element
22057      * @param {String} html The html that will replace the current innerHTML of the ghost element
22058      */
22059     update : function(html){
22060         if(typeof html == "string"){
22061             this.ghost.update(html);
22062         }else{
22063             this.ghost.update("");
22064             html.style.margin = "0";
22065             this.ghost.dom.appendChild(html);
22066         }
22067         // ensure float = none set?? cant remember why though.
22068         var el = this.ghost.dom.firstChild;
22069                 if(el){
22070                         Roo.fly(el).setStyle('float', 'none');
22071                 }
22072     },
22073     
22074     /**
22075      * Returns the underlying proxy {@link Roo.Layer}
22076      * @return {Roo.Layer} el
22077     */
22078     getEl : function(){
22079         return this.el;
22080     },
22081
22082     /**
22083      * Returns the ghost element
22084      * @return {Roo.Element} el
22085      */
22086     getGhost : function(){
22087         return this.ghost;
22088     },
22089
22090     /**
22091      * Hides the proxy
22092      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22093      */
22094     hide : function(clear){
22095         this.el.hide();
22096         if(clear){
22097             this.reset(true);
22098         }
22099     },
22100
22101     /**
22102      * Stops the repair animation if it's currently running
22103      */
22104     stop : function(){
22105         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22106             this.anim.stop();
22107         }
22108     },
22109
22110     /**
22111      * Displays this proxy
22112      */
22113     show : function(){
22114         this.el.show();
22115     },
22116
22117     /**
22118      * Force the Layer to sync its shadow and shim positions to the element
22119      */
22120     sync : function(){
22121         this.el.sync();
22122     },
22123
22124     /**
22125      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22126      * invalid drop operation by the item being dragged.
22127      * @param {Array} xy The XY position of the element ([x, y])
22128      * @param {Function} callback The function to call after the repair is complete
22129      * @param {Object} scope The scope in which to execute the callback
22130      */
22131     repair : function(xy, callback, scope){
22132         this.callback = callback;
22133         this.scope = scope;
22134         if(xy && this.animRepair !== false){
22135             this.el.addClass("x-dd-drag-repair");
22136             this.el.hideUnders(true);
22137             this.anim = this.el.shift({
22138                 duration: this.repairDuration || .5,
22139                 easing: 'easeOut',
22140                 xy: xy,
22141                 stopFx: true,
22142                 callback: this.afterRepair,
22143                 scope: this
22144             });
22145         }else{
22146             this.afterRepair();
22147         }
22148     },
22149
22150     // private
22151     afterRepair : function(){
22152         this.hide(true);
22153         if(typeof this.callback == "function"){
22154             this.callback.call(this.scope || this);
22155         }
22156         this.callback = null;
22157         this.scope = null;
22158     }
22159 };/*
22160  * Based on:
22161  * Ext JS Library 1.1.1
22162  * Copyright(c) 2006-2007, Ext JS, LLC.
22163  *
22164  * Originally Released Under LGPL - original licence link has changed is not relivant.
22165  *
22166  * Fork - LGPL
22167  * <script type="text/javascript">
22168  */
22169
22170 /**
22171  * @class Roo.dd.DragSource
22172  * @extends Roo.dd.DDProxy
22173  * A simple class that provides the basic implementation needed to make any element draggable.
22174  * @constructor
22175  * @param {String/HTMLElement/Element} el The container element
22176  * @param {Object} config
22177  */
22178 Roo.dd.DragSource = function(el, config){
22179     this.el = Roo.get(el);
22180     this.dragData = {};
22181     
22182     Roo.apply(this, config);
22183     
22184     if(!this.proxy){
22185         this.proxy = new Roo.dd.StatusProxy();
22186     }
22187
22188     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22189           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22190     
22191     this.dragging = false;
22192 };
22193
22194 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22195     /**
22196      * @cfg {String} dropAllowed
22197      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22198      */
22199     dropAllowed : "x-dd-drop-ok",
22200     /**
22201      * @cfg {String} dropNotAllowed
22202      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22203      */
22204     dropNotAllowed : "x-dd-drop-nodrop",
22205
22206     /**
22207      * Returns the data object associated with this drag source
22208      * @return {Object} data An object containing arbitrary data
22209      */
22210     getDragData : function(e){
22211         return this.dragData;
22212     },
22213
22214     // private
22215     onDragEnter : function(e, id){
22216         var target = Roo.dd.DragDropMgr.getDDById(id);
22217         this.cachedTarget = target;
22218         if(this.beforeDragEnter(target, e, id) !== false){
22219             if(target.isNotifyTarget){
22220                 var status = target.notifyEnter(this, e, this.dragData);
22221                 this.proxy.setStatus(status);
22222             }else{
22223                 this.proxy.setStatus(this.dropAllowed);
22224             }
22225             
22226             if(this.afterDragEnter){
22227                 /**
22228                  * An empty function by default, but provided so that you can perform a custom action
22229                  * when the dragged item enters the drop target by providing an implementation.
22230                  * @param {Roo.dd.DragDrop} target The drop target
22231                  * @param {Event} e The event object
22232                  * @param {String} id The id of the dragged element
22233                  * @method afterDragEnter
22234                  */
22235                 this.afterDragEnter(target, e, id);
22236             }
22237         }
22238     },
22239
22240     /**
22241      * An empty function by default, but provided so that you can perform a custom action
22242      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22243      * @param {Roo.dd.DragDrop} target The drop target
22244      * @param {Event} e The event object
22245      * @param {String} id The id of the dragged element
22246      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22247      */
22248     beforeDragEnter : function(target, e, id){
22249         return true;
22250     },
22251
22252     // private
22253     alignElWithMouse: function() {
22254         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22255         this.proxy.sync();
22256     },
22257
22258     // private
22259     onDragOver : function(e, id){
22260         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22261         if(this.beforeDragOver(target, e, id) !== false){
22262             if(target.isNotifyTarget){
22263                 var status = target.notifyOver(this, e, this.dragData);
22264                 this.proxy.setStatus(status);
22265             }
22266
22267             if(this.afterDragOver){
22268                 /**
22269                  * An empty function by default, but provided so that you can perform a custom action
22270                  * while the dragged item is over the drop target by providing an implementation.
22271                  * @param {Roo.dd.DragDrop} target The drop target
22272                  * @param {Event} e The event object
22273                  * @param {String} id The id of the dragged element
22274                  * @method afterDragOver
22275                  */
22276                 this.afterDragOver(target, e, id);
22277             }
22278         }
22279     },
22280
22281     /**
22282      * An empty function by default, but provided so that you can perform a custom action
22283      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22284      * @param {Roo.dd.DragDrop} target The drop target
22285      * @param {Event} e The event object
22286      * @param {String} id The id of the dragged element
22287      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22288      */
22289     beforeDragOver : function(target, e, id){
22290         return true;
22291     },
22292
22293     // private
22294     onDragOut : function(e, id){
22295         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22296         if(this.beforeDragOut(target, e, id) !== false){
22297             if(target.isNotifyTarget){
22298                 target.notifyOut(this, e, this.dragData);
22299             }
22300             this.proxy.reset();
22301             if(this.afterDragOut){
22302                 /**
22303                  * An empty function by default, but provided so that you can perform a custom action
22304                  * after the dragged item is dragged out of the target without dropping.
22305                  * @param {Roo.dd.DragDrop} target The drop target
22306                  * @param {Event} e The event object
22307                  * @param {String} id The id of the dragged element
22308                  * @method afterDragOut
22309                  */
22310                 this.afterDragOut(target, e, id);
22311             }
22312         }
22313         this.cachedTarget = null;
22314     },
22315
22316     /**
22317      * An empty function by default, but provided so that you can perform a custom action before the dragged
22318      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22319      * @param {Roo.dd.DragDrop} target The drop target
22320      * @param {Event} e The event object
22321      * @param {String} id The id of the dragged element
22322      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22323      */
22324     beforeDragOut : function(target, e, id){
22325         return true;
22326     },
22327     
22328     // private
22329     onDragDrop : function(e, id){
22330         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22331         if(this.beforeDragDrop(target, e, id) !== false){
22332             if(target.isNotifyTarget){
22333                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22334                     this.onValidDrop(target, e, id);
22335                 }else{
22336                     this.onInvalidDrop(target, e, id);
22337                 }
22338             }else{
22339                 this.onValidDrop(target, e, id);
22340             }
22341             
22342             if(this.afterDragDrop){
22343                 /**
22344                  * An empty function by default, but provided so that you can perform a custom action
22345                  * after a valid drag drop has occurred by providing an implementation.
22346                  * @param {Roo.dd.DragDrop} target The drop target
22347                  * @param {Event} e The event object
22348                  * @param {String} id The id of the dropped element
22349                  * @method afterDragDrop
22350                  */
22351                 this.afterDragDrop(target, e, id);
22352             }
22353         }
22354         delete this.cachedTarget;
22355     },
22356
22357     /**
22358      * An empty function by default, but provided so that you can perform a custom action before the dragged
22359      * item is dropped onto the target and optionally cancel the onDragDrop.
22360      * @param {Roo.dd.DragDrop} target The drop target
22361      * @param {Event} e The event object
22362      * @param {String} id The id of the dragged element
22363      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22364      */
22365     beforeDragDrop : function(target, e, id){
22366         return true;
22367     },
22368
22369     // private
22370     onValidDrop : function(target, e, id){
22371         this.hideProxy();
22372         if(this.afterValidDrop){
22373             /**
22374              * An empty function by default, but provided so that you can perform a custom action
22375              * after a valid drop has occurred by providing an implementation.
22376              * @param {Object} target The target DD 
22377              * @param {Event} e The event object
22378              * @param {String} id The id of the dropped element
22379              * @method afterInvalidDrop
22380              */
22381             this.afterValidDrop(target, e, id);
22382         }
22383     },
22384
22385     // private
22386     getRepairXY : function(e, data){
22387         return this.el.getXY();  
22388     },
22389
22390     // private
22391     onInvalidDrop : function(target, e, id){
22392         this.beforeInvalidDrop(target, e, id);
22393         if(this.cachedTarget){
22394             if(this.cachedTarget.isNotifyTarget){
22395                 this.cachedTarget.notifyOut(this, e, this.dragData);
22396             }
22397             this.cacheTarget = null;
22398         }
22399         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22400
22401         if(this.afterInvalidDrop){
22402             /**
22403              * An empty function by default, but provided so that you can perform a custom action
22404              * after an invalid drop has occurred by providing an implementation.
22405              * @param {Event} e The event object
22406              * @param {String} id The id of the dropped element
22407              * @method afterInvalidDrop
22408              */
22409             this.afterInvalidDrop(e, id);
22410         }
22411     },
22412
22413     // private
22414     afterRepair : function(){
22415         if(Roo.enableFx){
22416             this.el.highlight(this.hlColor || "c3daf9");
22417         }
22418         this.dragging = false;
22419     },
22420
22421     /**
22422      * An empty function by default, but provided so that you can perform a custom action after an invalid
22423      * drop has occurred.
22424      * @param {Roo.dd.DragDrop} target The drop target
22425      * @param {Event} e The event object
22426      * @param {String} id The id of the dragged element
22427      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22428      */
22429     beforeInvalidDrop : function(target, e, id){
22430         return true;
22431     },
22432
22433     // private
22434     handleMouseDown : function(e){
22435         if(this.dragging) {
22436             return;
22437         }
22438         var data = this.getDragData(e);
22439         if(data && this.onBeforeDrag(data, e) !== false){
22440             this.dragData = data;
22441             this.proxy.stop();
22442             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22443         } 
22444     },
22445
22446     /**
22447      * An empty function by default, but provided so that you can perform a custom action before the initial
22448      * drag event begins and optionally cancel it.
22449      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22450      * @param {Event} e The event object
22451      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22452      */
22453     onBeforeDrag : function(data, e){
22454         return true;
22455     },
22456
22457     /**
22458      * An empty function by default, but provided so that you can perform a custom action once the initial
22459      * drag event has begun.  The drag cannot be canceled from this function.
22460      * @param {Number} x The x position of the click on the dragged object
22461      * @param {Number} y The y position of the click on the dragged object
22462      */
22463     onStartDrag : Roo.emptyFn,
22464
22465     // private - YUI override
22466     startDrag : function(x, y){
22467         this.proxy.reset();
22468         this.dragging = true;
22469         this.proxy.update("");
22470         this.onInitDrag(x, y);
22471         this.proxy.show();
22472     },
22473
22474     // private
22475     onInitDrag : function(x, y){
22476         var clone = this.el.dom.cloneNode(true);
22477         clone.id = Roo.id(); // prevent duplicate ids
22478         this.proxy.update(clone);
22479         this.onStartDrag(x, y);
22480         return true;
22481     },
22482
22483     /**
22484      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22485      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22486      */
22487     getProxy : function(){
22488         return this.proxy;  
22489     },
22490
22491     /**
22492      * Hides the drag source's {@link Roo.dd.StatusProxy}
22493      */
22494     hideProxy : function(){
22495         this.proxy.hide();  
22496         this.proxy.reset(true);
22497         this.dragging = false;
22498     },
22499
22500     // private
22501     triggerCacheRefresh : function(){
22502         Roo.dd.DDM.refreshCache(this.groups);
22503     },
22504
22505     // private - override to prevent hiding
22506     b4EndDrag: function(e) {
22507     },
22508
22509     // private - override to prevent moving
22510     endDrag : function(e){
22511         this.onEndDrag(this.dragData, e);
22512     },
22513
22514     // private
22515     onEndDrag : function(data, e){
22516     },
22517     
22518     // private - pin to cursor
22519     autoOffset : function(x, y) {
22520         this.setDelta(-12, -20);
22521     }    
22522 });/*
22523  * Based on:
22524  * Ext JS Library 1.1.1
22525  * Copyright(c) 2006-2007, Ext JS, LLC.
22526  *
22527  * Originally Released Under LGPL - original licence link has changed is not relivant.
22528  *
22529  * Fork - LGPL
22530  * <script type="text/javascript">
22531  */
22532
22533
22534 /**
22535  * @class Roo.dd.DropTarget
22536  * @extends Roo.dd.DDTarget
22537  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22538  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22539  * @constructor
22540  * @param {String/HTMLElement/Element} el The container element
22541  * @param {Object} config
22542  */
22543 Roo.dd.DropTarget = function(el, config){
22544     this.el = Roo.get(el);
22545     
22546     var listeners = false; ;
22547     if (config && config.listeners) {
22548         listeners= config.listeners;
22549         delete config.listeners;
22550     }
22551     Roo.apply(this, config);
22552     
22553     if(this.containerScroll){
22554         Roo.dd.ScrollManager.register(this.el);
22555     }
22556     this.addEvents( {
22557          /**
22558          * @scope Roo.dd.DropTarget
22559          */
22560          
22561          /**
22562          * @event enter
22563          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22564          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22565          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22566          * 
22567          * IMPORTANT : it should set this.overClass and this.dropAllowed
22568          * 
22569          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22570          * @param {Event} e The event
22571          * @param {Object} data An object containing arbitrary data supplied by the drag source
22572          */
22573         "enter" : true,
22574         
22575          /**
22576          * @event over
22577          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22578          * This method will be called on every mouse movement while the drag source is over the drop target.
22579          * This default implementation simply returns the dropAllowed config value.
22580          * 
22581          * IMPORTANT : it should set this.dropAllowed
22582          * 
22583          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22584          * @param {Event} e The event
22585          * @param {Object} data An object containing arbitrary data supplied by the drag source
22586          
22587          */
22588         "over" : true,
22589         /**
22590          * @event out
22591          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22592          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22593          * overClass (if any) from the drop element.
22594          * 
22595          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22596          * @param {Event} e The event
22597          * @param {Object} data An object containing arbitrary data supplied by the drag source
22598          */
22599          "out" : true,
22600          
22601         /**
22602          * @event drop
22603          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22604          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22605          * implementation that does something to process the drop event and returns true so that the drag source's
22606          * repair action does not run.
22607          * 
22608          * IMPORTANT : it should set this.success
22609          * 
22610          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22611          * @param {Event} e The event
22612          * @param {Object} data An object containing arbitrary data supplied by the drag source
22613         */
22614          "drop" : true
22615     });
22616             
22617      
22618     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22619         this.el.dom, 
22620         this.ddGroup || this.group,
22621         {
22622             isTarget: true,
22623             listeners : listeners || {} 
22624            
22625         
22626         }
22627     );
22628
22629 };
22630
22631 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22632     /**
22633      * @cfg {String} overClass
22634      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22635      */
22636      /**
22637      * @cfg {String} ddGroup
22638      * The drag drop group to handle drop events for
22639      */
22640      
22641     /**
22642      * @cfg {String} dropAllowed
22643      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22644      */
22645     dropAllowed : "x-dd-drop-ok",
22646     /**
22647      * @cfg {String} dropNotAllowed
22648      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22649      */
22650     dropNotAllowed : "x-dd-drop-nodrop",
22651     /**
22652      * @cfg {boolean} success
22653      * set this after drop listener.. 
22654      */
22655     success : false,
22656     /**
22657      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22658      * if the drop point is valid for over/enter..
22659      */
22660     valid : false,
22661     // private
22662     isTarget : true,
22663
22664     // private
22665     isNotifyTarget : true,
22666     
22667     /**
22668      * @hide
22669      */
22670     notifyEnter : function(dd, e, data)
22671     {
22672         this.valid = true;
22673         this.fireEvent('enter', dd, e, data);
22674         if(this.overClass){
22675             this.el.addClass(this.overClass);
22676         }
22677         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22678             this.valid ? this.dropAllowed : this.dropNotAllowed
22679         );
22680     },
22681
22682     /**
22683      * @hide
22684      */
22685     notifyOver : function(dd, e, data)
22686     {
22687         this.valid = true;
22688         this.fireEvent('over', dd, e, data);
22689         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22690             this.valid ? this.dropAllowed : this.dropNotAllowed
22691         );
22692     },
22693
22694     /**
22695      * @hide
22696      */
22697     notifyOut : function(dd, e, data)
22698     {
22699         this.fireEvent('out', dd, e, data);
22700         if(this.overClass){
22701             this.el.removeClass(this.overClass);
22702         }
22703     },
22704
22705     /**
22706      * @hide
22707      */
22708     notifyDrop : function(dd, e, data)
22709     {
22710         this.success = false;
22711         this.fireEvent('drop', dd, e, data);
22712         return this.success;
22713     }
22714 });/*
22715  * Based on:
22716  * Ext JS Library 1.1.1
22717  * Copyright(c) 2006-2007, Ext JS, LLC.
22718  *
22719  * Originally Released Under LGPL - original licence link has changed is not relivant.
22720  *
22721  * Fork - LGPL
22722  * <script type="text/javascript">
22723  */
22724
22725
22726 /**
22727  * @class Roo.dd.DragZone
22728  * @extends Roo.dd.DragSource
22729  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22730  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22731  * @constructor
22732  * @param {String/HTMLElement/Element} el The container element
22733  * @param {Object} config
22734  */
22735 Roo.dd.DragZone = function(el, config){
22736     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22737     if(this.containerScroll){
22738         Roo.dd.ScrollManager.register(this.el);
22739     }
22740 };
22741
22742 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22743     /**
22744      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22745      * for auto scrolling during drag operations.
22746      */
22747     /**
22748      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22749      * method after a failed drop (defaults to "c3daf9" - light blue)
22750      */
22751
22752     /**
22753      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22754      * for a valid target to drag based on the mouse down. Override this method
22755      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22756      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22757      * @param {EventObject} e The mouse down event
22758      * @return {Object} The dragData
22759      */
22760     getDragData : function(e){
22761         return Roo.dd.Registry.getHandleFromEvent(e);
22762     },
22763     
22764     /**
22765      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22766      * this.dragData.ddel
22767      * @param {Number} x The x position of the click on the dragged object
22768      * @param {Number} y The y position of the click on the dragged object
22769      * @return {Boolean} true to continue the drag, false to cancel
22770      */
22771     onInitDrag : function(x, y){
22772         this.proxy.update(this.dragData.ddel.cloneNode(true));
22773         this.onStartDrag(x, y);
22774         return true;
22775     },
22776     
22777     /**
22778      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22779      */
22780     afterRepair : function(){
22781         if(Roo.enableFx){
22782             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22783         }
22784         this.dragging = false;
22785     },
22786
22787     /**
22788      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22789      * the XY of this.dragData.ddel
22790      * @param {EventObject} e The mouse up event
22791      * @return {Array} The xy location (e.g. [100, 200])
22792      */
22793     getRepairXY : function(e){
22794         return Roo.Element.fly(this.dragData.ddel).getXY();  
22795     }
22796 });/*
22797  * Based on:
22798  * Ext JS Library 1.1.1
22799  * Copyright(c) 2006-2007, Ext JS, LLC.
22800  *
22801  * Originally Released Under LGPL - original licence link has changed is not relivant.
22802  *
22803  * Fork - LGPL
22804  * <script type="text/javascript">
22805  */
22806 /**
22807  * @class Roo.dd.DropZone
22808  * @extends Roo.dd.DropTarget
22809  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22810  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22811  * @constructor
22812  * @param {String/HTMLElement/Element} el The container element
22813  * @param {Object} config
22814  */
22815 Roo.dd.DropZone = function(el, config){
22816     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22817 };
22818
22819 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22820     /**
22821      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22822      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22823      * provide your own custom lookup.
22824      * @param {Event} e The event
22825      * @return {Object} data The custom data
22826      */
22827     getTargetFromEvent : function(e){
22828         return Roo.dd.Registry.getTargetFromEvent(e);
22829     },
22830
22831     /**
22832      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22833      * that it has registered.  This method has no default implementation and should be overridden to provide
22834      * node-specific processing if necessary.
22835      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22836      * {@link #getTargetFromEvent} for this node)
22837      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22838      * @param {Event} e The event
22839      * @param {Object} data An object containing arbitrary data supplied by the drag source
22840      */
22841     onNodeEnter : function(n, dd, e, data){
22842         
22843     },
22844
22845     /**
22846      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22847      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22848      * overridden to provide the proper feedback.
22849      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22850      * {@link #getTargetFromEvent} for this node)
22851      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22852      * @param {Event} e The event
22853      * @param {Object} data An object containing arbitrary data supplied by the drag source
22854      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22855      * underlying {@link Roo.dd.StatusProxy} can be updated
22856      */
22857     onNodeOver : function(n, dd, e, data){
22858         return this.dropAllowed;
22859     },
22860
22861     /**
22862      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22863      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22864      * node-specific processing if necessary.
22865      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22866      * {@link #getTargetFromEvent} for this node)
22867      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22868      * @param {Event} e The event
22869      * @param {Object} data An object containing arbitrary data supplied by the drag source
22870      */
22871     onNodeOut : function(n, dd, e, data){
22872         
22873     },
22874
22875     /**
22876      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22877      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22878      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22879      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22880      * {@link #getTargetFromEvent} for this node)
22881      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22882      * @param {Event} e The event
22883      * @param {Object} data An object containing arbitrary data supplied by the drag source
22884      * @return {Boolean} True if the drop was valid, else false
22885      */
22886     onNodeDrop : function(n, dd, e, data){
22887         return false;
22888     },
22889
22890     /**
22891      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22892      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22893      * it should be overridden to provide the proper feedback if necessary.
22894      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22895      * @param {Event} e The event
22896      * @param {Object} data An object containing arbitrary data supplied by the drag source
22897      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22898      * underlying {@link Roo.dd.StatusProxy} can be updated
22899      */
22900     onContainerOver : function(dd, e, data){
22901         return this.dropNotAllowed;
22902     },
22903
22904     /**
22905      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22906      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22907      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22908      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22909      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22910      * @param {Event} e The event
22911      * @param {Object} data An object containing arbitrary data supplied by the drag source
22912      * @return {Boolean} True if the drop was valid, else false
22913      */
22914     onContainerDrop : function(dd, e, data){
22915         return false;
22916     },
22917
22918     /**
22919      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22920      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22921      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22922      * you should override this method and provide a custom implementation.
22923      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22924      * @param {Event} e The event
22925      * @param {Object} data An object containing arbitrary data supplied by the drag source
22926      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22927      * underlying {@link Roo.dd.StatusProxy} can be updated
22928      */
22929     notifyEnter : function(dd, e, data){
22930         return this.dropNotAllowed;
22931     },
22932
22933     /**
22934      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22935      * This method will be called on every mouse movement while the drag source is over the drop zone.
22936      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22937      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22938      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22939      * registered node, it will call {@link #onContainerOver}.
22940      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22941      * @param {Event} e The event
22942      * @param {Object} data An object containing arbitrary data supplied by the drag source
22943      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22944      * underlying {@link Roo.dd.StatusProxy} can be updated
22945      */
22946     notifyOver : function(dd, e, data){
22947         var n = this.getTargetFromEvent(e);
22948         if(!n){ // not over valid drop target
22949             if(this.lastOverNode){
22950                 this.onNodeOut(this.lastOverNode, dd, e, data);
22951                 this.lastOverNode = null;
22952             }
22953             return this.onContainerOver(dd, e, data);
22954         }
22955         if(this.lastOverNode != n){
22956             if(this.lastOverNode){
22957                 this.onNodeOut(this.lastOverNode, dd, e, data);
22958             }
22959             this.onNodeEnter(n, dd, e, data);
22960             this.lastOverNode = n;
22961         }
22962         return this.onNodeOver(n, dd, e, data);
22963     },
22964
22965     /**
22966      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22967      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22968      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22969      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22970      * @param {Event} e The event
22971      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22972      */
22973     notifyOut : function(dd, e, data){
22974         if(this.lastOverNode){
22975             this.onNodeOut(this.lastOverNode, dd, e, data);
22976             this.lastOverNode = null;
22977         }
22978     },
22979
22980     /**
22981      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22982      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22983      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22984      * otherwise it will call {@link #onContainerDrop}.
22985      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22986      * @param {Event} e The event
22987      * @param {Object} data An object containing arbitrary data supplied by the drag source
22988      * @return {Boolean} True if the drop was valid, else false
22989      */
22990     notifyDrop : function(dd, e, data){
22991         if(this.lastOverNode){
22992             this.onNodeOut(this.lastOverNode, dd, e, data);
22993             this.lastOverNode = null;
22994         }
22995         var n = this.getTargetFromEvent(e);
22996         return n ?
22997             this.onNodeDrop(n, dd, e, data) :
22998             this.onContainerDrop(dd, e, data);
22999     },
23000
23001     // private
23002     triggerCacheRefresh : function(){
23003         Roo.dd.DDM.refreshCache(this.groups);
23004     }  
23005 });/*
23006  * Based on:
23007  * Ext JS Library 1.1.1
23008  * Copyright(c) 2006-2007, Ext JS, LLC.
23009  *
23010  * Originally Released Under LGPL - original licence link has changed is not relivant.
23011  *
23012  * Fork - LGPL
23013  * <script type="text/javascript">
23014  */
23015
23016
23017 /**
23018  * @class Roo.data.SortTypes
23019  * @singleton
23020  * Defines the default sorting (casting?) comparison functions used when sorting data.
23021  */
23022 Roo.data.SortTypes = {
23023     /**
23024      * Default sort that does nothing
23025      * @param {Mixed} s The value being converted
23026      * @return {Mixed} The comparison value
23027      */
23028     none : function(s){
23029         return s;
23030     },
23031     
23032     /**
23033      * The regular expression used to strip tags
23034      * @type {RegExp}
23035      * @property
23036      */
23037     stripTagsRE : /<\/?[^>]+>/gi,
23038     
23039     /**
23040      * Strips all HTML tags to sort on text only
23041      * @param {Mixed} s The value being converted
23042      * @return {String} The comparison value
23043      */
23044     asText : function(s){
23045         return String(s).replace(this.stripTagsRE, "");
23046     },
23047     
23048     /**
23049      * Strips all HTML tags to sort on text only - Case insensitive
23050      * @param {Mixed} s The value being converted
23051      * @return {String} The comparison value
23052      */
23053     asUCText : function(s){
23054         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23055     },
23056     
23057     /**
23058      * Case insensitive string
23059      * @param {Mixed} s The value being converted
23060      * @return {String} The comparison value
23061      */
23062     asUCString : function(s) {
23063         return String(s).toUpperCase();
23064     },
23065     
23066     /**
23067      * Date sorting
23068      * @param {Mixed} s The value being converted
23069      * @return {Number} The comparison value
23070      */
23071     asDate : function(s) {
23072         if(!s){
23073             return 0;
23074         }
23075         if(s instanceof Date){
23076             return s.getTime();
23077         }
23078         return Date.parse(String(s));
23079     },
23080     
23081     /**
23082      * Float sorting
23083      * @param {Mixed} s The value being converted
23084      * @return {Float} The comparison value
23085      */
23086     asFloat : function(s) {
23087         var val = parseFloat(String(s).replace(/,/g, ""));
23088         if(isNaN(val)) {
23089             val = 0;
23090         }
23091         return val;
23092     },
23093     
23094     /**
23095      * Integer sorting
23096      * @param {Mixed} s The value being converted
23097      * @return {Number} The comparison value
23098      */
23099     asInt : function(s) {
23100         var val = parseInt(String(s).replace(/,/g, ""));
23101         if(isNaN(val)) {
23102             val = 0;
23103         }
23104         return val;
23105     }
23106 };/*
23107  * Based on:
23108  * Ext JS Library 1.1.1
23109  * Copyright(c) 2006-2007, Ext JS, LLC.
23110  *
23111  * Originally Released Under LGPL - original licence link has changed is not relivant.
23112  *
23113  * Fork - LGPL
23114  * <script type="text/javascript">
23115  */
23116
23117 /**
23118 * @class Roo.data.Record
23119  * Instances of this class encapsulate both record <em>definition</em> information, and record
23120  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23121  * to access Records cached in an {@link Roo.data.Store} object.<br>
23122  * <p>
23123  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23124  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23125  * objects.<br>
23126  * <p>
23127  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23128  * @constructor
23129  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23130  * {@link #create}. The parameters are the same.
23131  * @param {Array} data An associative Array of data values keyed by the field name.
23132  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23133  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23134  * not specified an integer id is generated.
23135  */
23136 Roo.data.Record = function(data, id){
23137     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23138     this.data = data;
23139 };
23140
23141 /**
23142  * Generate a constructor for a specific record layout.
23143  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23144  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23145  * Each field definition object may contain the following properties: <ul>
23146  * <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,
23147  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23148  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23149  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23150  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23151  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23152  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23153  * this may be omitted.</p></li>
23154  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23155  * <ul><li>auto (Default, implies no conversion)</li>
23156  * <li>string</li>
23157  * <li>int</li>
23158  * <li>float</li>
23159  * <li>boolean</li>
23160  * <li>date</li></ul></p></li>
23161  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23162  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23163  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23164  * by the Reader into an object that will be stored in the Record. It is passed the
23165  * following parameters:<ul>
23166  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23167  * </ul></p></li>
23168  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23169  * </ul>
23170  * <br>usage:<br><pre><code>
23171 var TopicRecord = Roo.data.Record.create(
23172     {name: 'title', mapping: 'topic_title'},
23173     {name: 'author', mapping: 'username'},
23174     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23175     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23176     {name: 'lastPoster', mapping: 'user2'},
23177     {name: 'excerpt', mapping: 'post_text'}
23178 );
23179
23180 var myNewRecord = new TopicRecord({
23181     title: 'Do my job please',
23182     author: 'noobie',
23183     totalPosts: 1,
23184     lastPost: new Date(),
23185     lastPoster: 'Animal',
23186     excerpt: 'No way dude!'
23187 });
23188 myStore.add(myNewRecord);
23189 </code></pre>
23190  * @method create
23191  * @static
23192  */
23193 Roo.data.Record.create = function(o){
23194     var f = function(){
23195         f.superclass.constructor.apply(this, arguments);
23196     };
23197     Roo.extend(f, Roo.data.Record);
23198     var p = f.prototype;
23199     p.fields = new Roo.util.MixedCollection(false, function(field){
23200         return field.name;
23201     });
23202     for(var i = 0, len = o.length; i < len; i++){
23203         p.fields.add(new Roo.data.Field(o[i]));
23204     }
23205     f.getField = function(name){
23206         return p.fields.get(name);  
23207     };
23208     return f;
23209 };
23210
23211 Roo.data.Record.AUTO_ID = 1000;
23212 Roo.data.Record.EDIT = 'edit';
23213 Roo.data.Record.REJECT = 'reject';
23214 Roo.data.Record.COMMIT = 'commit';
23215
23216 Roo.data.Record.prototype = {
23217     /**
23218      * Readonly flag - true if this record has been modified.
23219      * @type Boolean
23220      */
23221     dirty : false,
23222     editing : false,
23223     error: null,
23224     modified: null,
23225
23226     // private
23227     join : function(store){
23228         this.store = store;
23229     },
23230
23231     /**
23232      * Set the named field to the specified value.
23233      * @param {String} name The name of the field to set.
23234      * @param {Object} value The value to set the field to.
23235      */
23236     set : function(name, value){
23237         if(this.data[name] == value){
23238             return;
23239         }
23240         this.dirty = true;
23241         if(!this.modified){
23242             this.modified = {};
23243         }
23244         if(typeof this.modified[name] == 'undefined'){
23245             this.modified[name] = this.data[name];
23246         }
23247         this.data[name] = value;
23248         if(!this.editing && this.store){
23249             this.store.afterEdit(this);
23250         }       
23251     },
23252
23253     /**
23254      * Get the value of the named field.
23255      * @param {String} name The name of the field to get the value of.
23256      * @return {Object} The value of the field.
23257      */
23258     get : function(name){
23259         return this.data[name]; 
23260     },
23261
23262     // private
23263     beginEdit : function(){
23264         this.editing = true;
23265         this.modified = {}; 
23266     },
23267
23268     // private
23269     cancelEdit : function(){
23270         this.editing = false;
23271         delete this.modified;
23272     },
23273
23274     // private
23275     endEdit : function(){
23276         this.editing = false;
23277         if(this.dirty && this.store){
23278             this.store.afterEdit(this);
23279         }
23280     },
23281
23282     /**
23283      * Usually called by the {@link Roo.data.Store} which owns the Record.
23284      * Rejects all changes made to the Record since either creation, or the last commit operation.
23285      * Modified fields are reverted to their original values.
23286      * <p>
23287      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23288      * of reject operations.
23289      */
23290     reject : function(){
23291         var m = this.modified;
23292         for(var n in m){
23293             if(typeof m[n] != "function"){
23294                 this.data[n] = m[n];
23295             }
23296         }
23297         this.dirty = false;
23298         delete this.modified;
23299         this.editing = false;
23300         if(this.store){
23301             this.store.afterReject(this);
23302         }
23303     },
23304
23305     /**
23306      * Usually called by the {@link Roo.data.Store} which owns the Record.
23307      * Commits all changes made to the Record since either creation, or the last commit operation.
23308      * <p>
23309      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23310      * of commit operations.
23311      */
23312     commit : function(){
23313         this.dirty = false;
23314         delete this.modified;
23315         this.editing = false;
23316         if(this.store){
23317             this.store.afterCommit(this);
23318         }
23319     },
23320
23321     // private
23322     hasError : function(){
23323         return this.error != null;
23324     },
23325
23326     // private
23327     clearError : function(){
23328         this.error = null;
23329     },
23330
23331     /**
23332      * Creates a copy of this record.
23333      * @param {String} id (optional) A new record id if you don't want to use this record's id
23334      * @return {Record}
23335      */
23336     copy : function(newId) {
23337         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23338     }
23339 };/*
23340  * Based on:
23341  * Ext JS Library 1.1.1
23342  * Copyright(c) 2006-2007, Ext JS, LLC.
23343  *
23344  * Originally Released Under LGPL - original licence link has changed is not relivant.
23345  *
23346  * Fork - LGPL
23347  * <script type="text/javascript">
23348  */
23349
23350
23351
23352 /**
23353  * @class Roo.data.Store
23354  * @extends Roo.util.Observable
23355  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23356  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23357  * <p>
23358  * 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
23359  * has no knowledge of the format of the data returned by the Proxy.<br>
23360  * <p>
23361  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23362  * instances from the data object. These records are cached and made available through accessor functions.
23363  * @constructor
23364  * Creates a new Store.
23365  * @param {Object} config A config object containing the objects needed for the Store to access data,
23366  * and read the data into Records.
23367  */
23368 Roo.data.Store = function(config){
23369     this.data = new Roo.util.MixedCollection(false);
23370     this.data.getKey = function(o){
23371         return o.id;
23372     };
23373     this.baseParams = {};
23374     // private
23375     this.paramNames = {
23376         "start" : "start",
23377         "limit" : "limit",
23378         "sort" : "sort",
23379         "dir" : "dir",
23380         "multisort" : "_multisort"
23381     };
23382
23383     if(config && config.data){
23384         this.inlineData = config.data;
23385         delete config.data;
23386     }
23387
23388     Roo.apply(this, config);
23389     
23390     if(this.reader){ // reader passed
23391         this.reader = Roo.factory(this.reader, Roo.data);
23392         this.reader.xmodule = this.xmodule || false;
23393         if(!this.recordType){
23394             this.recordType = this.reader.recordType;
23395         }
23396         if(this.reader.onMetaChange){
23397             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23398         }
23399     }
23400
23401     if(this.recordType){
23402         this.fields = this.recordType.prototype.fields;
23403     }
23404     this.modified = [];
23405
23406     this.addEvents({
23407         /**
23408          * @event datachanged
23409          * Fires when the data cache has changed, and a widget which is using this Store
23410          * as a Record cache should refresh its view.
23411          * @param {Store} this
23412          */
23413         datachanged : true,
23414         /**
23415          * @event metachange
23416          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23417          * @param {Store} this
23418          * @param {Object} meta The JSON metadata
23419          */
23420         metachange : true,
23421         /**
23422          * @event add
23423          * Fires when Records have been added to the Store
23424          * @param {Store} this
23425          * @param {Roo.data.Record[]} records The array of Records added
23426          * @param {Number} index The index at which the record(s) were added
23427          */
23428         add : true,
23429         /**
23430          * @event remove
23431          * Fires when a Record has been removed from the Store
23432          * @param {Store} this
23433          * @param {Roo.data.Record} record The Record that was removed
23434          * @param {Number} index The index at which the record was removed
23435          */
23436         remove : true,
23437         /**
23438          * @event update
23439          * Fires when a Record has been updated
23440          * @param {Store} this
23441          * @param {Roo.data.Record} record The Record that was updated
23442          * @param {String} operation The update operation being performed.  Value may be one of:
23443          * <pre><code>
23444  Roo.data.Record.EDIT
23445  Roo.data.Record.REJECT
23446  Roo.data.Record.COMMIT
23447          * </code></pre>
23448          */
23449         update : true,
23450         /**
23451          * @event clear
23452          * Fires when the data cache has been cleared.
23453          * @param {Store} this
23454          */
23455         clear : true,
23456         /**
23457          * @event beforeload
23458          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23459          * the load action will be canceled.
23460          * @param {Store} this
23461          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23462          */
23463         beforeload : true,
23464         /**
23465          * @event beforeloadadd
23466          * Fires after a new set of Records has been loaded.
23467          * @param {Store} this
23468          * @param {Roo.data.Record[]} records The Records that were loaded
23469          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23470          */
23471         beforeloadadd : true,
23472         /**
23473          * @event load
23474          * Fires after a new set of Records has been loaded, before they are added to the store.
23475          * @param {Store} this
23476          * @param {Roo.data.Record[]} records The Records that were loaded
23477          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23478          * @params {Object} return from reader
23479          */
23480         load : true,
23481         /**
23482          * @event loadexception
23483          * Fires if an exception occurs in the Proxy during loading.
23484          * Called with the signature of the Proxy's "loadexception" event.
23485          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23486          * 
23487          * @param {Proxy} 
23488          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23489          * @param {Object} load options 
23490          * @param {Object} jsonData from your request (normally this contains the Exception)
23491          */
23492         loadexception : true
23493     });
23494     
23495     if(this.proxy){
23496         this.proxy = Roo.factory(this.proxy, Roo.data);
23497         this.proxy.xmodule = this.xmodule || false;
23498         this.relayEvents(this.proxy,  ["loadexception"]);
23499     }
23500     this.sortToggle = {};
23501     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23502
23503     Roo.data.Store.superclass.constructor.call(this);
23504
23505     if(this.inlineData){
23506         this.loadData(this.inlineData);
23507         delete this.inlineData;
23508     }
23509 };
23510
23511 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23512      /**
23513     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23514     * without a remote query - used by combo/forms at present.
23515     */
23516     
23517     /**
23518     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23519     */
23520     /**
23521     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23522     */
23523     /**
23524     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23525     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23526     */
23527     /**
23528     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23529     * on any HTTP request
23530     */
23531     /**
23532     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23533     */
23534     /**
23535     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23536     */
23537     multiSort: false,
23538     /**
23539     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23540     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23541     */
23542     remoteSort : false,
23543
23544     /**
23545     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23546      * loaded or when a record is removed. (defaults to false).
23547     */
23548     pruneModifiedRecords : false,
23549
23550     // private
23551     lastOptions : null,
23552
23553     /**
23554      * Add Records to the Store and fires the add event.
23555      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23556      */
23557     add : function(records){
23558         records = [].concat(records);
23559         for(var i = 0, len = records.length; i < len; i++){
23560             records[i].join(this);
23561         }
23562         var index = this.data.length;
23563         this.data.addAll(records);
23564         this.fireEvent("add", this, records, index);
23565     },
23566
23567     /**
23568      * Remove a Record from the Store and fires the remove event.
23569      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23570      */
23571     remove : function(record){
23572         var index = this.data.indexOf(record);
23573         this.data.removeAt(index);
23574  
23575         if(this.pruneModifiedRecords){
23576             this.modified.remove(record);
23577         }
23578         this.fireEvent("remove", this, record, index);
23579     },
23580
23581     /**
23582      * Remove all Records from the Store and fires the clear event.
23583      */
23584     removeAll : function(){
23585         this.data.clear();
23586         if(this.pruneModifiedRecords){
23587             this.modified = [];
23588         }
23589         this.fireEvent("clear", this);
23590     },
23591
23592     /**
23593      * Inserts Records to the Store at the given index and fires the add event.
23594      * @param {Number} index The start index at which to insert the passed Records.
23595      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23596      */
23597     insert : function(index, records){
23598         records = [].concat(records);
23599         for(var i = 0, len = records.length; i < len; i++){
23600             this.data.insert(index, records[i]);
23601             records[i].join(this);
23602         }
23603         this.fireEvent("add", this, records, index);
23604     },
23605
23606     /**
23607      * Get the index within the cache of the passed Record.
23608      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23609      * @return {Number} The index of the passed Record. Returns -1 if not found.
23610      */
23611     indexOf : function(record){
23612         return this.data.indexOf(record);
23613     },
23614
23615     /**
23616      * Get the index within the cache of the Record with the passed id.
23617      * @param {String} id The id of the Record to find.
23618      * @return {Number} The index of the Record. Returns -1 if not found.
23619      */
23620     indexOfId : function(id){
23621         return this.data.indexOfKey(id);
23622     },
23623
23624     /**
23625      * Get the Record with the specified id.
23626      * @param {String} id The id of the Record to find.
23627      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23628      */
23629     getById : function(id){
23630         return this.data.key(id);
23631     },
23632
23633     /**
23634      * Get the Record at the specified index.
23635      * @param {Number} index The index of the Record to find.
23636      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23637      */
23638     getAt : function(index){
23639         return this.data.itemAt(index);
23640     },
23641
23642     /**
23643      * Returns a range of Records between specified indices.
23644      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23645      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23646      * @return {Roo.data.Record[]} An array of Records
23647      */
23648     getRange : function(start, end){
23649         return this.data.getRange(start, end);
23650     },
23651
23652     // private
23653     storeOptions : function(o){
23654         o = Roo.apply({}, o);
23655         delete o.callback;
23656         delete o.scope;
23657         this.lastOptions = o;
23658     },
23659
23660     /**
23661      * Loads the Record cache from the configured Proxy using the configured Reader.
23662      * <p>
23663      * If using remote paging, then the first load call must specify the <em>start</em>
23664      * and <em>limit</em> properties in the options.params property to establish the initial
23665      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23666      * <p>
23667      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23668      * and this call will return before the new data has been loaded. Perform any post-processing
23669      * in a callback function, or in a "load" event handler.</strong>
23670      * <p>
23671      * @param {Object} options An object containing properties which control loading options:<ul>
23672      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23673      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23674      * passed the following arguments:<ul>
23675      * <li>r : Roo.data.Record[]</li>
23676      * <li>options: Options object from the load call</li>
23677      * <li>success: Boolean success indicator</li></ul></li>
23678      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23679      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23680      * </ul>
23681      */
23682     load : function(options){
23683         options = options || {};
23684         if(this.fireEvent("beforeload", this, options) !== false){
23685             this.storeOptions(options);
23686             var p = Roo.apply(options.params || {}, this.baseParams);
23687             // if meta was not loaded from remote source.. try requesting it.
23688             if (!this.reader.metaFromRemote) {
23689                 p._requestMeta = 1;
23690             }
23691             if(this.sortInfo && this.remoteSort){
23692                 var pn = this.paramNames;
23693                 p[pn["sort"]] = this.sortInfo.field;
23694                 p[pn["dir"]] = this.sortInfo.direction;
23695             }
23696             if (this.multiSort) {
23697                 var pn = this.paramNames;
23698                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23699             }
23700             
23701             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23702         }
23703     },
23704
23705     /**
23706      * Reloads the Record cache from the configured Proxy using the configured Reader and
23707      * the options from the last load operation performed.
23708      * @param {Object} options (optional) An object containing properties which may override the options
23709      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23710      * the most recently used options are reused).
23711      */
23712     reload : function(options){
23713         this.load(Roo.applyIf(options||{}, this.lastOptions));
23714     },
23715
23716     // private
23717     // Called as a callback by the Reader during a load operation.
23718     loadRecords : function(o, options, success){
23719         if(!o || success === false){
23720             if(success !== false){
23721                 this.fireEvent("load", this, [], options, o);
23722             }
23723             if(options.callback){
23724                 options.callback.call(options.scope || this, [], options, false);
23725             }
23726             return;
23727         }
23728         // if data returned failure - throw an exception.
23729         if (o.success === false) {
23730             // show a message if no listener is registered.
23731             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23732                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23733             }
23734             // loadmask wil be hooked into this..
23735             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23736             return;
23737         }
23738         var r = o.records, t = o.totalRecords || r.length;
23739         
23740         this.fireEvent("beforeloadadd", this, r, options, o);
23741         
23742         if(!options || options.add !== true){
23743             if(this.pruneModifiedRecords){
23744                 this.modified = [];
23745             }
23746             for(var i = 0, len = r.length; i < len; i++){
23747                 r[i].join(this);
23748             }
23749             if(this.snapshot){
23750                 this.data = this.snapshot;
23751                 delete this.snapshot;
23752             }
23753             this.data.clear();
23754             this.data.addAll(r);
23755             this.totalLength = t;
23756             this.applySort();
23757             this.fireEvent("datachanged", this);
23758         }else{
23759             this.totalLength = Math.max(t, this.data.length+r.length);
23760             this.add(r);
23761         }
23762         
23763         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23764                 
23765             var e = new Roo.data.Record({});
23766
23767             e.set(this.parent.displayField, this.parent.emptyTitle);
23768             e.set(this.parent.valueField, '');
23769
23770             this.insert(0, e);
23771         }
23772             
23773         this.fireEvent("load", this, r, options, o);
23774         if(options.callback){
23775             options.callback.call(options.scope || this, r, options, true);
23776         }
23777     },
23778
23779
23780     /**
23781      * Loads data from a passed data block. A Reader which understands the format of the data
23782      * must have been configured in the constructor.
23783      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23784      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23785      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23786      */
23787     loadData : function(o, append){
23788         var r = this.reader.readRecords(o);
23789         this.loadRecords(r, {add: append}, true);
23790     },
23791     
23792      /**
23793      * using 'cn' the nested child reader read the child array into it's child stores.
23794      * @param {Object} rec The record with a 'children array
23795      */
23796     loadDataFromChildren : function(rec)
23797     {
23798         this.loadData(this.reader.toLoadData(rec));
23799     },
23800     
23801
23802     /**
23803      * Gets the number of cached records.
23804      * <p>
23805      * <em>If using paging, this may not be the total size of the dataset. If the data object
23806      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23807      * the data set size</em>
23808      */
23809     getCount : function(){
23810         return this.data.length || 0;
23811     },
23812
23813     /**
23814      * Gets the total number of records in the dataset as returned by the server.
23815      * <p>
23816      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23817      * the dataset size</em>
23818      */
23819     getTotalCount : function(){
23820         return this.totalLength || 0;
23821     },
23822
23823     /**
23824      * Returns the sort state of the Store as an object with two properties:
23825      * <pre><code>
23826  field {String} The name of the field by which the Records are sorted
23827  direction {String} The sort order, "ASC" or "DESC"
23828      * </code></pre>
23829      */
23830     getSortState : function(){
23831         return this.sortInfo;
23832     },
23833
23834     // private
23835     applySort : function(){
23836         if(this.sortInfo && !this.remoteSort){
23837             var s = this.sortInfo, f = s.field;
23838             var st = this.fields.get(f).sortType;
23839             var fn = function(r1, r2){
23840                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23841                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23842             };
23843             this.data.sort(s.direction, fn);
23844             if(this.snapshot && this.snapshot != this.data){
23845                 this.snapshot.sort(s.direction, fn);
23846             }
23847         }
23848     },
23849
23850     /**
23851      * Sets the default sort column and order to be used by the next load operation.
23852      * @param {String} fieldName The name of the field to sort by.
23853      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23854      */
23855     setDefaultSort : function(field, dir){
23856         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23857     },
23858
23859     /**
23860      * Sort the Records.
23861      * If remote sorting is used, the sort is performed on the server, and the cache is
23862      * reloaded. If local sorting is used, the cache is sorted internally.
23863      * @param {String} fieldName The name of the field to sort by.
23864      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23865      */
23866     sort : function(fieldName, dir){
23867         var f = this.fields.get(fieldName);
23868         if(!dir){
23869             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23870             
23871             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23872                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23873             }else{
23874                 dir = f.sortDir;
23875             }
23876         }
23877         this.sortToggle[f.name] = dir;
23878         this.sortInfo = {field: f.name, direction: dir};
23879         if(!this.remoteSort){
23880             this.applySort();
23881             this.fireEvent("datachanged", this);
23882         }else{
23883             this.load(this.lastOptions);
23884         }
23885     },
23886
23887     /**
23888      * Calls the specified function for each of the Records in the cache.
23889      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23890      * Returning <em>false</em> aborts and exits the iteration.
23891      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23892      */
23893     each : function(fn, scope){
23894         this.data.each(fn, scope);
23895     },
23896
23897     /**
23898      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23899      * (e.g., during paging).
23900      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23901      */
23902     getModifiedRecords : function(){
23903         return this.modified;
23904     },
23905
23906     // private
23907     createFilterFn : function(property, value, anyMatch){
23908         if(!value.exec){ // not a regex
23909             value = String(value);
23910             if(value.length == 0){
23911                 return false;
23912             }
23913             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23914         }
23915         return function(r){
23916             return value.test(r.data[property]);
23917         };
23918     },
23919
23920     /**
23921      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23922      * @param {String} property A field on your records
23923      * @param {Number} start The record index to start at (defaults to 0)
23924      * @param {Number} end The last record index to include (defaults to length - 1)
23925      * @return {Number} The sum
23926      */
23927     sum : function(property, start, end){
23928         var rs = this.data.items, v = 0;
23929         start = start || 0;
23930         end = (end || end === 0) ? end : rs.length-1;
23931
23932         for(var i = start; i <= end; i++){
23933             v += (rs[i].data[property] || 0);
23934         }
23935         return v;
23936     },
23937
23938     /**
23939      * Filter the records by a specified property.
23940      * @param {String} field A field on your records
23941      * @param {String/RegExp} value Either a string that the field
23942      * should start with or a RegExp to test against the field
23943      * @param {Boolean} anyMatch True to match any part not just the beginning
23944      */
23945     filter : function(property, value, anyMatch){
23946         var fn = this.createFilterFn(property, value, anyMatch);
23947         return fn ? this.filterBy(fn) : this.clearFilter();
23948     },
23949
23950     /**
23951      * Filter by a function. The specified function will be called with each
23952      * record in this data source. If the function returns true the record is included,
23953      * otherwise it is filtered.
23954      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23955      * @param {Object} scope (optional) The scope of the function (defaults to this)
23956      */
23957     filterBy : function(fn, scope){
23958         this.snapshot = this.snapshot || this.data;
23959         this.data = this.queryBy(fn, scope||this);
23960         this.fireEvent("datachanged", this);
23961     },
23962
23963     /**
23964      * Query the records by a specified property.
23965      * @param {String} field A field on your records
23966      * @param {String/RegExp} value Either a string that the field
23967      * should start with or a RegExp to test against the field
23968      * @param {Boolean} anyMatch True to match any part not just the beginning
23969      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23970      */
23971     query : function(property, value, anyMatch){
23972         var fn = this.createFilterFn(property, value, anyMatch);
23973         return fn ? this.queryBy(fn) : this.data.clone();
23974     },
23975
23976     /**
23977      * Query by a function. The specified function will be called with each
23978      * record in this data source. If the function returns true the record is included
23979      * in the results.
23980      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23981      * @param {Object} scope (optional) The scope of the function (defaults to this)
23982       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23983      **/
23984     queryBy : function(fn, scope){
23985         var data = this.snapshot || this.data;
23986         return data.filterBy(fn, scope||this);
23987     },
23988
23989     /**
23990      * Collects unique values for a particular dataIndex from this store.
23991      * @param {String} dataIndex The property to collect
23992      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23993      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23994      * @return {Array} An array of the unique values
23995      **/
23996     collect : function(dataIndex, allowNull, bypassFilter){
23997         var d = (bypassFilter === true && this.snapshot) ?
23998                 this.snapshot.items : this.data.items;
23999         var v, sv, r = [], l = {};
24000         for(var i = 0, len = d.length; i < len; i++){
24001             v = d[i].data[dataIndex];
24002             sv = String(v);
24003             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24004                 l[sv] = true;
24005                 r[r.length] = v;
24006             }
24007         }
24008         return r;
24009     },
24010
24011     /**
24012      * Revert to a view of the Record cache with no filtering applied.
24013      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24014      */
24015     clearFilter : function(suppressEvent){
24016         if(this.snapshot && this.snapshot != this.data){
24017             this.data = this.snapshot;
24018             delete this.snapshot;
24019             if(suppressEvent !== true){
24020                 this.fireEvent("datachanged", this);
24021             }
24022         }
24023     },
24024
24025     // private
24026     afterEdit : function(record){
24027         if(this.modified.indexOf(record) == -1){
24028             this.modified.push(record);
24029         }
24030         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24031     },
24032     
24033     // private
24034     afterReject : function(record){
24035         this.modified.remove(record);
24036         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24037     },
24038
24039     // private
24040     afterCommit : function(record){
24041         this.modified.remove(record);
24042         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24043     },
24044
24045     /**
24046      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24047      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24048      */
24049     commitChanges : function(){
24050         var m = this.modified.slice(0);
24051         this.modified = [];
24052         for(var i = 0, len = m.length; i < len; i++){
24053             m[i].commit();
24054         }
24055     },
24056
24057     /**
24058      * Cancel outstanding changes on all changed records.
24059      */
24060     rejectChanges : function(){
24061         var m = this.modified.slice(0);
24062         this.modified = [];
24063         for(var i = 0, len = m.length; i < len; i++){
24064             m[i].reject();
24065         }
24066     },
24067
24068     onMetaChange : function(meta, rtype, o){
24069         this.recordType = rtype;
24070         this.fields = rtype.prototype.fields;
24071         delete this.snapshot;
24072         this.sortInfo = meta.sortInfo || this.sortInfo;
24073         this.modified = [];
24074         this.fireEvent('metachange', this, this.reader.meta);
24075     },
24076     
24077     moveIndex : function(data, type)
24078     {
24079         var index = this.indexOf(data);
24080         
24081         var newIndex = index + type;
24082         
24083         this.remove(data);
24084         
24085         this.insert(newIndex, data);
24086         
24087     }
24088 });/*
24089  * Based on:
24090  * Ext JS Library 1.1.1
24091  * Copyright(c) 2006-2007, Ext JS, LLC.
24092  *
24093  * Originally Released Under LGPL - original licence link has changed is not relivant.
24094  *
24095  * Fork - LGPL
24096  * <script type="text/javascript">
24097  */
24098
24099 /**
24100  * @class Roo.data.SimpleStore
24101  * @extends Roo.data.Store
24102  * Small helper class to make creating Stores from Array data easier.
24103  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24104  * @cfg {Array} fields An array of field definition objects, or field name strings.
24105  * @cfg {Object} an existing reader (eg. copied from another store)
24106  * @cfg {Array} data The multi-dimensional array of data
24107  * @constructor
24108  * @param {Object} config
24109  */
24110 Roo.data.SimpleStore = function(config)
24111 {
24112     Roo.data.SimpleStore.superclass.constructor.call(this, {
24113         isLocal : true,
24114         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24115                 id: config.id
24116             },
24117             Roo.data.Record.create(config.fields)
24118         ),
24119         proxy : new Roo.data.MemoryProxy(config.data)
24120     });
24121     this.load();
24122 };
24123 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24124  * Based on:
24125  * Ext JS Library 1.1.1
24126  * Copyright(c) 2006-2007, Ext JS, LLC.
24127  *
24128  * Originally Released Under LGPL - original licence link has changed is not relivant.
24129  *
24130  * Fork - LGPL
24131  * <script type="text/javascript">
24132  */
24133
24134 /**
24135 /**
24136  * @extends Roo.data.Store
24137  * @class Roo.data.JsonStore
24138  * Small helper class to make creating Stores for JSON data easier. <br/>
24139 <pre><code>
24140 var store = new Roo.data.JsonStore({
24141     url: 'get-images.php',
24142     root: 'images',
24143     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24144 });
24145 </code></pre>
24146  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24147  * JsonReader and HttpProxy (unless inline data is provided).</b>
24148  * @cfg {Array} fields An array of field definition objects, or field name strings.
24149  * @constructor
24150  * @param {Object} config
24151  */
24152 Roo.data.JsonStore = function(c){
24153     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24154         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24155         reader: new Roo.data.JsonReader(c, c.fields)
24156     }));
24157 };
24158 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24159  * Based on:
24160  * Ext JS Library 1.1.1
24161  * Copyright(c) 2006-2007, Ext JS, LLC.
24162  *
24163  * Originally Released Under LGPL - original licence link has changed is not relivant.
24164  *
24165  * Fork - LGPL
24166  * <script type="text/javascript">
24167  */
24168
24169  
24170 Roo.data.Field = function(config){
24171     if(typeof config == "string"){
24172         config = {name: config};
24173     }
24174     Roo.apply(this, config);
24175     
24176     if(!this.type){
24177         this.type = "auto";
24178     }
24179     
24180     var st = Roo.data.SortTypes;
24181     // named sortTypes are supported, here we look them up
24182     if(typeof this.sortType == "string"){
24183         this.sortType = st[this.sortType];
24184     }
24185     
24186     // set default sortType for strings and dates
24187     if(!this.sortType){
24188         switch(this.type){
24189             case "string":
24190                 this.sortType = st.asUCString;
24191                 break;
24192             case "date":
24193                 this.sortType = st.asDate;
24194                 break;
24195             default:
24196                 this.sortType = st.none;
24197         }
24198     }
24199
24200     // define once
24201     var stripRe = /[\$,%]/g;
24202
24203     // prebuilt conversion function for this field, instead of
24204     // switching every time we're reading a value
24205     if(!this.convert){
24206         var cv, dateFormat = this.dateFormat;
24207         switch(this.type){
24208             case "":
24209             case "auto":
24210             case undefined:
24211                 cv = function(v){ return v; };
24212                 break;
24213             case "string":
24214                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24215                 break;
24216             case "int":
24217                 cv = function(v){
24218                     return v !== undefined && v !== null && v !== '' ?
24219                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24220                     };
24221                 break;
24222             case "float":
24223                 cv = function(v){
24224                     return v !== undefined && v !== null && v !== '' ?
24225                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24226                     };
24227                 break;
24228             case "bool":
24229             case "boolean":
24230                 cv = function(v){ return v === true || v === "true" || v == 1; };
24231                 break;
24232             case "date":
24233                 cv = function(v){
24234                     if(!v){
24235                         return '';
24236                     }
24237                     if(v instanceof Date){
24238                         return v;
24239                     }
24240                     if(dateFormat){
24241                         if(dateFormat == "timestamp"){
24242                             return new Date(v*1000);
24243                         }
24244                         return Date.parseDate(v, dateFormat);
24245                     }
24246                     var parsed = Date.parse(v);
24247                     return parsed ? new Date(parsed) : null;
24248                 };
24249              break;
24250             
24251         }
24252         this.convert = cv;
24253     }
24254 };
24255
24256 Roo.data.Field.prototype = {
24257     dateFormat: null,
24258     defaultValue: "",
24259     mapping: null,
24260     sortType : null,
24261     sortDir : "ASC"
24262 };/*
24263  * Based on:
24264  * Ext JS Library 1.1.1
24265  * Copyright(c) 2006-2007, Ext JS, LLC.
24266  *
24267  * Originally Released Under LGPL - original licence link has changed is not relivant.
24268  *
24269  * Fork - LGPL
24270  * <script type="text/javascript">
24271  */
24272  
24273 // Base class for reading structured data from a data source.  This class is intended to be
24274 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24275
24276 /**
24277  * @class Roo.data.DataReader
24278  * Base class for reading structured data from a data source.  This class is intended to be
24279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24280  */
24281
24282 Roo.data.DataReader = function(meta, recordType){
24283     
24284     this.meta = meta;
24285     
24286     this.recordType = recordType instanceof Array ? 
24287         Roo.data.Record.create(recordType) : recordType;
24288 };
24289
24290 Roo.data.DataReader.prototype = {
24291     
24292     
24293     readerType : 'Data',
24294      /**
24295      * Create an empty record
24296      * @param {Object} data (optional) - overlay some values
24297      * @return {Roo.data.Record} record created.
24298      */
24299     newRow :  function(d) {
24300         var da =  {};
24301         this.recordType.prototype.fields.each(function(c) {
24302             switch( c.type) {
24303                 case 'int' : da[c.name] = 0; break;
24304                 case 'date' : da[c.name] = new Date(); break;
24305                 case 'float' : da[c.name] = 0.0; break;
24306                 case 'boolean' : da[c.name] = false; break;
24307                 default : da[c.name] = ""; break;
24308             }
24309             
24310         });
24311         return new this.recordType(Roo.apply(da, d));
24312     }
24313     
24314     
24315 };/*
24316  * Based on:
24317  * Ext JS Library 1.1.1
24318  * Copyright(c) 2006-2007, Ext JS, LLC.
24319  *
24320  * Originally Released Under LGPL - original licence link has changed is not relivant.
24321  *
24322  * Fork - LGPL
24323  * <script type="text/javascript">
24324  */
24325
24326 /**
24327  * @class Roo.data.DataProxy
24328  * @extends Roo.data.Observable
24329  * This class is an abstract base class for implementations which provide retrieval of
24330  * unformatted data objects.<br>
24331  * <p>
24332  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24333  * (of the appropriate type which knows how to parse the data object) to provide a block of
24334  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24335  * <p>
24336  * Custom implementations must implement the load method as described in
24337  * {@link Roo.data.HttpProxy#load}.
24338  */
24339 Roo.data.DataProxy = function(){
24340     this.addEvents({
24341         /**
24342          * @event beforeload
24343          * Fires before a network request is made to retrieve a data object.
24344          * @param {Object} This DataProxy object.
24345          * @param {Object} params The params parameter to the load function.
24346          */
24347         beforeload : true,
24348         /**
24349          * @event load
24350          * Fires before the load method's callback is called.
24351          * @param {Object} This DataProxy object.
24352          * @param {Object} o The data object.
24353          * @param {Object} arg The callback argument object passed to the load function.
24354          */
24355         load : true,
24356         /**
24357          * @event loadexception
24358          * Fires if an Exception occurs during data retrieval.
24359          * @param {Object} This DataProxy object.
24360          * @param {Object} o The data object.
24361          * @param {Object} arg The callback argument object passed to the load function.
24362          * @param {Object} e The Exception.
24363          */
24364         loadexception : true
24365     });
24366     Roo.data.DataProxy.superclass.constructor.call(this);
24367 };
24368
24369 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24370
24371     /**
24372      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24373      */
24374 /*
24375  * Based on:
24376  * Ext JS Library 1.1.1
24377  * Copyright(c) 2006-2007, Ext JS, LLC.
24378  *
24379  * Originally Released Under LGPL - original licence link has changed is not relivant.
24380  *
24381  * Fork - LGPL
24382  * <script type="text/javascript">
24383  */
24384 /**
24385  * @class Roo.data.MemoryProxy
24386  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24387  * to the Reader when its load method is called.
24388  * @constructor
24389  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24390  */
24391 Roo.data.MemoryProxy = function(data){
24392     if (data.data) {
24393         data = data.data;
24394     }
24395     Roo.data.MemoryProxy.superclass.constructor.call(this);
24396     this.data = data;
24397 };
24398
24399 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24400     
24401     /**
24402      * Load data from the requested source (in this case an in-memory
24403      * data object passed to the constructor), read the data object into
24404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24405      * process that block using the passed callback.
24406      * @param {Object} params This parameter is not used by the MemoryProxy class.
24407      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24408      * object into a block of Roo.data.Records.
24409      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24410      * The function must be passed <ul>
24411      * <li>The Record block object</li>
24412      * <li>The "arg" argument from the load function</li>
24413      * <li>A boolean success indicator</li>
24414      * </ul>
24415      * @param {Object} scope The scope in which to call the callback
24416      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24417      */
24418     load : function(params, reader, callback, scope, arg){
24419         params = params || {};
24420         var result;
24421         try {
24422             result = reader.readRecords(params.data ? params.data :this.data);
24423         }catch(e){
24424             this.fireEvent("loadexception", this, arg, null, e);
24425             callback.call(scope, null, arg, false);
24426             return;
24427         }
24428         callback.call(scope, result, arg, true);
24429     },
24430     
24431     // private
24432     update : function(params, records){
24433         
24434     }
24435 });/*
24436  * Based on:
24437  * Ext JS Library 1.1.1
24438  * Copyright(c) 2006-2007, Ext JS, LLC.
24439  *
24440  * Originally Released Under LGPL - original licence link has changed is not relivant.
24441  *
24442  * Fork - LGPL
24443  * <script type="text/javascript">
24444  */
24445 /**
24446  * @class Roo.data.HttpProxy
24447  * @extends Roo.data.DataProxy
24448  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24449  * configured to reference a certain URL.<br><br>
24450  * <p>
24451  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24452  * from which the running page was served.<br><br>
24453  * <p>
24454  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24455  * <p>
24456  * Be aware that to enable the browser to parse an XML document, the server must set
24457  * the Content-Type header in the HTTP response to "text/xml".
24458  * @constructor
24459  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24460  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24461  * will be used to make the request.
24462  */
24463 Roo.data.HttpProxy = function(conn){
24464     Roo.data.HttpProxy.superclass.constructor.call(this);
24465     // is conn a conn config or a real conn?
24466     this.conn = conn;
24467     this.useAjax = !conn || !conn.events;
24468   
24469 };
24470
24471 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24472     // thse are take from connection...
24473     
24474     /**
24475      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24476      */
24477     /**
24478      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24479      * extra parameters to each request made by this object. (defaults to undefined)
24480      */
24481     /**
24482      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24483      *  to each request made by this object. (defaults to undefined)
24484      */
24485     /**
24486      * @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)
24487      */
24488     /**
24489      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24490      */
24491      /**
24492      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24493      * @type Boolean
24494      */
24495   
24496
24497     /**
24498      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24499      * @type Boolean
24500      */
24501     /**
24502      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24503      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24504      * a finer-grained basis than the DataProxy events.
24505      */
24506     getConnection : function(){
24507         return this.useAjax ? Roo.Ajax : this.conn;
24508     },
24509
24510     /**
24511      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24512      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24513      * process that block using the passed callback.
24514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24515      * for the request to the remote server.
24516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24517      * object into a block of Roo.data.Records.
24518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24519      * The function must be passed <ul>
24520      * <li>The Record block object</li>
24521      * <li>The "arg" argument from the load function</li>
24522      * <li>A boolean success indicator</li>
24523      * </ul>
24524      * @param {Object} scope The scope in which to call the callback
24525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24526      */
24527     load : function(params, reader, callback, scope, arg){
24528         if(this.fireEvent("beforeload", this, params) !== false){
24529             var  o = {
24530                 params : params || {},
24531                 request: {
24532                     callback : callback,
24533                     scope : scope,
24534                     arg : arg
24535                 },
24536                 reader: reader,
24537                 callback : this.loadResponse,
24538                 scope: this
24539             };
24540             if(this.useAjax){
24541                 Roo.applyIf(o, this.conn);
24542                 if(this.activeRequest){
24543                     Roo.Ajax.abort(this.activeRequest);
24544                 }
24545                 this.activeRequest = Roo.Ajax.request(o);
24546             }else{
24547                 this.conn.request(o);
24548             }
24549         }else{
24550             callback.call(scope||this, null, arg, false);
24551         }
24552     },
24553
24554     // private
24555     loadResponse : function(o, success, response){
24556         delete this.activeRequest;
24557         if(!success){
24558             this.fireEvent("loadexception", this, o, response);
24559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24560             return;
24561         }
24562         var result;
24563         try {
24564             result = o.reader.read(response);
24565         }catch(e){
24566             this.fireEvent("loadexception", this, o, response, e);
24567             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24568             return;
24569         }
24570         
24571         this.fireEvent("load", this, o, o.request.arg);
24572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24573     },
24574
24575     // private
24576     update : function(dataSet){
24577
24578     },
24579
24580     // private
24581     updateResponse : function(dataSet){
24582
24583     }
24584 });/*
24585  * Based on:
24586  * Ext JS Library 1.1.1
24587  * Copyright(c) 2006-2007, Ext JS, LLC.
24588  *
24589  * Originally Released Under LGPL - original licence link has changed is not relivant.
24590  *
24591  * Fork - LGPL
24592  * <script type="text/javascript">
24593  */
24594
24595 /**
24596  * @class Roo.data.ScriptTagProxy
24597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24598  * other than the originating domain of the running page.<br><br>
24599  * <p>
24600  * <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
24601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24602  * <p>
24603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24604  * source code that is used as the source inside a &lt;script> tag.<br><br>
24605  * <p>
24606  * In order for the browser to process the returned data, the server must wrap the data object
24607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24609  * depending on whether the callback name was passed:
24610  * <p>
24611  * <pre><code>
24612 boolean scriptTag = false;
24613 String cb = request.getParameter("callback");
24614 if (cb != null) {
24615     scriptTag = true;
24616     response.setContentType("text/javascript");
24617 } else {
24618     response.setContentType("application/x-json");
24619 }
24620 Writer out = response.getWriter();
24621 if (scriptTag) {
24622     out.write(cb + "(");
24623 }
24624 out.print(dataBlock.toJsonString());
24625 if (scriptTag) {
24626     out.write(");");
24627 }
24628 </pre></code>
24629  *
24630  * @constructor
24631  * @param {Object} config A configuration object.
24632  */
24633 Roo.data.ScriptTagProxy = function(config){
24634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24635     Roo.apply(this, config);
24636     this.head = document.getElementsByTagName("head")[0];
24637 };
24638
24639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24640
24641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24642     /**
24643      * @cfg {String} url The URL from which to request the data object.
24644      */
24645     /**
24646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24647      */
24648     timeout : 30000,
24649     /**
24650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24651      * the server the name of the callback function set up by the load call to process the returned data object.
24652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24653      * javascript output which calls this named function passing the data object as its only parameter.
24654      */
24655     callbackParam : "callback",
24656     /**
24657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24658      * name to the request.
24659      */
24660     nocache : true,
24661
24662     /**
24663      * Load data from the configured URL, read the data object into
24664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24665      * process that block using the passed callback.
24666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24667      * for the request to the remote server.
24668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24669      * object into a block of Roo.data.Records.
24670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24671      * The function must be passed <ul>
24672      * <li>The Record block object</li>
24673      * <li>The "arg" argument from the load function</li>
24674      * <li>A boolean success indicator</li>
24675      * </ul>
24676      * @param {Object} scope The scope in which to call the callback
24677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24678      */
24679     load : function(params, reader, callback, scope, arg){
24680         if(this.fireEvent("beforeload", this, params) !== false){
24681
24682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24683
24684             var url = this.url;
24685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24686             if(this.nocache){
24687                 url += "&_dc=" + (new Date().getTime());
24688             }
24689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24690             var trans = {
24691                 id : transId,
24692                 cb : "stcCallback"+transId,
24693                 scriptId : "stcScript"+transId,
24694                 params : params,
24695                 arg : arg,
24696                 url : url,
24697                 callback : callback,
24698                 scope : scope,
24699                 reader : reader
24700             };
24701             var conn = this;
24702
24703             window[trans.cb] = function(o){
24704                 conn.handleResponse(o, trans);
24705             };
24706
24707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24708
24709             if(this.autoAbort !== false){
24710                 this.abort();
24711             }
24712
24713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24714
24715             var script = document.createElement("script");
24716             script.setAttribute("src", url);
24717             script.setAttribute("type", "text/javascript");
24718             script.setAttribute("id", trans.scriptId);
24719             this.head.appendChild(script);
24720
24721             this.trans = trans;
24722         }else{
24723             callback.call(scope||this, null, arg, false);
24724         }
24725     },
24726
24727     // private
24728     isLoading : function(){
24729         return this.trans ? true : false;
24730     },
24731
24732     /**
24733      * Abort the current server request.
24734      */
24735     abort : function(){
24736         if(this.isLoading()){
24737             this.destroyTrans(this.trans);
24738         }
24739     },
24740
24741     // private
24742     destroyTrans : function(trans, isLoaded){
24743         this.head.removeChild(document.getElementById(trans.scriptId));
24744         clearTimeout(trans.timeoutId);
24745         if(isLoaded){
24746             window[trans.cb] = undefined;
24747             try{
24748                 delete window[trans.cb];
24749             }catch(e){}
24750         }else{
24751             // if hasn't been loaded, wait for load to remove it to prevent script error
24752             window[trans.cb] = function(){
24753                 window[trans.cb] = undefined;
24754                 try{
24755                     delete window[trans.cb];
24756                 }catch(e){}
24757             };
24758         }
24759     },
24760
24761     // private
24762     handleResponse : function(o, trans){
24763         this.trans = false;
24764         this.destroyTrans(trans, true);
24765         var result;
24766         try {
24767             result = trans.reader.readRecords(o);
24768         }catch(e){
24769             this.fireEvent("loadexception", this, o, trans.arg, e);
24770             trans.callback.call(trans.scope||window, null, trans.arg, false);
24771             return;
24772         }
24773         this.fireEvent("load", this, o, trans.arg);
24774         trans.callback.call(trans.scope||window, result, trans.arg, true);
24775     },
24776
24777     // private
24778     handleFailure : function(trans){
24779         this.trans = false;
24780         this.destroyTrans(trans, false);
24781         this.fireEvent("loadexception", this, null, trans.arg);
24782         trans.callback.call(trans.scope||window, null, trans.arg, false);
24783     }
24784 });/*
24785  * Based on:
24786  * Ext JS Library 1.1.1
24787  * Copyright(c) 2006-2007, Ext JS, LLC.
24788  *
24789  * Originally Released Under LGPL - original licence link has changed is not relivant.
24790  *
24791  * Fork - LGPL
24792  * <script type="text/javascript">
24793  */
24794
24795 /**
24796  * @class Roo.data.JsonReader
24797  * @extends Roo.data.DataReader
24798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24799  * based on mappings in a provided Roo.data.Record constructor.
24800  * 
24801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24802  * in the reply previously. 
24803  * 
24804  * <p>
24805  * Example code:
24806  * <pre><code>
24807 var RecordDef = Roo.data.Record.create([
24808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24810 ]);
24811 var myReader = new Roo.data.JsonReader({
24812     totalProperty: "results",    // The property which contains the total dataset size (optional)
24813     root: "rows",                // The property which contains an Array of row objects
24814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24815 }, RecordDef);
24816 </code></pre>
24817  * <p>
24818  * This would consume a JSON file like this:
24819  * <pre><code>
24820 { 'results': 2, 'rows': [
24821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24823 }
24824 </code></pre>
24825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24827  * paged from the remote server.
24828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24829  * @cfg {String} root name of the property which contains the Array of row objects.
24830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24831  * @cfg {Array} fields Array of field definition objects
24832  * @constructor
24833  * Create a new JsonReader
24834  * @param {Object} meta Metadata configuration options
24835  * @param {Object} recordType Either an Array of field definition objects,
24836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24837  */
24838 Roo.data.JsonReader = function(meta, recordType){
24839     
24840     meta = meta || {};
24841     // set some defaults:
24842     Roo.applyIf(meta, {
24843         totalProperty: 'total',
24844         successProperty : 'success',
24845         root : 'data',
24846         id : 'id'
24847     });
24848     
24849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24850 };
24851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24852     
24853     readerType : 'Json',
24854     
24855     /**
24856      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24857      * Used by Store query builder to append _requestMeta to params.
24858      * 
24859      */
24860     metaFromRemote : false,
24861     /**
24862      * This method is only used by a DataProxy which has retrieved data from a remote server.
24863      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24864      * @return {Object} data A data block which is used by an Roo.data.Store object as
24865      * a cache of Roo.data.Records.
24866      */
24867     read : function(response){
24868         var json = response.responseText;
24869        
24870         var o = /* eval:var:o */ eval("("+json+")");
24871         if(!o) {
24872             throw {message: "JsonReader.read: Json object not found"};
24873         }
24874         
24875         if(o.metaData){
24876             
24877             delete this.ef;
24878             this.metaFromRemote = true;
24879             this.meta = o.metaData;
24880             this.recordType = Roo.data.Record.create(o.metaData.fields);
24881             this.onMetaChange(this.meta, this.recordType, o);
24882         }
24883         return this.readRecords(o);
24884     },
24885
24886     // private function a store will implement
24887     onMetaChange : function(meta, recordType, o){
24888
24889     },
24890
24891     /**
24892          * @ignore
24893          */
24894     simpleAccess: function(obj, subsc) {
24895         return obj[subsc];
24896     },
24897
24898         /**
24899          * @ignore
24900          */
24901     getJsonAccessor: function(){
24902         var re = /[\[\.]/;
24903         return function(expr) {
24904             try {
24905                 return(re.test(expr))
24906                     ? new Function("obj", "return obj." + expr)
24907                     : function(obj){
24908                         return obj[expr];
24909                     };
24910             } catch(e){}
24911             return Roo.emptyFn;
24912         };
24913     }(),
24914
24915     /**
24916      * Create a data block containing Roo.data.Records from an XML document.
24917      * @param {Object} o An object which contains an Array of row objects in the property specified
24918      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24919      * which contains the total size of the dataset.
24920      * @return {Object} data A data block which is used by an Roo.data.Store object as
24921      * a cache of Roo.data.Records.
24922      */
24923     readRecords : function(o){
24924         /**
24925          * After any data loads, the raw JSON data is available for further custom processing.
24926          * @type Object
24927          */
24928         this.o = o;
24929         var s = this.meta, Record = this.recordType,
24930             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24931
24932 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24933         if (!this.ef) {
24934             if(s.totalProperty) {
24935                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24936                 }
24937                 if(s.successProperty) {
24938                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24939                 }
24940                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24941                 if (s.id) {
24942                         var g = this.getJsonAccessor(s.id);
24943                         this.getId = function(rec) {
24944                                 var r = g(rec);  
24945                                 return (r === undefined || r === "") ? null : r;
24946                         };
24947                 } else {
24948                         this.getId = function(){return null;};
24949                 }
24950             this.ef = [];
24951             for(var jj = 0; jj < fl; jj++){
24952                 f = fi[jj];
24953                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24954                 this.ef[jj] = this.getJsonAccessor(map);
24955             }
24956         }
24957
24958         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24959         if(s.totalProperty){
24960             var vt = parseInt(this.getTotal(o), 10);
24961             if(!isNaN(vt)){
24962                 totalRecords = vt;
24963             }
24964         }
24965         if(s.successProperty){
24966             var vs = this.getSuccess(o);
24967             if(vs === false || vs === 'false'){
24968                 success = false;
24969             }
24970         }
24971         var records = [];
24972         for(var i = 0; i < c; i++){
24973                 var n = root[i];
24974             var values = {};
24975             var id = this.getId(n);
24976             for(var j = 0; j < fl; j++){
24977                 f = fi[j];
24978             var v = this.ef[j](n);
24979             if (!f.convert) {
24980                 Roo.log('missing convert for ' + f.name);
24981                 Roo.log(f);
24982                 continue;
24983             }
24984             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24985             }
24986             var record = new Record(values, id);
24987             record.json = n;
24988             records[i] = record;
24989         }
24990         return {
24991             raw : o,
24992             success : success,
24993             records : records,
24994             totalRecords : totalRecords
24995         };
24996     },
24997     // used when loading children.. @see loadDataFromChildren
24998     toLoadData: function(rec)
24999     {
25000         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25001         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25002         return { data : data, total : data.length };
25003         
25004     }
25005 });/*
25006  * Based on:
25007  * Ext JS Library 1.1.1
25008  * Copyright(c) 2006-2007, Ext JS, LLC.
25009  *
25010  * Originally Released Under LGPL - original licence link has changed is not relivant.
25011  *
25012  * Fork - LGPL
25013  * <script type="text/javascript">
25014  */
25015
25016 /**
25017  * @class Roo.data.XmlReader
25018  * @extends Roo.data.DataReader
25019  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25020  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25021  * <p>
25022  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25023  * header in the HTTP response must be set to "text/xml".</em>
25024  * <p>
25025  * Example code:
25026  * <pre><code>
25027 var RecordDef = Roo.data.Record.create([
25028    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25029    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25030 ]);
25031 var myReader = new Roo.data.XmlReader({
25032    totalRecords: "results", // The element which contains the total dataset size (optional)
25033    record: "row",           // The repeated element which contains row information
25034    id: "id"                 // The element within the row that provides an ID for the record (optional)
25035 }, RecordDef);
25036 </code></pre>
25037  * <p>
25038  * This would consume an XML file like this:
25039  * <pre><code>
25040 &lt;?xml?>
25041 &lt;dataset>
25042  &lt;results>2&lt;/results>
25043  &lt;row>
25044    &lt;id>1&lt;/id>
25045    &lt;name>Bill&lt;/name>
25046    &lt;occupation>Gardener&lt;/occupation>
25047  &lt;/row>
25048  &lt;row>
25049    &lt;id>2&lt;/id>
25050    &lt;name>Ben&lt;/name>
25051    &lt;occupation>Horticulturalist&lt;/occupation>
25052  &lt;/row>
25053 &lt;/dataset>
25054 </code></pre>
25055  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25056  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25057  * paged from the remote server.
25058  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25059  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25060  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25061  * a record identifier value.
25062  * @constructor
25063  * Create a new XmlReader
25064  * @param {Object} meta Metadata configuration options
25065  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25066  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25067  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25068  */
25069 Roo.data.XmlReader = function(meta, recordType){
25070     meta = meta || {};
25071     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25072 };
25073 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25074     
25075     readerType : 'Xml',
25076     
25077     /**
25078      * This method is only used by a DataProxy which has retrieved data from a remote server.
25079          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25080          * to contain a method called 'responseXML' that returns an XML document object.
25081      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25082      * a cache of Roo.data.Records.
25083      */
25084     read : function(response){
25085         var doc = response.responseXML;
25086         if(!doc) {
25087             throw {message: "XmlReader.read: XML Document not available"};
25088         }
25089         return this.readRecords(doc);
25090     },
25091
25092     /**
25093      * Create a data block containing Roo.data.Records from an XML document.
25094          * @param {Object} doc A parsed XML document.
25095      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25096      * a cache of Roo.data.Records.
25097      */
25098     readRecords : function(doc){
25099         /**
25100          * After any data loads/reads, the raw XML Document is available for further custom processing.
25101          * @type XMLDocument
25102          */
25103         this.xmlData = doc;
25104         var root = doc.documentElement || doc;
25105         var q = Roo.DomQuery;
25106         var recordType = this.recordType, fields = recordType.prototype.fields;
25107         var sid = this.meta.id;
25108         var totalRecords = 0, success = true;
25109         if(this.meta.totalRecords){
25110             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25111         }
25112         
25113         if(this.meta.success){
25114             var sv = q.selectValue(this.meta.success, root, true);
25115             success = sv !== false && sv !== 'false';
25116         }
25117         var records = [];
25118         var ns = q.select(this.meta.record, root);
25119         for(var i = 0, len = ns.length; i < len; i++) {
25120                 var n = ns[i];
25121                 var values = {};
25122                 var id = sid ? q.selectValue(sid, n) : undefined;
25123                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25124                     var f = fields.items[j];
25125                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25126                     v = f.convert(v);
25127                     values[f.name] = v;
25128                 }
25129                 var record = new recordType(values, id);
25130                 record.node = n;
25131                 records[records.length] = record;
25132             }
25133
25134             return {
25135                 success : success,
25136                 records : records,
25137                 totalRecords : totalRecords || records.length
25138             };
25139     }
25140 });/*
25141  * Based on:
25142  * Ext JS Library 1.1.1
25143  * Copyright(c) 2006-2007, Ext JS, LLC.
25144  *
25145  * Originally Released Under LGPL - original licence link has changed is not relivant.
25146  *
25147  * Fork - LGPL
25148  * <script type="text/javascript">
25149  */
25150
25151 /**
25152  * @class Roo.data.ArrayReader
25153  * @extends Roo.data.DataReader
25154  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25155  * Each element of that Array represents a row of data fields. The
25156  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25157  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25158  * <p>
25159  * Example code:.
25160  * <pre><code>
25161 var RecordDef = Roo.data.Record.create([
25162     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25163     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25164 ]);
25165 var myReader = new Roo.data.ArrayReader({
25166     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25167 }, RecordDef);
25168 </code></pre>
25169  * <p>
25170  * This would consume an Array like this:
25171  * <pre><code>
25172 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25173   </code></pre>
25174  
25175  * @constructor
25176  * Create a new JsonReader
25177  * @param {Object} meta Metadata configuration options.
25178  * @param {Object|Array} recordType Either an Array of field definition objects
25179  * 
25180  * @cfg {Array} fields Array of field definition objects
25181  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25182  * as specified to {@link Roo.data.Record#create},
25183  * or an {@link Roo.data.Record} object
25184  *
25185  * 
25186  * created using {@link Roo.data.Record#create}.
25187  */
25188 Roo.data.ArrayReader = function(meta, recordType)
25189 {    
25190     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25191 };
25192
25193 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25194     
25195       /**
25196      * Create a data block containing Roo.data.Records from an XML document.
25197      * @param {Object} o An Array of row objects which represents the dataset.
25198      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25199      * a cache of Roo.data.Records.
25200      */
25201     readRecords : function(o)
25202     {
25203         var sid = this.meta ? this.meta.id : null;
25204         var recordType = this.recordType, fields = recordType.prototype.fields;
25205         var records = [];
25206         var root = o;
25207         for(var i = 0; i < root.length; i++){
25208                 var n = root[i];
25209             var values = {};
25210             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25211             for(var j = 0, jlen = fields.length; j < jlen; j++){
25212                 var f = fields.items[j];
25213                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25214                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25215                 v = f.convert(v);
25216                 values[f.name] = v;
25217             }
25218             var record = new recordType(values, id);
25219             record.json = n;
25220             records[records.length] = record;
25221         }
25222         return {
25223             records : records,
25224             totalRecords : records.length
25225         };
25226     },
25227     // used when loading children.. @see loadDataFromChildren
25228     toLoadData: function(rec)
25229     {
25230         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25231         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25232         
25233     }
25234     
25235     
25236 });/*
25237  * Based on:
25238  * Ext JS Library 1.1.1
25239  * Copyright(c) 2006-2007, Ext JS, LLC.
25240  *
25241  * Originally Released Under LGPL - original licence link has changed is not relivant.
25242  *
25243  * Fork - LGPL
25244  * <script type="text/javascript">
25245  */
25246
25247
25248 /**
25249  * @class Roo.data.Tree
25250  * @extends Roo.util.Observable
25251  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25252  * in the tree have most standard DOM functionality.
25253  * @constructor
25254  * @param {Node} root (optional) The root node
25255  */
25256 Roo.data.Tree = function(root){
25257    this.nodeHash = {};
25258    /**
25259     * The root node for this tree
25260     * @type Node
25261     */
25262    this.root = null;
25263    if(root){
25264        this.setRootNode(root);
25265    }
25266    this.addEvents({
25267        /**
25268         * @event append
25269         * Fires when a new child node is appended to a node in this tree.
25270         * @param {Tree} tree The owner tree
25271         * @param {Node} parent The parent node
25272         * @param {Node} node The newly appended node
25273         * @param {Number} index The index of the newly appended node
25274         */
25275        "append" : true,
25276        /**
25277         * @event remove
25278         * Fires when a child node is removed from a node in this tree.
25279         * @param {Tree} tree The owner tree
25280         * @param {Node} parent The parent node
25281         * @param {Node} node The child node removed
25282         */
25283        "remove" : true,
25284        /**
25285         * @event move
25286         * Fires when a node is moved to a new location in the tree
25287         * @param {Tree} tree The owner tree
25288         * @param {Node} node The node moved
25289         * @param {Node} oldParent The old parent of this node
25290         * @param {Node} newParent The new parent of this node
25291         * @param {Number} index The index it was moved to
25292         */
25293        "move" : true,
25294        /**
25295         * @event insert
25296         * Fires when a new child node is inserted in a node in this tree.
25297         * @param {Tree} tree The owner tree
25298         * @param {Node} parent The parent node
25299         * @param {Node} node The child node inserted
25300         * @param {Node} refNode The child node the node was inserted before
25301         */
25302        "insert" : true,
25303        /**
25304         * @event beforeappend
25305         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25306         * @param {Tree} tree The owner tree
25307         * @param {Node} parent The parent node
25308         * @param {Node} node The child node to be appended
25309         */
25310        "beforeappend" : true,
25311        /**
25312         * @event beforeremove
25313         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25314         * @param {Tree} tree The owner tree
25315         * @param {Node} parent The parent node
25316         * @param {Node} node The child node to be removed
25317         */
25318        "beforeremove" : true,
25319        /**
25320         * @event beforemove
25321         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25322         * @param {Tree} tree The owner tree
25323         * @param {Node} node The node being moved
25324         * @param {Node} oldParent The parent of the node
25325         * @param {Node} newParent The new parent the node is moving to
25326         * @param {Number} index The index it is being moved to
25327         */
25328        "beforemove" : true,
25329        /**
25330         * @event beforeinsert
25331         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25332         * @param {Tree} tree The owner tree
25333         * @param {Node} parent The parent node
25334         * @param {Node} node The child node to be inserted
25335         * @param {Node} refNode The child node the node is being inserted before
25336         */
25337        "beforeinsert" : true
25338    });
25339
25340     Roo.data.Tree.superclass.constructor.call(this);
25341 };
25342
25343 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25344     pathSeparator: "/",
25345
25346     proxyNodeEvent : function(){
25347         return this.fireEvent.apply(this, arguments);
25348     },
25349
25350     /**
25351      * Returns the root node for this tree.
25352      * @return {Node}
25353      */
25354     getRootNode : function(){
25355         return this.root;
25356     },
25357
25358     /**
25359      * Sets the root node for this tree.
25360      * @param {Node} node
25361      * @return {Node}
25362      */
25363     setRootNode : function(node){
25364         this.root = node;
25365         node.ownerTree = this;
25366         node.isRoot = true;
25367         this.registerNode(node);
25368         return node;
25369     },
25370
25371     /**
25372      * Gets a node in this tree by its id.
25373      * @param {String} id
25374      * @return {Node}
25375      */
25376     getNodeById : function(id){
25377         return this.nodeHash[id];
25378     },
25379
25380     registerNode : function(node){
25381         this.nodeHash[node.id] = node;
25382     },
25383
25384     unregisterNode : function(node){
25385         delete this.nodeHash[node.id];
25386     },
25387
25388     toString : function(){
25389         return "[Tree"+(this.id?" "+this.id:"")+"]";
25390     }
25391 });
25392
25393 /**
25394  * @class Roo.data.Node
25395  * @extends Roo.util.Observable
25396  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25397  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25398  * @constructor
25399  * @param {Object} attributes The attributes/config for the node
25400  */
25401 Roo.data.Node = function(attributes){
25402     /**
25403      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25404      * @type {Object}
25405      */
25406     this.attributes = attributes || {};
25407     this.leaf = this.attributes.leaf;
25408     /**
25409      * The node id. @type String
25410      */
25411     this.id = this.attributes.id;
25412     if(!this.id){
25413         this.id = Roo.id(null, "ynode-");
25414         this.attributes.id = this.id;
25415     }
25416      
25417     
25418     /**
25419      * All child nodes of this node. @type Array
25420      */
25421     this.childNodes = [];
25422     if(!this.childNodes.indexOf){ // indexOf is a must
25423         this.childNodes.indexOf = function(o){
25424             for(var i = 0, len = this.length; i < len; i++){
25425                 if(this[i] == o) {
25426                     return i;
25427                 }
25428             }
25429             return -1;
25430         };
25431     }
25432     /**
25433      * The parent node for this node. @type Node
25434      */
25435     this.parentNode = null;
25436     /**
25437      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25438      */
25439     this.firstChild = null;
25440     /**
25441      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25442      */
25443     this.lastChild = null;
25444     /**
25445      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25446      */
25447     this.previousSibling = null;
25448     /**
25449      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25450      */
25451     this.nextSibling = null;
25452
25453     this.addEvents({
25454        /**
25455         * @event append
25456         * Fires when a new child node is appended
25457         * @param {Tree} tree The owner tree
25458         * @param {Node} this This node
25459         * @param {Node} node The newly appended node
25460         * @param {Number} index The index of the newly appended node
25461         */
25462        "append" : true,
25463        /**
25464         * @event remove
25465         * Fires when a child node is removed
25466         * @param {Tree} tree The owner tree
25467         * @param {Node} this This node
25468         * @param {Node} node The removed node
25469         */
25470        "remove" : true,
25471        /**
25472         * @event move
25473         * Fires when this node is moved to a new location in the tree
25474         * @param {Tree} tree The owner tree
25475         * @param {Node} this This node
25476         * @param {Node} oldParent The old parent of this node
25477         * @param {Node} newParent The new parent of this node
25478         * @param {Number} index The index it was moved to
25479         */
25480        "move" : true,
25481        /**
25482         * @event insert
25483         * Fires when a new child node is inserted.
25484         * @param {Tree} tree The owner tree
25485         * @param {Node} this This node
25486         * @param {Node} node The child node inserted
25487         * @param {Node} refNode The child node the node was inserted before
25488         */
25489        "insert" : true,
25490        /**
25491         * @event beforeappend
25492         * Fires before a new child is appended, return false to cancel the append.
25493         * @param {Tree} tree The owner tree
25494         * @param {Node} this This node
25495         * @param {Node} node The child node to be appended
25496         */
25497        "beforeappend" : true,
25498        /**
25499         * @event beforeremove
25500         * Fires before a child is removed, return false to cancel the remove.
25501         * @param {Tree} tree The owner tree
25502         * @param {Node} this This node
25503         * @param {Node} node The child node to be removed
25504         */
25505        "beforeremove" : true,
25506        /**
25507         * @event beforemove
25508         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25509         * @param {Tree} tree The owner tree
25510         * @param {Node} this This node
25511         * @param {Node} oldParent The parent of this node
25512         * @param {Node} newParent The new parent this node is moving to
25513         * @param {Number} index The index it is being moved to
25514         */
25515        "beforemove" : true,
25516        /**
25517         * @event beforeinsert
25518         * Fires before a new child is inserted, return false to cancel the insert.
25519         * @param {Tree} tree The owner tree
25520         * @param {Node} this This node
25521         * @param {Node} node The child node to be inserted
25522         * @param {Node} refNode The child node the node is being inserted before
25523         */
25524        "beforeinsert" : true
25525    });
25526     this.listeners = this.attributes.listeners;
25527     Roo.data.Node.superclass.constructor.call(this);
25528 };
25529
25530 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25531     fireEvent : function(evtName){
25532         // first do standard event for this node
25533         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25534             return false;
25535         }
25536         // then bubble it up to the tree if the event wasn't cancelled
25537         var ot = this.getOwnerTree();
25538         if(ot){
25539             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25540                 return false;
25541             }
25542         }
25543         return true;
25544     },
25545
25546     /**
25547      * Returns true if this node is a leaf
25548      * @return {Boolean}
25549      */
25550     isLeaf : function(){
25551         return this.leaf === true;
25552     },
25553
25554     // private
25555     setFirstChild : function(node){
25556         this.firstChild = node;
25557     },
25558
25559     //private
25560     setLastChild : function(node){
25561         this.lastChild = node;
25562     },
25563
25564
25565     /**
25566      * Returns true if this node is the last child of its parent
25567      * @return {Boolean}
25568      */
25569     isLast : function(){
25570        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25571     },
25572
25573     /**
25574      * Returns true if this node is the first child of its parent
25575      * @return {Boolean}
25576      */
25577     isFirst : function(){
25578        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25579     },
25580
25581     hasChildNodes : function(){
25582         return !this.isLeaf() && this.childNodes.length > 0;
25583     },
25584
25585     /**
25586      * Insert node(s) as the last child node of this node.
25587      * @param {Node/Array} node The node or Array of nodes to append
25588      * @return {Node} The appended node if single append, or null if an array was passed
25589      */
25590     appendChild : function(node){
25591         var multi = false;
25592         if(node instanceof Array){
25593             multi = node;
25594         }else if(arguments.length > 1){
25595             multi = arguments;
25596         }
25597         
25598         // if passed an array or multiple args do them one by one
25599         if(multi){
25600             for(var i = 0, len = multi.length; i < len; i++) {
25601                 this.appendChild(multi[i]);
25602             }
25603         }else{
25604             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25605                 return false;
25606             }
25607             var index = this.childNodes.length;
25608             var oldParent = node.parentNode;
25609             // it's a move, make sure we move it cleanly
25610             if(oldParent){
25611                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25612                     return false;
25613                 }
25614                 oldParent.removeChild(node);
25615             }
25616             
25617             index = this.childNodes.length;
25618             if(index == 0){
25619                 this.setFirstChild(node);
25620             }
25621             this.childNodes.push(node);
25622             node.parentNode = this;
25623             var ps = this.childNodes[index-1];
25624             if(ps){
25625                 node.previousSibling = ps;
25626                 ps.nextSibling = node;
25627             }else{
25628                 node.previousSibling = null;
25629             }
25630             node.nextSibling = null;
25631             this.setLastChild(node);
25632             node.setOwnerTree(this.getOwnerTree());
25633             this.fireEvent("append", this.ownerTree, this, node, index);
25634             if(this.ownerTree) {
25635                 this.ownerTree.fireEvent("appendnode", this, node, index);
25636             }
25637             if(oldParent){
25638                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25639             }
25640             return node;
25641         }
25642     },
25643
25644     /**
25645      * Removes a child node from this node.
25646      * @param {Node} node The node to remove
25647      * @return {Node} The removed node
25648      */
25649     removeChild : function(node){
25650         var index = this.childNodes.indexOf(node);
25651         if(index == -1){
25652             return false;
25653         }
25654         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25655             return false;
25656         }
25657
25658         // remove it from childNodes collection
25659         this.childNodes.splice(index, 1);
25660
25661         // update siblings
25662         if(node.previousSibling){
25663             node.previousSibling.nextSibling = node.nextSibling;
25664         }
25665         if(node.nextSibling){
25666             node.nextSibling.previousSibling = node.previousSibling;
25667         }
25668
25669         // update child refs
25670         if(this.firstChild == node){
25671             this.setFirstChild(node.nextSibling);
25672         }
25673         if(this.lastChild == node){
25674             this.setLastChild(node.previousSibling);
25675         }
25676
25677         node.setOwnerTree(null);
25678         // clear any references from the node
25679         node.parentNode = null;
25680         node.previousSibling = null;
25681         node.nextSibling = null;
25682         this.fireEvent("remove", this.ownerTree, this, node);
25683         return node;
25684     },
25685
25686     /**
25687      * Inserts the first node before the second node in this nodes childNodes collection.
25688      * @param {Node} node The node to insert
25689      * @param {Node} refNode The node to insert before (if null the node is appended)
25690      * @return {Node} The inserted node
25691      */
25692     insertBefore : function(node, refNode){
25693         if(!refNode){ // like standard Dom, refNode can be null for append
25694             return this.appendChild(node);
25695         }
25696         // nothing to do
25697         if(node == refNode){
25698             return false;
25699         }
25700
25701         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25702             return false;
25703         }
25704         var index = this.childNodes.indexOf(refNode);
25705         var oldParent = node.parentNode;
25706         var refIndex = index;
25707
25708         // when moving internally, indexes will change after remove
25709         if(oldParent == this && this.childNodes.indexOf(node) < index){
25710             refIndex--;
25711         }
25712
25713         // it's a move, make sure we move it cleanly
25714         if(oldParent){
25715             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25716                 return false;
25717             }
25718             oldParent.removeChild(node);
25719         }
25720         if(refIndex == 0){
25721             this.setFirstChild(node);
25722         }
25723         this.childNodes.splice(refIndex, 0, node);
25724         node.parentNode = this;
25725         var ps = this.childNodes[refIndex-1];
25726         if(ps){
25727             node.previousSibling = ps;
25728             ps.nextSibling = node;
25729         }else{
25730             node.previousSibling = null;
25731         }
25732         node.nextSibling = refNode;
25733         refNode.previousSibling = node;
25734         node.setOwnerTree(this.getOwnerTree());
25735         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25736         if(oldParent){
25737             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25738         }
25739         return node;
25740     },
25741
25742     /**
25743      * Returns the child node at the specified index.
25744      * @param {Number} index
25745      * @return {Node}
25746      */
25747     item : function(index){
25748         return this.childNodes[index];
25749     },
25750
25751     /**
25752      * Replaces one child node in this node with another.
25753      * @param {Node} newChild The replacement node
25754      * @param {Node} oldChild The node to replace
25755      * @return {Node} The replaced node
25756      */
25757     replaceChild : function(newChild, oldChild){
25758         this.insertBefore(newChild, oldChild);
25759         this.removeChild(oldChild);
25760         return oldChild;
25761     },
25762
25763     /**
25764      * Returns the index of a child node
25765      * @param {Node} node
25766      * @return {Number} The index of the node or -1 if it was not found
25767      */
25768     indexOf : function(child){
25769         return this.childNodes.indexOf(child);
25770     },
25771
25772     /**
25773      * Returns the tree this node is in.
25774      * @return {Tree}
25775      */
25776     getOwnerTree : function(){
25777         // if it doesn't have one, look for one
25778         if(!this.ownerTree){
25779             var p = this;
25780             while(p){
25781                 if(p.ownerTree){
25782                     this.ownerTree = p.ownerTree;
25783                     break;
25784                 }
25785                 p = p.parentNode;
25786             }
25787         }
25788         return this.ownerTree;
25789     },
25790
25791     /**
25792      * Returns depth of this node (the root node has a depth of 0)
25793      * @return {Number}
25794      */
25795     getDepth : function(){
25796         var depth = 0;
25797         var p = this;
25798         while(p.parentNode){
25799             ++depth;
25800             p = p.parentNode;
25801         }
25802         return depth;
25803     },
25804
25805     // private
25806     setOwnerTree : function(tree){
25807         // if it's move, we need to update everyone
25808         if(tree != this.ownerTree){
25809             if(this.ownerTree){
25810                 this.ownerTree.unregisterNode(this);
25811             }
25812             this.ownerTree = tree;
25813             var cs = this.childNodes;
25814             for(var i = 0, len = cs.length; i < len; i++) {
25815                 cs[i].setOwnerTree(tree);
25816             }
25817             if(tree){
25818                 tree.registerNode(this);
25819             }
25820         }
25821     },
25822
25823     /**
25824      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25825      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25826      * @return {String} The path
25827      */
25828     getPath : function(attr){
25829         attr = attr || "id";
25830         var p = this.parentNode;
25831         var b = [this.attributes[attr]];
25832         while(p){
25833             b.unshift(p.attributes[attr]);
25834             p = p.parentNode;
25835         }
25836         var sep = this.getOwnerTree().pathSeparator;
25837         return sep + b.join(sep);
25838     },
25839
25840     /**
25841      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25842      * function call will be the scope provided or the current node. The arguments to the function
25843      * will be the args provided or the current node. If the function returns false at any point,
25844      * the bubble is stopped.
25845      * @param {Function} fn The function to call
25846      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25847      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25848      */
25849     bubble : function(fn, scope, args){
25850         var p = this;
25851         while(p){
25852             if(fn.call(scope || p, args || p) === false){
25853                 break;
25854             }
25855             p = p.parentNode;
25856         }
25857     },
25858
25859     /**
25860      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25861      * function call will be the scope provided or the current node. The arguments to the function
25862      * will be the args provided or the current node. If the function returns false at any point,
25863      * the cascade is stopped on that branch.
25864      * @param {Function} fn The function to call
25865      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25866      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25867      */
25868     cascade : function(fn, scope, args){
25869         if(fn.call(scope || this, args || this) !== false){
25870             var cs = this.childNodes;
25871             for(var i = 0, len = cs.length; i < len; i++) {
25872                 cs[i].cascade(fn, scope, args);
25873             }
25874         }
25875     },
25876
25877     /**
25878      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25879      * function call will be the scope provided or the current node. The arguments to the function
25880      * will be the args provided or the current node. If the function returns false at any point,
25881      * the iteration stops.
25882      * @param {Function} fn The function to call
25883      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25884      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25885      */
25886     eachChild : function(fn, scope, args){
25887         var cs = this.childNodes;
25888         for(var i = 0, len = cs.length; i < len; i++) {
25889                 if(fn.call(scope || this, args || cs[i]) === false){
25890                     break;
25891                 }
25892         }
25893     },
25894
25895     /**
25896      * Finds the first child that has the attribute with the specified value.
25897      * @param {String} attribute The attribute name
25898      * @param {Mixed} value The value to search for
25899      * @return {Node} The found child or null if none was found
25900      */
25901     findChild : function(attribute, value){
25902         var cs = this.childNodes;
25903         for(var i = 0, len = cs.length; i < len; i++) {
25904                 if(cs[i].attributes[attribute] == value){
25905                     return cs[i];
25906                 }
25907         }
25908         return null;
25909     },
25910
25911     /**
25912      * Finds the first child by a custom function. The child matches if the function passed
25913      * returns true.
25914      * @param {Function} fn
25915      * @param {Object} scope (optional)
25916      * @return {Node} The found child or null if none was found
25917      */
25918     findChildBy : function(fn, scope){
25919         var cs = this.childNodes;
25920         for(var i = 0, len = cs.length; i < len; i++) {
25921                 if(fn.call(scope||cs[i], cs[i]) === true){
25922                     return cs[i];
25923                 }
25924         }
25925         return null;
25926     },
25927
25928     /**
25929      * Sorts this nodes children using the supplied sort function
25930      * @param {Function} fn
25931      * @param {Object} scope (optional)
25932      */
25933     sort : function(fn, scope){
25934         var cs = this.childNodes;
25935         var len = cs.length;
25936         if(len > 0){
25937             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25938             cs.sort(sortFn);
25939             for(var i = 0; i < len; i++){
25940                 var n = cs[i];
25941                 n.previousSibling = cs[i-1];
25942                 n.nextSibling = cs[i+1];
25943                 if(i == 0){
25944                     this.setFirstChild(n);
25945                 }
25946                 if(i == len-1){
25947                     this.setLastChild(n);
25948                 }
25949             }
25950         }
25951     },
25952
25953     /**
25954      * Returns true if this node is an ancestor (at any point) of the passed node.
25955      * @param {Node} node
25956      * @return {Boolean}
25957      */
25958     contains : function(node){
25959         return node.isAncestor(this);
25960     },
25961
25962     /**
25963      * Returns true if the passed node is an ancestor (at any point) of this node.
25964      * @param {Node} node
25965      * @return {Boolean}
25966      */
25967     isAncestor : function(node){
25968         var p = this.parentNode;
25969         while(p){
25970             if(p == node){
25971                 return true;
25972             }
25973             p = p.parentNode;
25974         }
25975         return false;
25976     },
25977
25978     toString : function(){
25979         return "[Node"+(this.id?" "+this.id:"")+"]";
25980     }
25981 });/*
25982  * Based on:
25983  * Ext JS Library 1.1.1
25984  * Copyright(c) 2006-2007, Ext JS, LLC.
25985  *
25986  * Originally Released Under LGPL - original licence link has changed is not relivant.
25987  *
25988  * Fork - LGPL
25989  * <script type="text/javascript">
25990  */
25991
25992
25993 /**
25994  * @class Roo.Shadow
25995  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25996  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25997  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25998  * @constructor
25999  * Create a new Shadow
26000  * @param {Object} config The config object
26001  */
26002 Roo.Shadow = function(config){
26003     Roo.apply(this, config);
26004     if(typeof this.mode != "string"){
26005         this.mode = this.defaultMode;
26006     }
26007     var o = this.offset, a = {h: 0};
26008     var rad = Math.floor(this.offset/2);
26009     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26010         case "drop":
26011             a.w = 0;
26012             a.l = a.t = o;
26013             a.t -= 1;
26014             if(Roo.isIE){
26015                 a.l -= this.offset + rad;
26016                 a.t -= this.offset + rad;
26017                 a.w -= rad;
26018                 a.h -= rad;
26019                 a.t += 1;
26020             }
26021         break;
26022         case "sides":
26023             a.w = (o*2);
26024             a.l = -o;
26025             a.t = o-1;
26026             if(Roo.isIE){
26027                 a.l -= (this.offset - rad);
26028                 a.t -= this.offset + rad;
26029                 a.l += 1;
26030                 a.w -= (this.offset - rad)*2;
26031                 a.w -= rad + 1;
26032                 a.h -= 1;
26033             }
26034         break;
26035         case "frame":
26036             a.w = a.h = (o*2);
26037             a.l = a.t = -o;
26038             a.t += 1;
26039             a.h -= 2;
26040             if(Roo.isIE){
26041                 a.l -= (this.offset - rad);
26042                 a.t -= (this.offset - rad);
26043                 a.l += 1;
26044                 a.w -= (this.offset + rad + 1);
26045                 a.h -= (this.offset + rad);
26046                 a.h += 1;
26047             }
26048         break;
26049     };
26050
26051     this.adjusts = a;
26052 };
26053
26054 Roo.Shadow.prototype = {
26055     /**
26056      * @cfg {String} mode
26057      * The shadow display mode.  Supports the following options:<br />
26058      * sides: Shadow displays on both sides and bottom only<br />
26059      * frame: Shadow displays equally on all four sides<br />
26060      * drop: Traditional bottom-right drop shadow (default)
26061      */
26062     /**
26063      * @cfg {String} offset
26064      * The number of pixels to offset the shadow from the element (defaults to 4)
26065      */
26066     offset: 4,
26067
26068     // private
26069     defaultMode: "drop",
26070
26071     /**
26072      * Displays the shadow under the target element
26073      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26074      */
26075     show : function(target){
26076         target = Roo.get(target);
26077         if(!this.el){
26078             this.el = Roo.Shadow.Pool.pull();
26079             if(this.el.dom.nextSibling != target.dom){
26080                 this.el.insertBefore(target);
26081             }
26082         }
26083         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26084         if(Roo.isIE){
26085             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26086         }
26087         this.realign(
26088             target.getLeft(true),
26089             target.getTop(true),
26090             target.getWidth(),
26091             target.getHeight()
26092         );
26093         this.el.dom.style.display = "block";
26094     },
26095
26096     /**
26097      * Returns true if the shadow is visible, else false
26098      */
26099     isVisible : function(){
26100         return this.el ? true : false;  
26101     },
26102
26103     /**
26104      * Direct alignment when values are already available. Show must be called at least once before
26105      * calling this method to ensure it is initialized.
26106      * @param {Number} left The target element left position
26107      * @param {Number} top The target element top position
26108      * @param {Number} width The target element width
26109      * @param {Number} height The target element height
26110      */
26111     realign : function(l, t, w, h){
26112         if(!this.el){
26113             return;
26114         }
26115         var a = this.adjusts, d = this.el.dom, s = d.style;
26116         var iea = 0;
26117         s.left = (l+a.l)+"px";
26118         s.top = (t+a.t)+"px";
26119         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26120  
26121         if(s.width != sws || s.height != shs){
26122             s.width = sws;
26123             s.height = shs;
26124             if(!Roo.isIE){
26125                 var cn = d.childNodes;
26126                 var sww = Math.max(0, (sw-12))+"px";
26127                 cn[0].childNodes[1].style.width = sww;
26128                 cn[1].childNodes[1].style.width = sww;
26129                 cn[2].childNodes[1].style.width = sww;
26130                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26131             }
26132         }
26133     },
26134
26135     /**
26136      * Hides this shadow
26137      */
26138     hide : function(){
26139         if(this.el){
26140             this.el.dom.style.display = "none";
26141             Roo.Shadow.Pool.push(this.el);
26142             delete this.el;
26143         }
26144     },
26145
26146     /**
26147      * Adjust the z-index of this shadow
26148      * @param {Number} zindex The new z-index
26149      */
26150     setZIndex : function(z){
26151         this.zIndex = z;
26152         if(this.el){
26153             this.el.setStyle("z-index", z);
26154         }
26155     }
26156 };
26157
26158 // Private utility class that manages the internal Shadow cache
26159 Roo.Shadow.Pool = function(){
26160     var p = [];
26161     var markup = Roo.isIE ?
26162                  '<div class="x-ie-shadow"></div>' :
26163                  '<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>';
26164     return {
26165         pull : function(){
26166             var sh = p.shift();
26167             if(!sh){
26168                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26169                 sh.autoBoxAdjust = false;
26170             }
26171             return sh;
26172         },
26173
26174         push : function(sh){
26175             p.push(sh);
26176         }
26177     };
26178 }();/*
26179  * Based on:
26180  * Ext JS Library 1.1.1
26181  * Copyright(c) 2006-2007, Ext JS, LLC.
26182  *
26183  * Originally Released Under LGPL - original licence link has changed is not relivant.
26184  *
26185  * Fork - LGPL
26186  * <script type="text/javascript">
26187  */
26188
26189
26190 /**
26191  * @class Roo.SplitBar
26192  * @extends Roo.util.Observable
26193  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26194  * <br><br>
26195  * Usage:
26196  * <pre><code>
26197 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26198                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26199 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26200 split.minSize = 100;
26201 split.maxSize = 600;
26202 split.animate = true;
26203 split.on('moved', splitterMoved);
26204 </code></pre>
26205  * @constructor
26206  * Create a new SplitBar
26207  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26208  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26209  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26210  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26211                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26212                         position of the SplitBar).
26213  */
26214 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26215     
26216     /** @private */
26217     this.el = Roo.get(dragElement, true);
26218     this.el.dom.unselectable = "on";
26219     /** @private */
26220     this.resizingEl = Roo.get(resizingElement, true);
26221
26222     /**
26223      * @private
26224      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26225      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26226      * @type Number
26227      */
26228     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26229     
26230     /**
26231      * The minimum size of the resizing element. (Defaults to 0)
26232      * @type Number
26233      */
26234     this.minSize = 0;
26235     
26236     /**
26237      * The maximum size of the resizing element. (Defaults to 2000)
26238      * @type Number
26239      */
26240     this.maxSize = 2000;
26241     
26242     /**
26243      * Whether to animate the transition to the new size
26244      * @type Boolean
26245      */
26246     this.animate = false;
26247     
26248     /**
26249      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26250      * @type Boolean
26251      */
26252     this.useShim = false;
26253     
26254     /** @private */
26255     this.shim = null;
26256     
26257     if(!existingProxy){
26258         /** @private */
26259         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26260     }else{
26261         this.proxy = Roo.get(existingProxy).dom;
26262     }
26263     /** @private */
26264     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26265     
26266     /** @private */
26267     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26268     
26269     /** @private */
26270     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26271     
26272     /** @private */
26273     this.dragSpecs = {};
26274     
26275     /**
26276      * @private The adapter to use to positon and resize elements
26277      */
26278     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26279     this.adapter.init(this);
26280     
26281     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26282         /** @private */
26283         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26284         this.el.addClass("x-splitbar-h");
26285     }else{
26286         /** @private */
26287         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26288         this.el.addClass("x-splitbar-v");
26289     }
26290     
26291     this.addEvents({
26292         /**
26293          * @event resize
26294          * Fires when the splitter is moved (alias for {@link #event-moved})
26295          * @param {Roo.SplitBar} this
26296          * @param {Number} newSize the new width or height
26297          */
26298         "resize" : true,
26299         /**
26300          * @event moved
26301          * Fires when the splitter is moved
26302          * @param {Roo.SplitBar} this
26303          * @param {Number} newSize the new width or height
26304          */
26305         "moved" : true,
26306         /**
26307          * @event beforeresize
26308          * Fires before the splitter is dragged
26309          * @param {Roo.SplitBar} this
26310          */
26311         "beforeresize" : true,
26312
26313         "beforeapply" : true
26314     });
26315
26316     Roo.util.Observable.call(this);
26317 };
26318
26319 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26320     onStartProxyDrag : function(x, y){
26321         this.fireEvent("beforeresize", this);
26322         if(!this.overlay){
26323             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26324             o.unselectable();
26325             o.enableDisplayMode("block");
26326             // all splitbars share the same overlay
26327             Roo.SplitBar.prototype.overlay = o;
26328         }
26329         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26330         this.overlay.show();
26331         Roo.get(this.proxy).setDisplayed("block");
26332         var size = this.adapter.getElementSize(this);
26333         this.activeMinSize = this.getMinimumSize();;
26334         this.activeMaxSize = this.getMaximumSize();;
26335         var c1 = size - this.activeMinSize;
26336         var c2 = Math.max(this.activeMaxSize - size, 0);
26337         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26338             this.dd.resetConstraints();
26339             this.dd.setXConstraint(
26340                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26341                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26342             );
26343             this.dd.setYConstraint(0, 0);
26344         }else{
26345             this.dd.resetConstraints();
26346             this.dd.setXConstraint(0, 0);
26347             this.dd.setYConstraint(
26348                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26349                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26350             );
26351          }
26352         this.dragSpecs.startSize = size;
26353         this.dragSpecs.startPoint = [x, y];
26354         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26355     },
26356     
26357     /** 
26358      * @private Called after the drag operation by the DDProxy
26359      */
26360     onEndProxyDrag : function(e){
26361         Roo.get(this.proxy).setDisplayed(false);
26362         var endPoint = Roo.lib.Event.getXY(e);
26363         if(this.overlay){
26364             this.overlay.hide();
26365         }
26366         var newSize;
26367         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26368             newSize = this.dragSpecs.startSize + 
26369                 (this.placement == Roo.SplitBar.LEFT ?
26370                     endPoint[0] - this.dragSpecs.startPoint[0] :
26371                     this.dragSpecs.startPoint[0] - endPoint[0]
26372                 );
26373         }else{
26374             newSize = this.dragSpecs.startSize + 
26375                 (this.placement == Roo.SplitBar.TOP ?
26376                     endPoint[1] - this.dragSpecs.startPoint[1] :
26377                     this.dragSpecs.startPoint[1] - endPoint[1]
26378                 );
26379         }
26380         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26381         if(newSize != this.dragSpecs.startSize){
26382             if(this.fireEvent('beforeapply', this, newSize) !== false){
26383                 this.adapter.setElementSize(this, newSize);
26384                 this.fireEvent("moved", this, newSize);
26385                 this.fireEvent("resize", this, newSize);
26386             }
26387         }
26388     },
26389     
26390     /**
26391      * Get the adapter this SplitBar uses
26392      * @return The adapter object
26393      */
26394     getAdapter : function(){
26395         return this.adapter;
26396     },
26397     
26398     /**
26399      * Set the adapter this SplitBar uses
26400      * @param {Object} adapter A SplitBar adapter object
26401      */
26402     setAdapter : function(adapter){
26403         this.adapter = adapter;
26404         this.adapter.init(this);
26405     },
26406     
26407     /**
26408      * Gets the minimum size for the resizing element
26409      * @return {Number} The minimum size
26410      */
26411     getMinimumSize : function(){
26412         return this.minSize;
26413     },
26414     
26415     /**
26416      * Sets the minimum size for the resizing element
26417      * @param {Number} minSize The minimum size
26418      */
26419     setMinimumSize : function(minSize){
26420         this.minSize = minSize;
26421     },
26422     
26423     /**
26424      * Gets the maximum size for the resizing element
26425      * @return {Number} The maximum size
26426      */
26427     getMaximumSize : function(){
26428         return this.maxSize;
26429     },
26430     
26431     /**
26432      * Sets the maximum size for the resizing element
26433      * @param {Number} maxSize The maximum size
26434      */
26435     setMaximumSize : function(maxSize){
26436         this.maxSize = maxSize;
26437     },
26438     
26439     /**
26440      * Sets the initialize size for the resizing element
26441      * @param {Number} size The initial size
26442      */
26443     setCurrentSize : function(size){
26444         var oldAnimate = this.animate;
26445         this.animate = false;
26446         this.adapter.setElementSize(this, size);
26447         this.animate = oldAnimate;
26448     },
26449     
26450     /**
26451      * Destroy this splitbar. 
26452      * @param {Boolean} removeEl True to remove the element
26453      */
26454     destroy : function(removeEl){
26455         if(this.shim){
26456             this.shim.remove();
26457         }
26458         this.dd.unreg();
26459         this.proxy.parentNode.removeChild(this.proxy);
26460         if(removeEl){
26461             this.el.remove();
26462         }
26463     }
26464 });
26465
26466 /**
26467  * @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.
26468  */
26469 Roo.SplitBar.createProxy = function(dir){
26470     var proxy = new Roo.Element(document.createElement("div"));
26471     proxy.unselectable();
26472     var cls = 'x-splitbar-proxy';
26473     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26474     document.body.appendChild(proxy.dom);
26475     return proxy.dom;
26476 };
26477
26478 /** 
26479  * @class Roo.SplitBar.BasicLayoutAdapter
26480  * Default Adapter. It assumes the splitter and resizing element are not positioned
26481  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26482  */
26483 Roo.SplitBar.BasicLayoutAdapter = function(){
26484 };
26485
26486 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26487     // do nothing for now
26488     init : function(s){
26489     
26490     },
26491     /**
26492      * Called before drag operations to get the current size of the resizing element. 
26493      * @param {Roo.SplitBar} s The SplitBar using this adapter
26494      */
26495      getElementSize : function(s){
26496         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26497             return s.resizingEl.getWidth();
26498         }else{
26499             return s.resizingEl.getHeight();
26500         }
26501     },
26502     
26503     /**
26504      * Called after drag operations to set the size of the resizing element.
26505      * @param {Roo.SplitBar} s The SplitBar using this adapter
26506      * @param {Number} newSize The new size to set
26507      * @param {Function} onComplete A function to be invoked when resizing is complete
26508      */
26509     setElementSize : function(s, newSize, onComplete){
26510         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26511             if(!s.animate){
26512                 s.resizingEl.setWidth(newSize);
26513                 if(onComplete){
26514                     onComplete(s, newSize);
26515                 }
26516             }else{
26517                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26518             }
26519         }else{
26520             
26521             if(!s.animate){
26522                 s.resizingEl.setHeight(newSize);
26523                 if(onComplete){
26524                     onComplete(s, newSize);
26525                 }
26526             }else{
26527                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26528             }
26529         }
26530     }
26531 };
26532
26533 /** 
26534  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26535  * @extends Roo.SplitBar.BasicLayoutAdapter
26536  * Adapter that  moves the splitter element to align with the resized sizing element. 
26537  * Used with an absolute positioned SplitBar.
26538  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26539  * document.body, make sure you assign an id to the body element.
26540  */
26541 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26542     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26543     this.container = Roo.get(container);
26544 };
26545
26546 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26547     init : function(s){
26548         this.basic.init(s);
26549     },
26550     
26551     getElementSize : function(s){
26552         return this.basic.getElementSize(s);
26553     },
26554     
26555     setElementSize : function(s, newSize, onComplete){
26556         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26557     },
26558     
26559     moveSplitter : function(s){
26560         var yes = Roo.SplitBar;
26561         switch(s.placement){
26562             case yes.LEFT:
26563                 s.el.setX(s.resizingEl.getRight());
26564                 break;
26565             case yes.RIGHT:
26566                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26567                 break;
26568             case yes.TOP:
26569                 s.el.setY(s.resizingEl.getBottom());
26570                 break;
26571             case yes.BOTTOM:
26572                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26573                 break;
26574         }
26575     }
26576 };
26577
26578 /**
26579  * Orientation constant - Create a vertical SplitBar
26580  * @static
26581  * @type Number
26582  */
26583 Roo.SplitBar.VERTICAL = 1;
26584
26585 /**
26586  * Orientation constant - Create a horizontal SplitBar
26587  * @static
26588  * @type Number
26589  */
26590 Roo.SplitBar.HORIZONTAL = 2;
26591
26592 /**
26593  * Placement constant - The resizing element is to the left of the splitter element
26594  * @static
26595  * @type Number
26596  */
26597 Roo.SplitBar.LEFT = 1;
26598
26599 /**
26600  * Placement constant - The resizing element is to the right of the splitter element
26601  * @static
26602  * @type Number
26603  */
26604 Roo.SplitBar.RIGHT = 2;
26605
26606 /**
26607  * Placement constant - The resizing element is positioned above the splitter element
26608  * @static
26609  * @type Number
26610  */
26611 Roo.SplitBar.TOP = 3;
26612
26613 /**
26614  * Placement constant - The resizing element is positioned under splitter element
26615  * @static
26616  * @type Number
26617  */
26618 Roo.SplitBar.BOTTOM = 4;
26619 /*
26620  * Based on:
26621  * Ext JS Library 1.1.1
26622  * Copyright(c) 2006-2007, Ext JS, LLC.
26623  *
26624  * Originally Released Under LGPL - original licence link has changed is not relivant.
26625  *
26626  * Fork - LGPL
26627  * <script type="text/javascript">
26628  */
26629
26630 /**
26631  * @class Roo.View
26632  * @extends Roo.util.Observable
26633  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26634  * This class also supports single and multi selection modes. <br>
26635  * Create a data model bound view:
26636  <pre><code>
26637  var store = new Roo.data.Store(...);
26638
26639  var view = new Roo.View({
26640     el : "my-element",
26641     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26642  
26643     singleSelect: true,
26644     selectedClass: "ydataview-selected",
26645     store: store
26646  });
26647
26648  // listen for node click?
26649  view.on("click", function(vw, index, node, e){
26650  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26651  });
26652
26653  // load XML data
26654  dataModel.load("foobar.xml");
26655  </code></pre>
26656  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26657  * <br><br>
26658  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26659  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26660  * 
26661  * Note: old style constructor is still suported (container, template, config)
26662  * 
26663  * @constructor
26664  * Create a new View
26665  * @param {Object} config The config object
26666  * 
26667  */
26668 Roo.View = function(config, depreciated_tpl, depreciated_config){
26669     
26670     this.parent = false;
26671     
26672     if (typeof(depreciated_tpl) == 'undefined') {
26673         // new way.. - universal constructor.
26674         Roo.apply(this, config);
26675         this.el  = Roo.get(this.el);
26676     } else {
26677         // old format..
26678         this.el  = Roo.get(config);
26679         this.tpl = depreciated_tpl;
26680         Roo.apply(this, depreciated_config);
26681     }
26682     this.wrapEl  = this.el.wrap().wrap();
26683     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26684     
26685     
26686     if(typeof(this.tpl) == "string"){
26687         this.tpl = new Roo.Template(this.tpl);
26688     } else {
26689         // support xtype ctors..
26690         this.tpl = new Roo.factory(this.tpl, Roo);
26691     }
26692     
26693     
26694     this.tpl.compile();
26695     
26696     /** @private */
26697     this.addEvents({
26698         /**
26699          * @event beforeclick
26700          * Fires before a click is processed. Returns false to cancel the default action.
26701          * @param {Roo.View} this
26702          * @param {Number} index The index of the target node
26703          * @param {HTMLElement} node The target node
26704          * @param {Roo.EventObject} e The raw event object
26705          */
26706             "beforeclick" : true,
26707         /**
26708          * @event click
26709          * Fires when a template node is clicked.
26710          * @param {Roo.View} this
26711          * @param {Number} index The index of the target node
26712          * @param {HTMLElement} node The target node
26713          * @param {Roo.EventObject} e The raw event object
26714          */
26715             "click" : true,
26716         /**
26717          * @event dblclick
26718          * Fires when a template node is double clicked.
26719          * @param {Roo.View} this
26720          * @param {Number} index The index of the target node
26721          * @param {HTMLElement} node The target node
26722          * @param {Roo.EventObject} e The raw event object
26723          */
26724             "dblclick" : true,
26725         /**
26726          * @event contextmenu
26727          * Fires when a template node is right clicked.
26728          * @param {Roo.View} this
26729          * @param {Number} index The index of the target node
26730          * @param {HTMLElement} node The target node
26731          * @param {Roo.EventObject} e The raw event object
26732          */
26733             "contextmenu" : true,
26734         /**
26735          * @event selectionchange
26736          * Fires when the selected nodes change.
26737          * @param {Roo.View} this
26738          * @param {Array} selections Array of the selected nodes
26739          */
26740             "selectionchange" : true,
26741     
26742         /**
26743          * @event beforeselect
26744          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26745          * @param {Roo.View} this
26746          * @param {HTMLElement} node The node to be selected
26747          * @param {Array} selections Array of currently selected nodes
26748          */
26749             "beforeselect" : true,
26750         /**
26751          * @event preparedata
26752          * Fires on every row to render, to allow you to change the data.
26753          * @param {Roo.View} this
26754          * @param {Object} data to be rendered (change this)
26755          */
26756           "preparedata" : true
26757           
26758           
26759         });
26760
26761
26762
26763     this.el.on({
26764         "click": this.onClick,
26765         "dblclick": this.onDblClick,
26766         "contextmenu": this.onContextMenu,
26767         scope:this
26768     });
26769
26770     this.selections = [];
26771     this.nodes = [];
26772     this.cmp = new Roo.CompositeElementLite([]);
26773     if(this.store){
26774         this.store = Roo.factory(this.store, Roo.data);
26775         this.setStore(this.store, true);
26776     }
26777     
26778     if ( this.footer && this.footer.xtype) {
26779            
26780          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26781         
26782         this.footer.dataSource = this.store;
26783         this.footer.container = fctr;
26784         this.footer = Roo.factory(this.footer, Roo);
26785         fctr.insertFirst(this.el);
26786         
26787         // this is a bit insane - as the paging toolbar seems to detach the el..
26788 //        dom.parentNode.parentNode.parentNode
26789          // they get detached?
26790     }
26791     
26792     
26793     Roo.View.superclass.constructor.call(this);
26794     
26795     
26796 };
26797
26798 Roo.extend(Roo.View, Roo.util.Observable, {
26799     
26800      /**
26801      * @cfg {Roo.data.Store} store Data store to load data from.
26802      */
26803     store : false,
26804     
26805     /**
26806      * @cfg {String|Roo.Element} el The container element.
26807      */
26808     el : '',
26809     
26810     /**
26811      * @cfg {String|Roo.Template} tpl The template used by this View 
26812      */
26813     tpl : false,
26814     /**
26815      * @cfg {String} dataName the named area of the template to use as the data area
26816      *                          Works with domtemplates roo-name="name"
26817      */
26818     dataName: false,
26819     /**
26820      * @cfg {String} selectedClass The css class to add to selected nodes
26821      */
26822     selectedClass : "x-view-selected",
26823      /**
26824      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26825      */
26826     emptyText : "",
26827     
26828     /**
26829      * @cfg {String} text to display on mask (default Loading)
26830      */
26831     mask : false,
26832     /**
26833      * @cfg {Boolean} multiSelect Allow multiple selection
26834      */
26835     multiSelect : false,
26836     /**
26837      * @cfg {Boolean} singleSelect Allow single selection
26838      */
26839     singleSelect:  false,
26840     
26841     /**
26842      * @cfg {Boolean} toggleSelect - selecting 
26843      */
26844     toggleSelect : false,
26845     
26846     /**
26847      * @cfg {Boolean} tickable - selecting 
26848      */
26849     tickable : false,
26850     
26851     /**
26852      * Returns the element this view is bound to.
26853      * @return {Roo.Element}
26854      */
26855     getEl : function(){
26856         return this.wrapEl;
26857     },
26858     
26859     
26860
26861     /**
26862      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26863      */
26864     refresh : function(){
26865         //Roo.log('refresh');
26866         var t = this.tpl;
26867         
26868         // if we are using something like 'domtemplate', then
26869         // the what gets used is:
26870         // t.applySubtemplate(NAME, data, wrapping data..)
26871         // the outer template then get' applied with
26872         //     the store 'extra data'
26873         // and the body get's added to the
26874         //      roo-name="data" node?
26875         //      <span class='roo-tpl-{name}'></span> ?????
26876         
26877         
26878         
26879         this.clearSelections();
26880         this.el.update("");
26881         var html = [];
26882         var records = this.store.getRange();
26883         if(records.length < 1) {
26884             
26885             // is this valid??  = should it render a template??
26886             
26887             this.el.update(this.emptyText);
26888             return;
26889         }
26890         var el = this.el;
26891         if (this.dataName) {
26892             this.el.update(t.apply(this.store.meta)); //????
26893             el = this.el.child('.roo-tpl-' + this.dataName);
26894         }
26895         
26896         for(var i = 0, len = records.length; i < len; i++){
26897             var data = this.prepareData(records[i].data, i, records[i]);
26898             this.fireEvent("preparedata", this, data, i, records[i]);
26899             
26900             var d = Roo.apply({}, data);
26901             
26902             if(this.tickable){
26903                 Roo.apply(d, {'roo-id' : Roo.id()});
26904                 
26905                 var _this = this;
26906             
26907                 Roo.each(this.parent.item, function(item){
26908                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26909                         return;
26910                     }
26911                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26912                 });
26913             }
26914             
26915             html[html.length] = Roo.util.Format.trim(
26916                 this.dataName ?
26917                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26918                     t.apply(d)
26919             );
26920         }
26921         
26922         
26923         
26924         el.update(html.join(""));
26925         this.nodes = el.dom.childNodes;
26926         this.updateIndexes(0);
26927     },
26928     
26929
26930     /**
26931      * Function to override to reformat the data that is sent to
26932      * the template for each node.
26933      * DEPRICATED - use the preparedata event handler.
26934      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26935      * a JSON object for an UpdateManager bound view).
26936      */
26937     prepareData : function(data, index, record)
26938     {
26939         this.fireEvent("preparedata", this, data, index, record);
26940         return data;
26941     },
26942
26943     onUpdate : function(ds, record){
26944         // Roo.log('on update');   
26945         this.clearSelections();
26946         var index = this.store.indexOf(record);
26947         var n = this.nodes[index];
26948         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26949         n.parentNode.removeChild(n);
26950         this.updateIndexes(index, index);
26951     },
26952
26953     
26954     
26955 // --------- FIXME     
26956     onAdd : function(ds, records, index)
26957     {
26958         //Roo.log(['on Add', ds, records, index] );        
26959         this.clearSelections();
26960         if(this.nodes.length == 0){
26961             this.refresh();
26962             return;
26963         }
26964         var n = this.nodes[index];
26965         for(var i = 0, len = records.length; i < len; i++){
26966             var d = this.prepareData(records[i].data, i, records[i]);
26967             if(n){
26968                 this.tpl.insertBefore(n, d);
26969             }else{
26970                 
26971                 this.tpl.append(this.el, d);
26972             }
26973         }
26974         this.updateIndexes(index);
26975     },
26976
26977     onRemove : function(ds, record, index){
26978        // Roo.log('onRemove');
26979         this.clearSelections();
26980         var el = this.dataName  ?
26981             this.el.child('.roo-tpl-' + this.dataName) :
26982             this.el; 
26983         
26984         el.dom.removeChild(this.nodes[index]);
26985         this.updateIndexes(index);
26986     },
26987
26988     /**
26989      * Refresh an individual node.
26990      * @param {Number} index
26991      */
26992     refreshNode : function(index){
26993         this.onUpdate(this.store, this.store.getAt(index));
26994     },
26995
26996     updateIndexes : function(startIndex, endIndex){
26997         var ns = this.nodes;
26998         startIndex = startIndex || 0;
26999         endIndex = endIndex || ns.length - 1;
27000         for(var i = startIndex; i <= endIndex; i++){
27001             ns[i].nodeIndex = i;
27002         }
27003     },
27004
27005     /**
27006      * Changes the data store this view uses and refresh the view.
27007      * @param {Store} store
27008      */
27009     setStore : function(store, initial){
27010         if(!initial && this.store){
27011             this.store.un("datachanged", this.refresh);
27012             this.store.un("add", this.onAdd);
27013             this.store.un("remove", this.onRemove);
27014             this.store.un("update", this.onUpdate);
27015             this.store.un("clear", this.refresh);
27016             this.store.un("beforeload", this.onBeforeLoad);
27017             this.store.un("load", this.onLoad);
27018             this.store.un("loadexception", this.onLoad);
27019         }
27020         if(store){
27021           
27022             store.on("datachanged", this.refresh, this);
27023             store.on("add", this.onAdd, this);
27024             store.on("remove", this.onRemove, this);
27025             store.on("update", this.onUpdate, this);
27026             store.on("clear", this.refresh, this);
27027             store.on("beforeload", this.onBeforeLoad, this);
27028             store.on("load", this.onLoad, this);
27029             store.on("loadexception", this.onLoad, this);
27030         }
27031         
27032         if(store){
27033             this.refresh();
27034         }
27035     },
27036     /**
27037      * onbeforeLoad - masks the loading area.
27038      *
27039      */
27040     onBeforeLoad : function(store,opts)
27041     {
27042          //Roo.log('onBeforeLoad');   
27043         if (!opts.add) {
27044             this.el.update("");
27045         }
27046         this.el.mask(this.mask ? this.mask : "Loading" ); 
27047     },
27048     onLoad : function ()
27049     {
27050         this.el.unmask();
27051     },
27052     
27053
27054     /**
27055      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27056      * @param {HTMLElement} node
27057      * @return {HTMLElement} The template node
27058      */
27059     findItemFromChild : function(node){
27060         var el = this.dataName  ?
27061             this.el.child('.roo-tpl-' + this.dataName,true) :
27062             this.el.dom; 
27063         
27064         if(!node || node.parentNode == el){
27065                     return node;
27066             }
27067             var p = node.parentNode;
27068             while(p && p != el){
27069             if(p.parentNode == el){
27070                 return p;
27071             }
27072             p = p.parentNode;
27073         }
27074             return null;
27075     },
27076
27077     /** @ignore */
27078     onClick : function(e){
27079         var item = this.findItemFromChild(e.getTarget());
27080         if(item){
27081             var index = this.indexOf(item);
27082             if(this.onItemClick(item, index, e) !== false){
27083                 this.fireEvent("click", this, index, item, e);
27084             }
27085         }else{
27086             this.clearSelections();
27087         }
27088     },
27089
27090     /** @ignore */
27091     onContextMenu : function(e){
27092         var item = this.findItemFromChild(e.getTarget());
27093         if(item){
27094             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27095         }
27096     },
27097
27098     /** @ignore */
27099     onDblClick : function(e){
27100         var item = this.findItemFromChild(e.getTarget());
27101         if(item){
27102             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27103         }
27104     },
27105
27106     onItemClick : function(item, index, e)
27107     {
27108         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27109             return false;
27110         }
27111         if (this.toggleSelect) {
27112             var m = this.isSelected(item) ? 'unselect' : 'select';
27113             //Roo.log(m);
27114             var _t = this;
27115             _t[m](item, true, false);
27116             return true;
27117         }
27118         if(this.multiSelect || this.singleSelect){
27119             if(this.multiSelect && e.shiftKey && this.lastSelection){
27120                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27121             }else{
27122                 this.select(item, this.multiSelect && e.ctrlKey);
27123                 this.lastSelection = item;
27124             }
27125             
27126             if(!this.tickable){
27127                 e.preventDefault();
27128             }
27129             
27130         }
27131         return true;
27132     },
27133
27134     /**
27135      * Get the number of selected nodes.
27136      * @return {Number}
27137      */
27138     getSelectionCount : function(){
27139         return this.selections.length;
27140     },
27141
27142     /**
27143      * Get the currently selected nodes.
27144      * @return {Array} An array of HTMLElements
27145      */
27146     getSelectedNodes : function(){
27147         return this.selections;
27148     },
27149
27150     /**
27151      * Get the indexes of the selected nodes.
27152      * @return {Array}
27153      */
27154     getSelectedIndexes : function(){
27155         var indexes = [], s = this.selections;
27156         for(var i = 0, len = s.length; i < len; i++){
27157             indexes.push(s[i].nodeIndex);
27158         }
27159         return indexes;
27160     },
27161
27162     /**
27163      * Clear all selections
27164      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27165      */
27166     clearSelections : function(suppressEvent){
27167         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27168             this.cmp.elements = this.selections;
27169             this.cmp.removeClass(this.selectedClass);
27170             this.selections = [];
27171             if(!suppressEvent){
27172                 this.fireEvent("selectionchange", this, this.selections);
27173             }
27174         }
27175     },
27176
27177     /**
27178      * Returns true if the passed node is selected
27179      * @param {HTMLElement/Number} node The node or node index
27180      * @return {Boolean}
27181      */
27182     isSelected : function(node){
27183         var s = this.selections;
27184         if(s.length < 1){
27185             return false;
27186         }
27187         node = this.getNode(node);
27188         return s.indexOf(node) !== -1;
27189     },
27190
27191     /**
27192      * Selects nodes.
27193      * @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
27194      * @param {Boolean} keepExisting (optional) true to keep existing selections
27195      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27196      */
27197     select : function(nodeInfo, keepExisting, suppressEvent){
27198         if(nodeInfo instanceof Array){
27199             if(!keepExisting){
27200                 this.clearSelections(true);
27201             }
27202             for(var i = 0, len = nodeInfo.length; i < len; i++){
27203                 this.select(nodeInfo[i], true, true);
27204             }
27205             return;
27206         } 
27207         var node = this.getNode(nodeInfo);
27208         if(!node || this.isSelected(node)){
27209             return; // already selected.
27210         }
27211         if(!keepExisting){
27212             this.clearSelections(true);
27213         }
27214         
27215         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27216             Roo.fly(node).addClass(this.selectedClass);
27217             this.selections.push(node);
27218             if(!suppressEvent){
27219                 this.fireEvent("selectionchange", this, this.selections);
27220             }
27221         }
27222         
27223         
27224     },
27225       /**
27226      * Unselects nodes.
27227      * @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
27228      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27229      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27230      */
27231     unselect : function(nodeInfo, keepExisting, suppressEvent)
27232     {
27233         if(nodeInfo instanceof Array){
27234             Roo.each(this.selections, function(s) {
27235                 this.unselect(s, nodeInfo);
27236             }, this);
27237             return;
27238         }
27239         var node = this.getNode(nodeInfo);
27240         if(!node || !this.isSelected(node)){
27241             //Roo.log("not selected");
27242             return; // not selected.
27243         }
27244         // fireevent???
27245         var ns = [];
27246         Roo.each(this.selections, function(s) {
27247             if (s == node ) {
27248                 Roo.fly(node).removeClass(this.selectedClass);
27249
27250                 return;
27251             }
27252             ns.push(s);
27253         },this);
27254         
27255         this.selections= ns;
27256         this.fireEvent("selectionchange", this, this.selections);
27257     },
27258
27259     /**
27260      * Gets a template node.
27261      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27262      * @return {HTMLElement} The node or null if it wasn't found
27263      */
27264     getNode : function(nodeInfo){
27265         if(typeof nodeInfo == "string"){
27266             return document.getElementById(nodeInfo);
27267         }else if(typeof nodeInfo == "number"){
27268             return this.nodes[nodeInfo];
27269         }
27270         return nodeInfo;
27271     },
27272
27273     /**
27274      * Gets a range template nodes.
27275      * @param {Number} startIndex
27276      * @param {Number} endIndex
27277      * @return {Array} An array of nodes
27278      */
27279     getNodes : function(start, end){
27280         var ns = this.nodes;
27281         start = start || 0;
27282         end = typeof end == "undefined" ? ns.length - 1 : end;
27283         var nodes = [];
27284         if(start <= end){
27285             for(var i = start; i <= end; i++){
27286                 nodes.push(ns[i]);
27287             }
27288         } else{
27289             for(var i = start; i >= end; i--){
27290                 nodes.push(ns[i]);
27291             }
27292         }
27293         return nodes;
27294     },
27295
27296     /**
27297      * Finds the index of the passed node
27298      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27299      * @return {Number} The index of the node or -1
27300      */
27301     indexOf : function(node){
27302         node = this.getNode(node);
27303         if(typeof node.nodeIndex == "number"){
27304             return node.nodeIndex;
27305         }
27306         var ns = this.nodes;
27307         for(var i = 0, len = ns.length; i < len; i++){
27308             if(ns[i] == node){
27309                 return i;
27310             }
27311         }
27312         return -1;
27313     }
27314 });
27315 /*
27316  * Based on:
27317  * Ext JS Library 1.1.1
27318  * Copyright(c) 2006-2007, Ext JS, LLC.
27319  *
27320  * Originally Released Under LGPL - original licence link has changed is not relivant.
27321  *
27322  * Fork - LGPL
27323  * <script type="text/javascript">
27324  */
27325
27326 /**
27327  * @class Roo.JsonView
27328  * @extends Roo.View
27329  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27330 <pre><code>
27331 var view = new Roo.JsonView({
27332     container: "my-element",
27333     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27334     multiSelect: true, 
27335     jsonRoot: "data" 
27336 });
27337
27338 // listen for node click?
27339 view.on("click", function(vw, index, node, e){
27340     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27341 });
27342
27343 // direct load of JSON data
27344 view.load("foobar.php");
27345
27346 // Example from my blog list
27347 var tpl = new Roo.Template(
27348     '&lt;div class="entry"&gt;' +
27349     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27350     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27351     "&lt;/div&gt;&lt;hr /&gt;"
27352 );
27353
27354 var moreView = new Roo.JsonView({
27355     container :  "entry-list", 
27356     template : tpl,
27357     jsonRoot: "posts"
27358 });
27359 moreView.on("beforerender", this.sortEntries, this);
27360 moreView.load({
27361     url: "/blog/get-posts.php",
27362     params: "allposts=true",
27363     text: "Loading Blog Entries..."
27364 });
27365 </code></pre>
27366
27367 * Note: old code is supported with arguments : (container, template, config)
27368
27369
27370  * @constructor
27371  * Create a new JsonView
27372  * 
27373  * @param {Object} config The config object
27374  * 
27375  */
27376 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27377     
27378     
27379     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27380
27381     var um = this.el.getUpdateManager();
27382     um.setRenderer(this);
27383     um.on("update", this.onLoad, this);
27384     um.on("failure", this.onLoadException, this);
27385
27386     /**
27387      * @event beforerender
27388      * Fires before rendering of the downloaded JSON data.
27389      * @param {Roo.JsonView} this
27390      * @param {Object} data The JSON data loaded
27391      */
27392     /**
27393      * @event load
27394      * Fires when data is loaded.
27395      * @param {Roo.JsonView} this
27396      * @param {Object} data The JSON data loaded
27397      * @param {Object} response The raw Connect response object
27398      */
27399     /**
27400      * @event loadexception
27401      * Fires when loading fails.
27402      * @param {Roo.JsonView} this
27403      * @param {Object} response The raw Connect response object
27404      */
27405     this.addEvents({
27406         'beforerender' : true,
27407         'load' : true,
27408         'loadexception' : true
27409     });
27410 };
27411 Roo.extend(Roo.JsonView, Roo.View, {
27412     /**
27413      * @type {String} The root property in the loaded JSON object that contains the data
27414      */
27415     jsonRoot : "",
27416
27417     /**
27418      * Refreshes the view.
27419      */
27420     refresh : function(){
27421         this.clearSelections();
27422         this.el.update("");
27423         var html = [];
27424         var o = this.jsonData;
27425         if(o && o.length > 0){
27426             for(var i = 0, len = o.length; i < len; i++){
27427                 var data = this.prepareData(o[i], i, o);
27428                 html[html.length] = this.tpl.apply(data);
27429             }
27430         }else{
27431             html.push(this.emptyText);
27432         }
27433         this.el.update(html.join(""));
27434         this.nodes = this.el.dom.childNodes;
27435         this.updateIndexes(0);
27436     },
27437
27438     /**
27439      * 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.
27440      * @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:
27441      <pre><code>
27442      view.load({
27443          url: "your-url.php",
27444          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27445          callback: yourFunction,
27446          scope: yourObject, //(optional scope)
27447          discardUrl: false,
27448          nocache: false,
27449          text: "Loading...",
27450          timeout: 30,
27451          scripts: false
27452      });
27453      </code></pre>
27454      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27455      * 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.
27456      * @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}
27457      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27458      * @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.
27459      */
27460     load : function(){
27461         var um = this.el.getUpdateManager();
27462         um.update.apply(um, arguments);
27463     },
27464
27465     // note - render is a standard framework call...
27466     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27467     render : function(el, response){
27468         
27469         this.clearSelections();
27470         this.el.update("");
27471         var o;
27472         try{
27473             if (response != '') {
27474                 o = Roo.util.JSON.decode(response.responseText);
27475                 if(this.jsonRoot){
27476                     
27477                     o = o[this.jsonRoot];
27478                 }
27479             }
27480         } catch(e){
27481         }
27482         /**
27483          * The current JSON data or null
27484          */
27485         this.jsonData = o;
27486         this.beforeRender();
27487         this.refresh();
27488     },
27489
27490 /**
27491  * Get the number of records in the current JSON dataset
27492  * @return {Number}
27493  */
27494     getCount : function(){
27495         return this.jsonData ? this.jsonData.length : 0;
27496     },
27497
27498 /**
27499  * Returns the JSON object for the specified node(s)
27500  * @param {HTMLElement/Array} node The node or an array of nodes
27501  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27502  * you get the JSON object for the node
27503  */
27504     getNodeData : function(node){
27505         if(node instanceof Array){
27506             var data = [];
27507             for(var i = 0, len = node.length; i < len; i++){
27508                 data.push(this.getNodeData(node[i]));
27509             }
27510             return data;
27511         }
27512         return this.jsonData[this.indexOf(node)] || null;
27513     },
27514
27515     beforeRender : function(){
27516         this.snapshot = this.jsonData;
27517         if(this.sortInfo){
27518             this.sort.apply(this, this.sortInfo);
27519         }
27520         this.fireEvent("beforerender", this, this.jsonData);
27521     },
27522
27523     onLoad : function(el, o){
27524         this.fireEvent("load", this, this.jsonData, o);
27525     },
27526
27527     onLoadException : function(el, o){
27528         this.fireEvent("loadexception", this, o);
27529     },
27530
27531 /**
27532  * Filter the data by a specific property.
27533  * @param {String} property A property on your JSON objects
27534  * @param {String/RegExp} value Either string that the property values
27535  * should start with, or a RegExp to test against the property
27536  */
27537     filter : function(property, value){
27538         if(this.jsonData){
27539             var data = [];
27540             var ss = this.snapshot;
27541             if(typeof value == "string"){
27542                 var vlen = value.length;
27543                 if(vlen == 0){
27544                     this.clearFilter();
27545                     return;
27546                 }
27547                 value = value.toLowerCase();
27548                 for(var i = 0, len = ss.length; i < len; i++){
27549                     var o = ss[i];
27550                     if(o[property].substr(0, vlen).toLowerCase() == value){
27551                         data.push(o);
27552                     }
27553                 }
27554             } else if(value.exec){ // regex?
27555                 for(var i = 0, len = ss.length; i < len; i++){
27556                     var o = ss[i];
27557                     if(value.test(o[property])){
27558                         data.push(o);
27559                     }
27560                 }
27561             } else{
27562                 return;
27563             }
27564             this.jsonData = data;
27565             this.refresh();
27566         }
27567     },
27568
27569 /**
27570  * Filter by a function. The passed function will be called with each
27571  * object in the current dataset. If the function returns true the value is kept,
27572  * otherwise it is filtered.
27573  * @param {Function} fn
27574  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27575  */
27576     filterBy : function(fn, scope){
27577         if(this.jsonData){
27578             var data = [];
27579             var ss = this.snapshot;
27580             for(var i = 0, len = ss.length; i < len; i++){
27581                 var o = ss[i];
27582                 if(fn.call(scope || this, o)){
27583                     data.push(o);
27584                 }
27585             }
27586             this.jsonData = data;
27587             this.refresh();
27588         }
27589     },
27590
27591 /**
27592  * Clears the current filter.
27593  */
27594     clearFilter : function(){
27595         if(this.snapshot && this.jsonData != this.snapshot){
27596             this.jsonData = this.snapshot;
27597             this.refresh();
27598         }
27599     },
27600
27601
27602 /**
27603  * Sorts the data for this view and refreshes it.
27604  * @param {String} property A property on your JSON objects to sort on
27605  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27606  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27607  */
27608     sort : function(property, dir, sortType){
27609         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27610         if(this.jsonData){
27611             var p = property;
27612             var dsc = dir && dir.toLowerCase() == "desc";
27613             var f = function(o1, o2){
27614                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27615                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27616                 ;
27617                 if(v1 < v2){
27618                     return dsc ? +1 : -1;
27619                 } else if(v1 > v2){
27620                     return dsc ? -1 : +1;
27621                 } else{
27622                     return 0;
27623                 }
27624             };
27625             this.jsonData.sort(f);
27626             this.refresh();
27627             if(this.jsonData != this.snapshot){
27628                 this.snapshot.sort(f);
27629             }
27630         }
27631     }
27632 });/*
27633  * Based on:
27634  * Ext JS Library 1.1.1
27635  * Copyright(c) 2006-2007, Ext JS, LLC.
27636  *
27637  * Originally Released Under LGPL - original licence link has changed is not relivant.
27638  *
27639  * Fork - LGPL
27640  * <script type="text/javascript">
27641  */
27642  
27643
27644 /**
27645  * @class Roo.ColorPalette
27646  * @extends Roo.Component
27647  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27648  * Here's an example of typical usage:
27649  * <pre><code>
27650 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27651 cp.render('my-div');
27652
27653 cp.on('select', function(palette, selColor){
27654     // do something with selColor
27655 });
27656 </code></pre>
27657  * @constructor
27658  * Create a new ColorPalette
27659  * @param {Object} config The config object
27660  */
27661 Roo.ColorPalette = function(config){
27662     Roo.ColorPalette.superclass.constructor.call(this, config);
27663     this.addEvents({
27664         /**
27665              * @event select
27666              * Fires when a color is selected
27667              * @param {ColorPalette} this
27668              * @param {String} color The 6-digit color hex code (without the # symbol)
27669              */
27670         select: true
27671     });
27672
27673     if(this.handler){
27674         this.on("select", this.handler, this.scope, true);
27675     }
27676 };
27677 Roo.extend(Roo.ColorPalette, Roo.Component, {
27678     /**
27679      * @cfg {String} itemCls
27680      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27681      */
27682     itemCls : "x-color-palette",
27683     /**
27684      * @cfg {String} value
27685      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27686      * the hex codes are case-sensitive.
27687      */
27688     value : null,
27689     clickEvent:'click',
27690     // private
27691     ctype: "Roo.ColorPalette",
27692
27693     /**
27694      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27695      */
27696     allowReselect : false,
27697
27698     /**
27699      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27700      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27701      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27702      * of colors with the width setting until the box is symmetrical.</p>
27703      * <p>You can override individual colors if needed:</p>
27704      * <pre><code>
27705 var cp = new Roo.ColorPalette();
27706 cp.colors[0] = "FF0000";  // change the first box to red
27707 </code></pre>
27708
27709 Or you can provide a custom array of your own for complete control:
27710 <pre><code>
27711 var cp = new Roo.ColorPalette();
27712 cp.colors = ["000000", "993300", "333300"];
27713 </code></pre>
27714      * @type Array
27715      */
27716     colors : [
27717         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27718         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27719         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27720         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27721         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27722     ],
27723
27724     // private
27725     onRender : function(container, position){
27726         var t = new Roo.MasterTemplate(
27727             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27728         );
27729         var c = this.colors;
27730         for(var i = 0, len = c.length; i < len; i++){
27731             t.add([c[i]]);
27732         }
27733         var el = document.createElement("div");
27734         el.className = this.itemCls;
27735         t.overwrite(el);
27736         container.dom.insertBefore(el, position);
27737         this.el = Roo.get(el);
27738         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27739         if(this.clickEvent != 'click'){
27740             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27741         }
27742     },
27743
27744     // private
27745     afterRender : function(){
27746         Roo.ColorPalette.superclass.afterRender.call(this);
27747         if(this.value){
27748             var s = this.value;
27749             this.value = null;
27750             this.select(s);
27751         }
27752     },
27753
27754     // private
27755     handleClick : function(e, t){
27756         e.preventDefault();
27757         if(!this.disabled){
27758             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27759             this.select(c.toUpperCase());
27760         }
27761     },
27762
27763     /**
27764      * Selects the specified color in the palette (fires the select event)
27765      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27766      */
27767     select : function(color){
27768         color = color.replace("#", "");
27769         if(color != this.value || this.allowReselect){
27770             var el = this.el;
27771             if(this.value){
27772                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27773             }
27774             el.child("a.color-"+color).addClass("x-color-palette-sel");
27775             this.value = color;
27776             this.fireEvent("select", this, color);
27777         }
27778     }
27779 });/*
27780  * Based on:
27781  * Ext JS Library 1.1.1
27782  * Copyright(c) 2006-2007, Ext JS, LLC.
27783  *
27784  * Originally Released Under LGPL - original licence link has changed is not relivant.
27785  *
27786  * Fork - LGPL
27787  * <script type="text/javascript">
27788  */
27789  
27790 /**
27791  * @class Roo.DatePicker
27792  * @extends Roo.Component
27793  * Simple date picker class.
27794  * @constructor
27795  * Create a new DatePicker
27796  * @param {Object} config The config object
27797  */
27798 Roo.DatePicker = function(config){
27799     Roo.DatePicker.superclass.constructor.call(this, config);
27800
27801     this.value = config && config.value ?
27802                  config.value.clearTime() : new Date().clearTime();
27803
27804     this.addEvents({
27805         /**
27806              * @event select
27807              * Fires when a date is selected
27808              * @param {DatePicker} this
27809              * @param {Date} date The selected date
27810              */
27811         'select': true,
27812         /**
27813              * @event monthchange
27814              * Fires when the displayed month changes 
27815              * @param {DatePicker} this
27816              * @param {Date} date The selected month
27817              */
27818         'monthchange': true
27819     });
27820
27821     if(this.handler){
27822         this.on("select", this.handler,  this.scope || this);
27823     }
27824     // build the disabledDatesRE
27825     if(!this.disabledDatesRE && this.disabledDates){
27826         var dd = this.disabledDates;
27827         var re = "(?:";
27828         for(var i = 0; i < dd.length; i++){
27829             re += dd[i];
27830             if(i != dd.length-1) {
27831                 re += "|";
27832             }
27833         }
27834         this.disabledDatesRE = new RegExp(re + ")");
27835     }
27836 };
27837
27838 Roo.extend(Roo.DatePicker, Roo.Component, {
27839     /**
27840      * @cfg {String} todayText
27841      * The text to display on the button that selects the current date (defaults to "Today")
27842      */
27843     todayText : "Today",
27844     /**
27845      * @cfg {String} okText
27846      * The text to display on the ok button
27847      */
27848     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27849     /**
27850      * @cfg {String} cancelText
27851      * The text to display on the cancel button
27852      */
27853     cancelText : "Cancel",
27854     /**
27855      * @cfg {String} todayTip
27856      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27857      */
27858     todayTip : "{0} (Spacebar)",
27859     /**
27860      * @cfg {Date} minDate
27861      * Minimum allowable date (JavaScript date object, defaults to null)
27862      */
27863     minDate : null,
27864     /**
27865      * @cfg {Date} maxDate
27866      * Maximum allowable date (JavaScript date object, defaults to null)
27867      */
27868     maxDate : null,
27869     /**
27870      * @cfg {String} minText
27871      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27872      */
27873     minText : "This date is before the minimum date",
27874     /**
27875      * @cfg {String} maxText
27876      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27877      */
27878     maxText : "This date is after the maximum date",
27879     /**
27880      * @cfg {String} format
27881      * The default date format string which can be overriden for localization support.  The format must be
27882      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27883      */
27884     format : "m/d/y",
27885     /**
27886      * @cfg {Array} disabledDays
27887      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27888      */
27889     disabledDays : null,
27890     /**
27891      * @cfg {String} disabledDaysText
27892      * The tooltip to display when the date falls on a disabled day (defaults to "")
27893      */
27894     disabledDaysText : "",
27895     /**
27896      * @cfg {RegExp} disabledDatesRE
27897      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27898      */
27899     disabledDatesRE : null,
27900     /**
27901      * @cfg {String} disabledDatesText
27902      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27903      */
27904     disabledDatesText : "",
27905     /**
27906      * @cfg {Boolean} constrainToViewport
27907      * True to constrain the date picker to the viewport (defaults to true)
27908      */
27909     constrainToViewport : true,
27910     /**
27911      * @cfg {Array} monthNames
27912      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27913      */
27914     monthNames : Date.monthNames,
27915     /**
27916      * @cfg {Array} dayNames
27917      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27918      */
27919     dayNames : Date.dayNames,
27920     /**
27921      * @cfg {String} nextText
27922      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27923      */
27924     nextText: 'Next Month (Control+Right)',
27925     /**
27926      * @cfg {String} prevText
27927      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27928      */
27929     prevText: 'Previous Month (Control+Left)',
27930     /**
27931      * @cfg {String} monthYearText
27932      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27933      */
27934     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27935     /**
27936      * @cfg {Number} startDay
27937      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27938      */
27939     startDay : 0,
27940     /**
27941      * @cfg {Bool} showClear
27942      * Show a clear button (usefull for date form elements that can be blank.)
27943      */
27944     
27945     showClear: false,
27946     
27947     /**
27948      * Sets the value of the date field
27949      * @param {Date} value The date to set
27950      */
27951     setValue : function(value){
27952         var old = this.value;
27953         
27954         if (typeof(value) == 'string') {
27955          
27956             value = Date.parseDate(value, this.format);
27957         }
27958         if (!value) {
27959             value = new Date();
27960         }
27961         
27962         this.value = value.clearTime(true);
27963         if(this.el){
27964             this.update(this.value);
27965         }
27966     },
27967
27968     /**
27969      * Gets the current selected value of the date field
27970      * @return {Date} The selected date
27971      */
27972     getValue : function(){
27973         return this.value;
27974     },
27975
27976     // private
27977     focus : function(){
27978         if(this.el){
27979             this.update(this.activeDate);
27980         }
27981     },
27982
27983     // privateval
27984     onRender : function(container, position){
27985         
27986         var m = [
27987              '<table cellspacing="0">',
27988                 '<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>',
27989                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27990         var dn = this.dayNames;
27991         for(var i = 0; i < 7; i++){
27992             var d = this.startDay+i;
27993             if(d > 6){
27994                 d = d-7;
27995             }
27996             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27997         }
27998         m[m.length] = "</tr></thead><tbody><tr>";
27999         for(var i = 0; i < 42; i++) {
28000             if(i % 7 == 0 && i != 0){
28001                 m[m.length] = "</tr><tr>";
28002             }
28003             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28004         }
28005         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28006             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28007
28008         var el = document.createElement("div");
28009         el.className = "x-date-picker";
28010         el.innerHTML = m.join("");
28011
28012         container.dom.insertBefore(el, position);
28013
28014         this.el = Roo.get(el);
28015         this.eventEl = Roo.get(el.firstChild);
28016
28017         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28018             handler: this.showPrevMonth,
28019             scope: this,
28020             preventDefault:true,
28021             stopDefault:true
28022         });
28023
28024         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28025             handler: this.showNextMonth,
28026             scope: this,
28027             preventDefault:true,
28028             stopDefault:true
28029         });
28030
28031         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28032
28033         this.monthPicker = this.el.down('div.x-date-mp');
28034         this.monthPicker.enableDisplayMode('block');
28035         
28036         var kn = new Roo.KeyNav(this.eventEl, {
28037             "left" : function(e){
28038                 e.ctrlKey ?
28039                     this.showPrevMonth() :
28040                     this.update(this.activeDate.add("d", -1));
28041             },
28042
28043             "right" : function(e){
28044                 e.ctrlKey ?
28045                     this.showNextMonth() :
28046                     this.update(this.activeDate.add("d", 1));
28047             },
28048
28049             "up" : function(e){
28050                 e.ctrlKey ?
28051                     this.showNextYear() :
28052                     this.update(this.activeDate.add("d", -7));
28053             },
28054
28055             "down" : function(e){
28056                 e.ctrlKey ?
28057                     this.showPrevYear() :
28058                     this.update(this.activeDate.add("d", 7));
28059             },
28060
28061             "pageUp" : function(e){
28062                 this.showNextMonth();
28063             },
28064
28065             "pageDown" : function(e){
28066                 this.showPrevMonth();
28067             },
28068
28069             "enter" : function(e){
28070                 e.stopPropagation();
28071                 return true;
28072             },
28073
28074             scope : this
28075         });
28076
28077         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28078
28079         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28080
28081         this.el.unselectable();
28082         
28083         this.cells = this.el.select("table.x-date-inner tbody td");
28084         this.textNodes = this.el.query("table.x-date-inner tbody span");
28085
28086         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28087             text: "&#160;",
28088             tooltip: this.monthYearText
28089         });
28090
28091         this.mbtn.on('click', this.showMonthPicker, this);
28092         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28093
28094
28095         var today = (new Date()).dateFormat(this.format);
28096         
28097         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28098         if (this.showClear) {
28099             baseTb.add( new Roo.Toolbar.Fill());
28100         }
28101         baseTb.add({
28102             text: String.format(this.todayText, today),
28103             tooltip: String.format(this.todayTip, today),
28104             handler: this.selectToday,
28105             scope: this
28106         });
28107         
28108         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28109             
28110         //});
28111         if (this.showClear) {
28112             
28113             baseTb.add( new Roo.Toolbar.Fill());
28114             baseTb.add({
28115                 text: '&#160;',
28116                 cls: 'x-btn-icon x-btn-clear',
28117                 handler: function() {
28118                     //this.value = '';
28119                     this.fireEvent("select", this, '');
28120                 },
28121                 scope: this
28122             });
28123         }
28124         
28125         
28126         if(Roo.isIE){
28127             this.el.repaint();
28128         }
28129         this.update(this.value);
28130     },
28131
28132     createMonthPicker : function(){
28133         if(!this.monthPicker.dom.firstChild){
28134             var buf = ['<table border="0" cellspacing="0">'];
28135             for(var i = 0; i < 6; i++){
28136                 buf.push(
28137                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28138                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28139                     i == 0 ?
28140                     '<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>' :
28141                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28142                 );
28143             }
28144             buf.push(
28145                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28146                     this.okText,
28147                     '</button><button type="button" class="x-date-mp-cancel">',
28148                     this.cancelText,
28149                     '</button></td></tr>',
28150                 '</table>'
28151             );
28152             this.monthPicker.update(buf.join(''));
28153             this.monthPicker.on('click', this.onMonthClick, this);
28154             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28155
28156             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28157             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28158
28159             this.mpMonths.each(function(m, a, i){
28160                 i += 1;
28161                 if((i%2) == 0){
28162                     m.dom.xmonth = 5 + Math.round(i * .5);
28163                 }else{
28164                     m.dom.xmonth = Math.round((i-1) * .5);
28165                 }
28166             });
28167         }
28168     },
28169
28170     showMonthPicker : function(){
28171         this.createMonthPicker();
28172         var size = this.el.getSize();
28173         this.monthPicker.setSize(size);
28174         this.monthPicker.child('table').setSize(size);
28175
28176         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28177         this.updateMPMonth(this.mpSelMonth);
28178         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28179         this.updateMPYear(this.mpSelYear);
28180
28181         this.monthPicker.slideIn('t', {duration:.2});
28182     },
28183
28184     updateMPYear : function(y){
28185         this.mpyear = y;
28186         var ys = this.mpYears.elements;
28187         for(var i = 1; i <= 10; i++){
28188             var td = ys[i-1], y2;
28189             if((i%2) == 0){
28190                 y2 = y + Math.round(i * .5);
28191                 td.firstChild.innerHTML = y2;
28192                 td.xyear = y2;
28193             }else{
28194                 y2 = y - (5-Math.round(i * .5));
28195                 td.firstChild.innerHTML = y2;
28196                 td.xyear = y2;
28197             }
28198             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28199         }
28200     },
28201
28202     updateMPMonth : function(sm){
28203         this.mpMonths.each(function(m, a, i){
28204             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28205         });
28206     },
28207
28208     selectMPMonth: function(m){
28209         
28210     },
28211
28212     onMonthClick : function(e, t){
28213         e.stopEvent();
28214         var el = new Roo.Element(t), pn;
28215         if(el.is('button.x-date-mp-cancel')){
28216             this.hideMonthPicker();
28217         }
28218         else if(el.is('button.x-date-mp-ok')){
28219             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28220             this.hideMonthPicker();
28221         }
28222         else if(pn = el.up('td.x-date-mp-month', 2)){
28223             this.mpMonths.removeClass('x-date-mp-sel');
28224             pn.addClass('x-date-mp-sel');
28225             this.mpSelMonth = pn.dom.xmonth;
28226         }
28227         else if(pn = el.up('td.x-date-mp-year', 2)){
28228             this.mpYears.removeClass('x-date-mp-sel');
28229             pn.addClass('x-date-mp-sel');
28230             this.mpSelYear = pn.dom.xyear;
28231         }
28232         else if(el.is('a.x-date-mp-prev')){
28233             this.updateMPYear(this.mpyear-10);
28234         }
28235         else if(el.is('a.x-date-mp-next')){
28236             this.updateMPYear(this.mpyear+10);
28237         }
28238     },
28239
28240     onMonthDblClick : function(e, t){
28241         e.stopEvent();
28242         var el = new Roo.Element(t), pn;
28243         if(pn = el.up('td.x-date-mp-month', 2)){
28244             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28245             this.hideMonthPicker();
28246         }
28247         else if(pn = el.up('td.x-date-mp-year', 2)){
28248             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28249             this.hideMonthPicker();
28250         }
28251     },
28252
28253     hideMonthPicker : function(disableAnim){
28254         if(this.monthPicker){
28255             if(disableAnim === true){
28256                 this.monthPicker.hide();
28257             }else{
28258                 this.monthPicker.slideOut('t', {duration:.2});
28259             }
28260         }
28261     },
28262
28263     // private
28264     showPrevMonth : function(e){
28265         this.update(this.activeDate.add("mo", -1));
28266     },
28267
28268     // private
28269     showNextMonth : function(e){
28270         this.update(this.activeDate.add("mo", 1));
28271     },
28272
28273     // private
28274     showPrevYear : function(){
28275         this.update(this.activeDate.add("y", -1));
28276     },
28277
28278     // private
28279     showNextYear : function(){
28280         this.update(this.activeDate.add("y", 1));
28281     },
28282
28283     // private
28284     handleMouseWheel : function(e){
28285         var delta = e.getWheelDelta();
28286         if(delta > 0){
28287             this.showPrevMonth();
28288             e.stopEvent();
28289         } else if(delta < 0){
28290             this.showNextMonth();
28291             e.stopEvent();
28292         }
28293     },
28294
28295     // private
28296     handleDateClick : function(e, t){
28297         e.stopEvent();
28298         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28299             this.setValue(new Date(t.dateValue));
28300             this.fireEvent("select", this, this.value);
28301         }
28302     },
28303
28304     // private
28305     selectToday : function(){
28306         this.setValue(new Date().clearTime());
28307         this.fireEvent("select", this, this.value);
28308     },
28309
28310     // private
28311     update : function(date)
28312     {
28313         var vd = this.activeDate;
28314         this.activeDate = date;
28315         if(vd && this.el){
28316             var t = date.getTime();
28317             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28318                 this.cells.removeClass("x-date-selected");
28319                 this.cells.each(function(c){
28320                    if(c.dom.firstChild.dateValue == t){
28321                        c.addClass("x-date-selected");
28322                        setTimeout(function(){
28323                             try{c.dom.firstChild.focus();}catch(e){}
28324                        }, 50);
28325                        return false;
28326                    }
28327                 });
28328                 return;
28329             }
28330         }
28331         
28332         var days = date.getDaysInMonth();
28333         var firstOfMonth = date.getFirstDateOfMonth();
28334         var startingPos = firstOfMonth.getDay()-this.startDay;
28335
28336         if(startingPos <= this.startDay){
28337             startingPos += 7;
28338         }
28339
28340         var pm = date.add("mo", -1);
28341         var prevStart = pm.getDaysInMonth()-startingPos;
28342
28343         var cells = this.cells.elements;
28344         var textEls = this.textNodes;
28345         days += startingPos;
28346
28347         // convert everything to numbers so it's fast
28348         var day = 86400000;
28349         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28350         var today = new Date().clearTime().getTime();
28351         var sel = date.clearTime().getTime();
28352         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28353         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28354         var ddMatch = this.disabledDatesRE;
28355         var ddText = this.disabledDatesText;
28356         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28357         var ddaysText = this.disabledDaysText;
28358         var format = this.format;
28359
28360         var setCellClass = function(cal, cell){
28361             cell.title = "";
28362             var t = d.getTime();
28363             cell.firstChild.dateValue = t;
28364             if(t == today){
28365                 cell.className += " x-date-today";
28366                 cell.title = cal.todayText;
28367             }
28368             if(t == sel){
28369                 cell.className += " x-date-selected";
28370                 setTimeout(function(){
28371                     try{cell.firstChild.focus();}catch(e){}
28372                 }, 50);
28373             }
28374             // disabling
28375             if(t < min) {
28376                 cell.className = " x-date-disabled";
28377                 cell.title = cal.minText;
28378                 return;
28379             }
28380             if(t > max) {
28381                 cell.className = " x-date-disabled";
28382                 cell.title = cal.maxText;
28383                 return;
28384             }
28385             if(ddays){
28386                 if(ddays.indexOf(d.getDay()) != -1){
28387                     cell.title = ddaysText;
28388                     cell.className = " x-date-disabled";
28389                 }
28390             }
28391             if(ddMatch && format){
28392                 var fvalue = d.dateFormat(format);
28393                 if(ddMatch.test(fvalue)){
28394                     cell.title = ddText.replace("%0", fvalue);
28395                     cell.className = " x-date-disabled";
28396                 }
28397             }
28398         };
28399
28400         var i = 0;
28401         for(; i < startingPos; i++) {
28402             textEls[i].innerHTML = (++prevStart);
28403             d.setDate(d.getDate()+1);
28404             cells[i].className = "x-date-prevday";
28405             setCellClass(this, cells[i]);
28406         }
28407         for(; i < days; i++){
28408             intDay = i - startingPos + 1;
28409             textEls[i].innerHTML = (intDay);
28410             d.setDate(d.getDate()+1);
28411             cells[i].className = "x-date-active";
28412             setCellClass(this, cells[i]);
28413         }
28414         var extraDays = 0;
28415         for(; i < 42; i++) {
28416              textEls[i].innerHTML = (++extraDays);
28417              d.setDate(d.getDate()+1);
28418              cells[i].className = "x-date-nextday";
28419              setCellClass(this, cells[i]);
28420         }
28421
28422         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28423         this.fireEvent('monthchange', this, date);
28424         
28425         if(!this.internalRender){
28426             var main = this.el.dom.firstChild;
28427             var w = main.offsetWidth;
28428             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28429             Roo.fly(main).setWidth(w);
28430             this.internalRender = true;
28431             // opera does not respect the auto grow header center column
28432             // then, after it gets a width opera refuses to recalculate
28433             // without a second pass
28434             if(Roo.isOpera && !this.secondPass){
28435                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28436                 this.secondPass = true;
28437                 this.update.defer(10, this, [date]);
28438             }
28439         }
28440         
28441         
28442     }
28443 });        /*
28444  * Based on:
28445  * Ext JS Library 1.1.1
28446  * Copyright(c) 2006-2007, Ext JS, LLC.
28447  *
28448  * Originally Released Under LGPL - original licence link has changed is not relivant.
28449  *
28450  * Fork - LGPL
28451  * <script type="text/javascript">
28452  */
28453 /**
28454  * @class Roo.TabPanel
28455  * @extends Roo.util.Observable
28456  * A lightweight tab container.
28457  * <br><br>
28458  * Usage:
28459  * <pre><code>
28460 // basic tabs 1, built from existing content
28461 var tabs = new Roo.TabPanel("tabs1");
28462 tabs.addTab("script", "View Script");
28463 tabs.addTab("markup", "View Markup");
28464 tabs.activate("script");
28465
28466 // more advanced tabs, built from javascript
28467 var jtabs = new Roo.TabPanel("jtabs");
28468 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28469
28470 // set up the UpdateManager
28471 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28472 var updater = tab2.getUpdateManager();
28473 updater.setDefaultUrl("ajax1.htm");
28474 tab2.on('activate', updater.refresh, updater, true);
28475
28476 // Use setUrl for Ajax loading
28477 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28478 tab3.setUrl("ajax2.htm", null, true);
28479
28480 // Disabled tab
28481 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28482 tab4.disable();
28483
28484 jtabs.activate("jtabs-1");
28485  * </code></pre>
28486  * @constructor
28487  * Create a new TabPanel.
28488  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28489  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28490  */
28491 Roo.TabPanel = function(container, config){
28492     /**
28493     * The container element for this TabPanel.
28494     * @type Roo.Element
28495     */
28496     this.el = Roo.get(container, true);
28497     if(config){
28498         if(typeof config == "boolean"){
28499             this.tabPosition = config ? "bottom" : "top";
28500         }else{
28501             Roo.apply(this, config);
28502         }
28503     }
28504     if(this.tabPosition == "bottom"){
28505         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28506         this.el.addClass("x-tabs-bottom");
28507     }
28508     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28509     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28510     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28511     if(Roo.isIE){
28512         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28513     }
28514     if(this.tabPosition != "bottom"){
28515         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28516          * @type Roo.Element
28517          */
28518         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28519         this.el.addClass("x-tabs-top");
28520     }
28521     this.items = [];
28522
28523     this.bodyEl.setStyle("position", "relative");
28524
28525     this.active = null;
28526     this.activateDelegate = this.activate.createDelegate(this);
28527
28528     this.addEvents({
28529         /**
28530          * @event tabchange
28531          * Fires when the active tab changes
28532          * @param {Roo.TabPanel} this
28533          * @param {Roo.TabPanelItem} activePanel The new active tab
28534          */
28535         "tabchange": true,
28536         /**
28537          * @event beforetabchange
28538          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28539          * @param {Roo.TabPanel} this
28540          * @param {Object} e Set cancel to true on this object to cancel the tab change
28541          * @param {Roo.TabPanelItem} tab The tab being changed to
28542          */
28543         "beforetabchange" : true
28544     });
28545
28546     Roo.EventManager.onWindowResize(this.onResize, this);
28547     this.cpad = this.el.getPadding("lr");
28548     this.hiddenCount = 0;
28549
28550
28551     // toolbar on the tabbar support...
28552     if (this.toolbar) {
28553         var tcfg = this.toolbar;
28554         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28555         this.toolbar = new Roo.Toolbar(tcfg);
28556         if (Roo.isSafari) {
28557             var tbl = tcfg.container.child('table', true);
28558             tbl.setAttribute('width', '100%');
28559         }
28560         
28561     }
28562    
28563
28564
28565     Roo.TabPanel.superclass.constructor.call(this);
28566 };
28567
28568 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28569     /*
28570      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28571      */
28572     tabPosition : "top",
28573     /*
28574      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28575      */
28576     currentTabWidth : 0,
28577     /*
28578      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28579      */
28580     minTabWidth : 40,
28581     /*
28582      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28583      */
28584     maxTabWidth : 250,
28585     /*
28586      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28587      */
28588     preferredTabWidth : 175,
28589     /*
28590      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28591      */
28592     resizeTabs : false,
28593     /*
28594      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28595      */
28596     monitorResize : true,
28597     /*
28598      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28599      */
28600     toolbar : false,
28601
28602     /**
28603      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28604      * @param {String} id The id of the div to use <b>or create</b>
28605      * @param {String} text The text for the tab
28606      * @param {String} content (optional) Content to put in the TabPanelItem body
28607      * @param {Boolean} closable (optional) True to create a close icon on the tab
28608      * @return {Roo.TabPanelItem} The created TabPanelItem
28609      */
28610     addTab : function(id, text, content, closable){
28611         var item = new Roo.TabPanelItem(this, id, text, closable);
28612         this.addTabItem(item);
28613         if(content){
28614             item.setContent(content);
28615         }
28616         return item;
28617     },
28618
28619     /**
28620      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28621      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28622      * @return {Roo.TabPanelItem}
28623      */
28624     getTab : function(id){
28625         return this.items[id];
28626     },
28627
28628     /**
28629      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28630      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28631      */
28632     hideTab : function(id){
28633         var t = this.items[id];
28634         if(!t.isHidden()){
28635            t.setHidden(true);
28636            this.hiddenCount++;
28637            this.autoSizeTabs();
28638         }
28639     },
28640
28641     /**
28642      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28643      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28644      */
28645     unhideTab : function(id){
28646         var t = this.items[id];
28647         if(t.isHidden()){
28648            t.setHidden(false);
28649            this.hiddenCount--;
28650            this.autoSizeTabs();
28651         }
28652     },
28653
28654     /**
28655      * Adds an existing {@link Roo.TabPanelItem}.
28656      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28657      */
28658     addTabItem : function(item){
28659         this.items[item.id] = item;
28660         this.items.push(item);
28661         if(this.resizeTabs){
28662            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28663            this.autoSizeTabs();
28664         }else{
28665             item.autoSize();
28666         }
28667     },
28668
28669     /**
28670      * Removes a {@link Roo.TabPanelItem}.
28671      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28672      */
28673     removeTab : function(id){
28674         var items = this.items;
28675         var tab = items[id];
28676         if(!tab) { return; }
28677         var index = items.indexOf(tab);
28678         if(this.active == tab && items.length > 1){
28679             var newTab = this.getNextAvailable(index);
28680             if(newTab) {
28681                 newTab.activate();
28682             }
28683         }
28684         this.stripEl.dom.removeChild(tab.pnode.dom);
28685         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28686             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28687         }
28688         items.splice(index, 1);
28689         delete this.items[tab.id];
28690         tab.fireEvent("close", tab);
28691         tab.purgeListeners();
28692         this.autoSizeTabs();
28693     },
28694
28695     getNextAvailable : function(start){
28696         var items = this.items;
28697         var index = start;
28698         // look for a next tab that will slide over to
28699         // replace the one being removed
28700         while(index < items.length){
28701             var item = items[++index];
28702             if(item && !item.isHidden()){
28703                 return item;
28704             }
28705         }
28706         // if one isn't found select the previous tab (on the left)
28707         index = start;
28708         while(index >= 0){
28709             var item = items[--index];
28710             if(item && !item.isHidden()){
28711                 return item;
28712             }
28713         }
28714         return null;
28715     },
28716
28717     /**
28718      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28719      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28720      */
28721     disableTab : function(id){
28722         var tab = this.items[id];
28723         if(tab && this.active != tab){
28724             tab.disable();
28725         }
28726     },
28727
28728     /**
28729      * Enables a {@link Roo.TabPanelItem} that is disabled.
28730      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28731      */
28732     enableTab : function(id){
28733         var tab = this.items[id];
28734         tab.enable();
28735     },
28736
28737     /**
28738      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28739      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28740      * @return {Roo.TabPanelItem} The TabPanelItem.
28741      */
28742     activate : function(id){
28743         var tab = this.items[id];
28744         if(!tab){
28745             return null;
28746         }
28747         if(tab == this.active || tab.disabled){
28748             return tab;
28749         }
28750         var e = {};
28751         this.fireEvent("beforetabchange", this, e, tab);
28752         if(e.cancel !== true && !tab.disabled){
28753             if(this.active){
28754                 this.active.hide();
28755             }
28756             this.active = this.items[id];
28757             this.active.show();
28758             this.fireEvent("tabchange", this, this.active);
28759         }
28760         return tab;
28761     },
28762
28763     /**
28764      * Gets the active {@link Roo.TabPanelItem}.
28765      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28766      */
28767     getActiveTab : function(){
28768         return this.active;
28769     },
28770
28771     /**
28772      * Updates the tab body element to fit the height of the container element
28773      * for overflow scrolling
28774      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28775      */
28776     syncHeight : function(targetHeight){
28777         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28778         var bm = this.bodyEl.getMargins();
28779         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28780         this.bodyEl.setHeight(newHeight);
28781         return newHeight;
28782     },
28783
28784     onResize : function(){
28785         if(this.monitorResize){
28786             this.autoSizeTabs();
28787         }
28788     },
28789
28790     /**
28791      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28792      */
28793     beginUpdate : function(){
28794         this.updating = true;
28795     },
28796
28797     /**
28798      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28799      */
28800     endUpdate : function(){
28801         this.updating = false;
28802         this.autoSizeTabs();
28803     },
28804
28805     /**
28806      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28807      */
28808     autoSizeTabs : function(){
28809         var count = this.items.length;
28810         var vcount = count - this.hiddenCount;
28811         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28812             return;
28813         }
28814         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28815         var availWidth = Math.floor(w / vcount);
28816         var b = this.stripBody;
28817         if(b.getWidth() > w){
28818             var tabs = this.items;
28819             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28820             if(availWidth < this.minTabWidth){
28821                 /*if(!this.sleft){    // incomplete scrolling code
28822                     this.createScrollButtons();
28823                 }
28824                 this.showScroll();
28825                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28826             }
28827         }else{
28828             if(this.currentTabWidth < this.preferredTabWidth){
28829                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28830             }
28831         }
28832     },
28833
28834     /**
28835      * Returns the number of tabs in this TabPanel.
28836      * @return {Number}
28837      */
28838      getCount : function(){
28839          return this.items.length;
28840      },
28841
28842     /**
28843      * Resizes all the tabs to the passed width
28844      * @param {Number} The new width
28845      */
28846     setTabWidth : function(width){
28847         this.currentTabWidth = width;
28848         for(var i = 0, len = this.items.length; i < len; i++) {
28849                 if(!this.items[i].isHidden()) {
28850                 this.items[i].setWidth(width);
28851             }
28852         }
28853     },
28854
28855     /**
28856      * Destroys this TabPanel
28857      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28858      */
28859     destroy : function(removeEl){
28860         Roo.EventManager.removeResizeListener(this.onResize, this);
28861         for(var i = 0, len = this.items.length; i < len; i++){
28862             this.items[i].purgeListeners();
28863         }
28864         if(removeEl === true){
28865             this.el.update("");
28866             this.el.remove();
28867         }
28868     }
28869 });
28870
28871 /**
28872  * @class Roo.TabPanelItem
28873  * @extends Roo.util.Observable
28874  * Represents an individual item (tab plus body) in a TabPanel.
28875  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28876  * @param {String} id The id of this TabPanelItem
28877  * @param {String} text The text for the tab of this TabPanelItem
28878  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28879  */
28880 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28881     /**
28882      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28883      * @type Roo.TabPanel
28884      */
28885     this.tabPanel = tabPanel;
28886     /**
28887      * The id for this TabPanelItem
28888      * @type String
28889      */
28890     this.id = id;
28891     /** @private */
28892     this.disabled = false;
28893     /** @private */
28894     this.text = text;
28895     /** @private */
28896     this.loaded = false;
28897     this.closable = closable;
28898
28899     /**
28900      * The body element for this TabPanelItem.
28901      * @type Roo.Element
28902      */
28903     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28904     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28905     this.bodyEl.setStyle("display", "block");
28906     this.bodyEl.setStyle("zoom", "1");
28907     this.hideAction();
28908
28909     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28910     /** @private */
28911     this.el = Roo.get(els.el, true);
28912     this.inner = Roo.get(els.inner, true);
28913     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28914     this.pnode = Roo.get(els.el.parentNode, true);
28915     this.el.on("mousedown", this.onTabMouseDown, this);
28916     this.el.on("click", this.onTabClick, this);
28917     /** @private */
28918     if(closable){
28919         var c = Roo.get(els.close, true);
28920         c.dom.title = this.closeText;
28921         c.addClassOnOver("close-over");
28922         c.on("click", this.closeClick, this);
28923      }
28924
28925     this.addEvents({
28926          /**
28927          * @event activate
28928          * Fires when this tab becomes the active tab.
28929          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28930          * @param {Roo.TabPanelItem} this
28931          */
28932         "activate": true,
28933         /**
28934          * @event beforeclose
28935          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28936          * @param {Roo.TabPanelItem} this
28937          * @param {Object} e Set cancel to true on this object to cancel the close.
28938          */
28939         "beforeclose": true,
28940         /**
28941          * @event close
28942          * Fires when this tab is closed.
28943          * @param {Roo.TabPanelItem} this
28944          */
28945          "close": true,
28946         /**
28947          * @event deactivate
28948          * Fires when this tab is no longer the active tab.
28949          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28950          * @param {Roo.TabPanelItem} this
28951          */
28952          "deactivate" : true
28953     });
28954     this.hidden = false;
28955
28956     Roo.TabPanelItem.superclass.constructor.call(this);
28957 };
28958
28959 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28960     purgeListeners : function(){
28961        Roo.util.Observable.prototype.purgeListeners.call(this);
28962        this.el.removeAllListeners();
28963     },
28964     /**
28965      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28966      */
28967     show : function(){
28968         this.pnode.addClass("on");
28969         this.showAction();
28970         if(Roo.isOpera){
28971             this.tabPanel.stripWrap.repaint();
28972         }
28973         this.fireEvent("activate", this.tabPanel, this);
28974     },
28975
28976     /**
28977      * Returns true if this tab is the active tab.
28978      * @return {Boolean}
28979      */
28980     isActive : function(){
28981         return this.tabPanel.getActiveTab() == this;
28982     },
28983
28984     /**
28985      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28986      */
28987     hide : function(){
28988         this.pnode.removeClass("on");
28989         this.hideAction();
28990         this.fireEvent("deactivate", this.tabPanel, this);
28991     },
28992
28993     hideAction : function(){
28994         this.bodyEl.hide();
28995         this.bodyEl.setStyle("position", "absolute");
28996         this.bodyEl.setLeft("-20000px");
28997         this.bodyEl.setTop("-20000px");
28998     },
28999
29000     showAction : function(){
29001         this.bodyEl.setStyle("position", "relative");
29002         this.bodyEl.setTop("");
29003         this.bodyEl.setLeft("");
29004         this.bodyEl.show();
29005     },
29006
29007     /**
29008      * Set the tooltip for the tab.
29009      * @param {String} tooltip The tab's tooltip
29010      */
29011     setTooltip : function(text){
29012         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29013             this.textEl.dom.qtip = text;
29014             this.textEl.dom.removeAttribute('title');
29015         }else{
29016             this.textEl.dom.title = text;
29017         }
29018     },
29019
29020     onTabClick : function(e){
29021         e.preventDefault();
29022         this.tabPanel.activate(this.id);
29023     },
29024
29025     onTabMouseDown : function(e){
29026         e.preventDefault();
29027         this.tabPanel.activate(this.id);
29028     },
29029
29030     getWidth : function(){
29031         return this.inner.getWidth();
29032     },
29033
29034     setWidth : function(width){
29035         var iwidth = width - this.pnode.getPadding("lr");
29036         this.inner.setWidth(iwidth);
29037         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29038         this.pnode.setWidth(width);
29039     },
29040
29041     /**
29042      * Show or hide the tab
29043      * @param {Boolean} hidden True to hide or false to show.
29044      */
29045     setHidden : function(hidden){
29046         this.hidden = hidden;
29047         this.pnode.setStyle("display", hidden ? "none" : "");
29048     },
29049
29050     /**
29051      * Returns true if this tab is "hidden"
29052      * @return {Boolean}
29053      */
29054     isHidden : function(){
29055         return this.hidden;
29056     },
29057
29058     /**
29059      * Returns the text for this tab
29060      * @return {String}
29061      */
29062     getText : function(){
29063         return this.text;
29064     },
29065
29066     autoSize : function(){
29067         //this.el.beginMeasure();
29068         this.textEl.setWidth(1);
29069         /*
29070          *  #2804 [new] Tabs in Roojs
29071          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29072          */
29073         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29074         //this.el.endMeasure();
29075     },
29076
29077     /**
29078      * Sets the text for the tab (Note: this also sets the tooltip text)
29079      * @param {String} text The tab's text and tooltip
29080      */
29081     setText : function(text){
29082         this.text = text;
29083         this.textEl.update(text);
29084         this.setTooltip(text);
29085         if(!this.tabPanel.resizeTabs){
29086             this.autoSize();
29087         }
29088     },
29089     /**
29090      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29091      */
29092     activate : function(){
29093         this.tabPanel.activate(this.id);
29094     },
29095
29096     /**
29097      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29098      */
29099     disable : function(){
29100         if(this.tabPanel.active != this){
29101             this.disabled = true;
29102             this.pnode.addClass("disabled");
29103         }
29104     },
29105
29106     /**
29107      * Enables this TabPanelItem if it was previously disabled.
29108      */
29109     enable : function(){
29110         this.disabled = false;
29111         this.pnode.removeClass("disabled");
29112     },
29113
29114     /**
29115      * Sets the content for this TabPanelItem.
29116      * @param {String} content The content
29117      * @param {Boolean} loadScripts true to look for and load scripts
29118      */
29119     setContent : function(content, loadScripts){
29120         this.bodyEl.update(content, loadScripts);
29121     },
29122
29123     /**
29124      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29125      * @return {Roo.UpdateManager} The UpdateManager
29126      */
29127     getUpdateManager : function(){
29128         return this.bodyEl.getUpdateManager();
29129     },
29130
29131     /**
29132      * Set a URL to be used to load the content for this TabPanelItem.
29133      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29134      * @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)
29135      * @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)
29136      * @return {Roo.UpdateManager} The UpdateManager
29137      */
29138     setUrl : function(url, params, loadOnce){
29139         if(this.refreshDelegate){
29140             this.un('activate', this.refreshDelegate);
29141         }
29142         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29143         this.on("activate", this.refreshDelegate);
29144         return this.bodyEl.getUpdateManager();
29145     },
29146
29147     /** @private */
29148     _handleRefresh : function(url, params, loadOnce){
29149         if(!loadOnce || !this.loaded){
29150             var updater = this.bodyEl.getUpdateManager();
29151             updater.update(url, params, this._setLoaded.createDelegate(this));
29152         }
29153     },
29154
29155     /**
29156      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29157      *   Will fail silently if the setUrl method has not been called.
29158      *   This does not activate the panel, just updates its content.
29159      */
29160     refresh : function(){
29161         if(this.refreshDelegate){
29162            this.loaded = false;
29163            this.refreshDelegate();
29164         }
29165     },
29166
29167     /** @private */
29168     _setLoaded : function(){
29169         this.loaded = true;
29170     },
29171
29172     /** @private */
29173     closeClick : function(e){
29174         var o = {};
29175         e.stopEvent();
29176         this.fireEvent("beforeclose", this, o);
29177         if(o.cancel !== true){
29178             this.tabPanel.removeTab(this.id);
29179         }
29180     },
29181     /**
29182      * The text displayed in the tooltip for the close icon.
29183      * @type String
29184      */
29185     closeText : "Close this tab"
29186 });
29187
29188 /** @private */
29189 Roo.TabPanel.prototype.createStrip = function(container){
29190     var strip = document.createElement("div");
29191     strip.className = "x-tabs-wrap";
29192     container.appendChild(strip);
29193     return strip;
29194 };
29195 /** @private */
29196 Roo.TabPanel.prototype.createStripList = function(strip){
29197     // div wrapper for retard IE
29198     // returns the "tr" element.
29199     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29200         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29201         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29202     return strip.firstChild.firstChild.firstChild.firstChild;
29203 };
29204 /** @private */
29205 Roo.TabPanel.prototype.createBody = function(container){
29206     var body = document.createElement("div");
29207     Roo.id(body, "tab-body");
29208     Roo.fly(body).addClass("x-tabs-body");
29209     container.appendChild(body);
29210     return body;
29211 };
29212 /** @private */
29213 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29214     var body = Roo.getDom(id);
29215     if(!body){
29216         body = document.createElement("div");
29217         body.id = id;
29218     }
29219     Roo.fly(body).addClass("x-tabs-item-body");
29220     bodyEl.insertBefore(body, bodyEl.firstChild);
29221     return body;
29222 };
29223 /** @private */
29224 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29225     var td = document.createElement("td");
29226     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29227     //stripEl.appendChild(td);
29228     if(closable){
29229         td.className = "x-tabs-closable";
29230         if(!this.closeTpl){
29231             this.closeTpl = new Roo.Template(
29232                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29233                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29234                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29235             );
29236         }
29237         var el = this.closeTpl.overwrite(td, {"text": text});
29238         var close = el.getElementsByTagName("div")[0];
29239         var inner = el.getElementsByTagName("em")[0];
29240         return {"el": el, "close": close, "inner": inner};
29241     } else {
29242         if(!this.tabTpl){
29243             this.tabTpl = new Roo.Template(
29244                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29245                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29246             );
29247         }
29248         var el = this.tabTpl.overwrite(td, {"text": text});
29249         var inner = el.getElementsByTagName("em")[0];
29250         return {"el": el, "inner": inner};
29251     }
29252 };/*
29253  * Based on:
29254  * Ext JS Library 1.1.1
29255  * Copyright(c) 2006-2007, Ext JS, LLC.
29256  *
29257  * Originally Released Under LGPL - original licence link has changed is not relivant.
29258  *
29259  * Fork - LGPL
29260  * <script type="text/javascript">
29261  */
29262
29263 /**
29264  * @class Roo.Button
29265  * @extends Roo.util.Observable
29266  * Simple Button class
29267  * @cfg {String} text The button text
29268  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29269  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29270  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29271  * @cfg {Object} scope The scope of the handler
29272  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29273  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29274  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29275  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29276  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29277  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29278    applies if enableToggle = true)
29279  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29280  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29281   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29282  * @constructor
29283  * Create a new button
29284  * @param {Object} config The config object
29285  */
29286 Roo.Button = function(renderTo, config)
29287 {
29288     if (!config) {
29289         config = renderTo;
29290         renderTo = config.renderTo || false;
29291     }
29292     
29293     Roo.apply(this, config);
29294     this.addEvents({
29295         /**
29296              * @event click
29297              * Fires when this button is clicked
29298              * @param {Button} this
29299              * @param {EventObject} e The click event
29300              */
29301             "click" : true,
29302         /**
29303              * @event toggle
29304              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29305              * @param {Button} this
29306              * @param {Boolean} pressed
29307              */
29308             "toggle" : true,
29309         /**
29310              * @event mouseover
29311              * Fires when the mouse hovers over the button
29312              * @param {Button} this
29313              * @param {Event} e The event object
29314              */
29315         'mouseover' : true,
29316         /**
29317              * @event mouseout
29318              * Fires when the mouse exits the button
29319              * @param {Button} this
29320              * @param {Event} e The event object
29321              */
29322         'mouseout': true,
29323          /**
29324              * @event render
29325              * Fires when the button is rendered
29326              * @param {Button} this
29327              */
29328         'render': true
29329     });
29330     if(this.menu){
29331         this.menu = Roo.menu.MenuMgr.get(this.menu);
29332     }
29333     // register listeners first!!  - so render can be captured..
29334     Roo.util.Observable.call(this);
29335     if(renderTo){
29336         this.render(renderTo);
29337     }
29338     
29339   
29340 };
29341
29342 Roo.extend(Roo.Button, Roo.util.Observable, {
29343     /**
29344      * 
29345      */
29346     
29347     /**
29348      * Read-only. True if this button is hidden
29349      * @type Boolean
29350      */
29351     hidden : false,
29352     /**
29353      * Read-only. True if this button is disabled
29354      * @type Boolean
29355      */
29356     disabled : false,
29357     /**
29358      * Read-only. True if this button is pressed (only if enableToggle = true)
29359      * @type Boolean
29360      */
29361     pressed : false,
29362
29363     /**
29364      * @cfg {Number} tabIndex 
29365      * The DOM tabIndex for this button (defaults to undefined)
29366      */
29367     tabIndex : undefined,
29368
29369     /**
29370      * @cfg {Boolean} enableToggle
29371      * True to enable pressed/not pressed toggling (defaults to false)
29372      */
29373     enableToggle: false,
29374     /**
29375      * @cfg {Mixed} menu
29376      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29377      */
29378     menu : undefined,
29379     /**
29380      * @cfg {String} menuAlign
29381      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29382      */
29383     menuAlign : "tl-bl?",
29384
29385     /**
29386      * @cfg {String} iconCls
29387      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29388      */
29389     iconCls : undefined,
29390     /**
29391      * @cfg {String} type
29392      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29393      */
29394     type : 'button',
29395
29396     // private
29397     menuClassTarget: 'tr',
29398
29399     /**
29400      * @cfg {String} clickEvent
29401      * The type of event to map to the button's event handler (defaults to 'click')
29402      */
29403     clickEvent : 'click',
29404
29405     /**
29406      * @cfg {Boolean} handleMouseEvents
29407      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29408      */
29409     handleMouseEvents : true,
29410
29411     /**
29412      * @cfg {String} tooltipType
29413      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29414      */
29415     tooltipType : 'qtip',
29416
29417     /**
29418      * @cfg {String} cls
29419      * A CSS class to apply to the button's main element.
29420      */
29421     
29422     /**
29423      * @cfg {Roo.Template} template (Optional)
29424      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29425      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29426      * require code modifications if required elements (e.g. a button) aren't present.
29427      */
29428
29429     // private
29430     render : function(renderTo){
29431         var btn;
29432         if(this.hideParent){
29433             this.parentEl = Roo.get(renderTo);
29434         }
29435         if(!this.dhconfig){
29436             if(!this.template){
29437                 if(!Roo.Button.buttonTemplate){
29438                     // hideous table template
29439                     Roo.Button.buttonTemplate = new Roo.Template(
29440                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29441                         '<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>',
29442                         "</tr></tbody></table>");
29443                 }
29444                 this.template = Roo.Button.buttonTemplate;
29445             }
29446             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29447             var btnEl = btn.child("button:first");
29448             btnEl.on('focus', this.onFocus, this);
29449             btnEl.on('blur', this.onBlur, this);
29450             if(this.cls){
29451                 btn.addClass(this.cls);
29452             }
29453             if(this.icon){
29454                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29455             }
29456             if(this.iconCls){
29457                 btnEl.addClass(this.iconCls);
29458                 if(!this.cls){
29459                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29460                 }
29461             }
29462             if(this.tabIndex !== undefined){
29463                 btnEl.dom.tabIndex = this.tabIndex;
29464             }
29465             if(this.tooltip){
29466                 if(typeof this.tooltip == 'object'){
29467                     Roo.QuickTips.tips(Roo.apply({
29468                           target: btnEl.id
29469                     }, this.tooltip));
29470                 } else {
29471                     btnEl.dom[this.tooltipType] = this.tooltip;
29472                 }
29473             }
29474         }else{
29475             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29476         }
29477         this.el = btn;
29478         if(this.id){
29479             this.el.dom.id = this.el.id = this.id;
29480         }
29481         if(this.menu){
29482             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29483             this.menu.on("show", this.onMenuShow, this);
29484             this.menu.on("hide", this.onMenuHide, this);
29485         }
29486         btn.addClass("x-btn");
29487         if(Roo.isIE && !Roo.isIE7){
29488             this.autoWidth.defer(1, this);
29489         }else{
29490             this.autoWidth();
29491         }
29492         if(this.handleMouseEvents){
29493             btn.on("mouseover", this.onMouseOver, this);
29494             btn.on("mouseout", this.onMouseOut, this);
29495             btn.on("mousedown", this.onMouseDown, this);
29496         }
29497         btn.on(this.clickEvent, this.onClick, this);
29498         //btn.on("mouseup", this.onMouseUp, this);
29499         if(this.hidden){
29500             this.hide();
29501         }
29502         if(this.disabled){
29503             this.disable();
29504         }
29505         Roo.ButtonToggleMgr.register(this);
29506         if(this.pressed){
29507             this.el.addClass("x-btn-pressed");
29508         }
29509         if(this.repeat){
29510             var repeater = new Roo.util.ClickRepeater(btn,
29511                 typeof this.repeat == "object" ? this.repeat : {}
29512             );
29513             repeater.on("click", this.onClick,  this);
29514         }
29515         
29516         this.fireEvent('render', this);
29517         
29518     },
29519     /**
29520      * Returns the button's underlying element
29521      * @return {Roo.Element} The element
29522      */
29523     getEl : function(){
29524         return this.el;  
29525     },
29526     
29527     /**
29528      * Destroys this Button and removes any listeners.
29529      */
29530     destroy : function(){
29531         Roo.ButtonToggleMgr.unregister(this);
29532         this.el.removeAllListeners();
29533         this.purgeListeners();
29534         this.el.remove();
29535     },
29536
29537     // private
29538     autoWidth : function(){
29539         if(this.el){
29540             this.el.setWidth("auto");
29541             if(Roo.isIE7 && Roo.isStrict){
29542                 var ib = this.el.child('button');
29543                 if(ib && ib.getWidth() > 20){
29544                     ib.clip();
29545                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29546                 }
29547             }
29548             if(this.minWidth){
29549                 if(this.hidden){
29550                     this.el.beginMeasure();
29551                 }
29552                 if(this.el.getWidth() < this.minWidth){
29553                     this.el.setWidth(this.minWidth);
29554                 }
29555                 if(this.hidden){
29556                     this.el.endMeasure();
29557                 }
29558             }
29559         }
29560     },
29561
29562     /**
29563      * Assigns this button's click handler
29564      * @param {Function} handler The function to call when the button is clicked
29565      * @param {Object} scope (optional) Scope for the function passed in
29566      */
29567     setHandler : function(handler, scope){
29568         this.handler = handler;
29569         this.scope = scope;  
29570     },
29571     
29572     /**
29573      * Sets this button's text
29574      * @param {String} text The button text
29575      */
29576     setText : function(text){
29577         this.text = text;
29578         if(this.el){
29579             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29580         }
29581         this.autoWidth();
29582     },
29583     
29584     /**
29585      * Gets the text for this button
29586      * @return {String} The button text
29587      */
29588     getText : function(){
29589         return this.text;  
29590     },
29591     
29592     /**
29593      * Show this button
29594      */
29595     show: function(){
29596         this.hidden = false;
29597         if(this.el){
29598             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29599         }
29600     },
29601     
29602     /**
29603      * Hide this button
29604      */
29605     hide: function(){
29606         this.hidden = true;
29607         if(this.el){
29608             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29609         }
29610     },
29611     
29612     /**
29613      * Convenience function for boolean show/hide
29614      * @param {Boolean} visible True to show, false to hide
29615      */
29616     setVisible: function(visible){
29617         if(visible) {
29618             this.show();
29619         }else{
29620             this.hide();
29621         }
29622     },
29623     
29624     /**
29625      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29626      * @param {Boolean} state (optional) Force a particular state
29627      */
29628     toggle : function(state){
29629         state = state === undefined ? !this.pressed : state;
29630         if(state != this.pressed){
29631             if(state){
29632                 this.el.addClass("x-btn-pressed");
29633                 this.pressed = true;
29634                 this.fireEvent("toggle", this, true);
29635             }else{
29636                 this.el.removeClass("x-btn-pressed");
29637                 this.pressed = false;
29638                 this.fireEvent("toggle", this, false);
29639             }
29640             if(this.toggleHandler){
29641                 this.toggleHandler.call(this.scope || this, this, state);
29642             }
29643         }
29644     },
29645     
29646     /**
29647      * Focus the button
29648      */
29649     focus : function(){
29650         this.el.child('button:first').focus();
29651     },
29652     
29653     /**
29654      * Disable this button
29655      */
29656     disable : function(){
29657         if(this.el){
29658             this.el.addClass("x-btn-disabled");
29659         }
29660         this.disabled = true;
29661     },
29662     
29663     /**
29664      * Enable this button
29665      */
29666     enable : function(){
29667         if(this.el){
29668             this.el.removeClass("x-btn-disabled");
29669         }
29670         this.disabled = false;
29671     },
29672
29673     /**
29674      * Convenience function for boolean enable/disable
29675      * @param {Boolean} enabled True to enable, false to disable
29676      */
29677     setDisabled : function(v){
29678         this[v !== true ? "enable" : "disable"]();
29679     },
29680
29681     // private
29682     onClick : function(e)
29683     {
29684         if(e){
29685             e.preventDefault();
29686         }
29687         if(e.button != 0){
29688             return;
29689         }
29690         if(!this.disabled){
29691             if(this.enableToggle){
29692                 this.toggle();
29693             }
29694             if(this.menu && !this.menu.isVisible()){
29695                 this.menu.show(this.el, this.menuAlign);
29696             }
29697             this.fireEvent("click", this, e);
29698             if(this.handler){
29699                 this.el.removeClass("x-btn-over");
29700                 this.handler.call(this.scope || this, this, e);
29701             }
29702         }
29703     },
29704     // private
29705     onMouseOver : function(e){
29706         if(!this.disabled){
29707             this.el.addClass("x-btn-over");
29708             this.fireEvent('mouseover', this, e);
29709         }
29710     },
29711     // private
29712     onMouseOut : function(e){
29713         if(!e.within(this.el,  true)){
29714             this.el.removeClass("x-btn-over");
29715             this.fireEvent('mouseout', this, e);
29716         }
29717     },
29718     // private
29719     onFocus : function(e){
29720         if(!this.disabled){
29721             this.el.addClass("x-btn-focus");
29722         }
29723     },
29724     // private
29725     onBlur : function(e){
29726         this.el.removeClass("x-btn-focus");
29727     },
29728     // private
29729     onMouseDown : function(e){
29730         if(!this.disabled && e.button == 0){
29731             this.el.addClass("x-btn-click");
29732             Roo.get(document).on('mouseup', this.onMouseUp, this);
29733         }
29734     },
29735     // private
29736     onMouseUp : function(e){
29737         if(e.button == 0){
29738             this.el.removeClass("x-btn-click");
29739             Roo.get(document).un('mouseup', this.onMouseUp, this);
29740         }
29741     },
29742     // private
29743     onMenuShow : function(e){
29744         this.el.addClass("x-btn-menu-active");
29745     },
29746     // private
29747     onMenuHide : function(e){
29748         this.el.removeClass("x-btn-menu-active");
29749     }   
29750 });
29751
29752 // Private utility class used by Button
29753 Roo.ButtonToggleMgr = function(){
29754    var groups = {};
29755    
29756    function toggleGroup(btn, state){
29757        if(state){
29758            var g = groups[btn.toggleGroup];
29759            for(var i = 0, l = g.length; i < l; i++){
29760                if(g[i] != btn){
29761                    g[i].toggle(false);
29762                }
29763            }
29764        }
29765    }
29766    
29767    return {
29768        register : function(btn){
29769            if(!btn.toggleGroup){
29770                return;
29771            }
29772            var g = groups[btn.toggleGroup];
29773            if(!g){
29774                g = groups[btn.toggleGroup] = [];
29775            }
29776            g.push(btn);
29777            btn.on("toggle", toggleGroup);
29778        },
29779        
29780        unregister : function(btn){
29781            if(!btn.toggleGroup){
29782                return;
29783            }
29784            var g = groups[btn.toggleGroup];
29785            if(g){
29786                g.remove(btn);
29787                btn.un("toggle", toggleGroup);
29788            }
29789        }
29790    };
29791 }();/*
29792  * Based on:
29793  * Ext JS Library 1.1.1
29794  * Copyright(c) 2006-2007, Ext JS, LLC.
29795  *
29796  * Originally Released Under LGPL - original licence link has changed is not relivant.
29797  *
29798  * Fork - LGPL
29799  * <script type="text/javascript">
29800  */
29801  
29802 /**
29803  * @class Roo.SplitButton
29804  * @extends Roo.Button
29805  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29806  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29807  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29808  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29809  * @cfg {String} arrowTooltip The title attribute of the arrow
29810  * @constructor
29811  * Create a new menu button
29812  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29813  * @param {Object} config The config object
29814  */
29815 Roo.SplitButton = function(renderTo, config){
29816     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29817     /**
29818      * @event arrowclick
29819      * Fires when this button's arrow is clicked
29820      * @param {SplitButton} this
29821      * @param {EventObject} e The click event
29822      */
29823     this.addEvents({"arrowclick":true});
29824 };
29825
29826 Roo.extend(Roo.SplitButton, Roo.Button, {
29827     render : function(renderTo){
29828         // this is one sweet looking template!
29829         var tpl = new Roo.Template(
29830             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29831             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29832             '<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>',
29833             "</tbody></table></td><td>",
29834             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29835             '<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>',
29836             "</tbody></table></td></tr></table>"
29837         );
29838         var btn = tpl.append(renderTo, [this.text, this.type], true);
29839         var btnEl = btn.child("button");
29840         if(this.cls){
29841             btn.addClass(this.cls);
29842         }
29843         if(this.icon){
29844             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29845         }
29846         if(this.iconCls){
29847             btnEl.addClass(this.iconCls);
29848             if(!this.cls){
29849                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29850             }
29851         }
29852         this.el = btn;
29853         if(this.handleMouseEvents){
29854             btn.on("mouseover", this.onMouseOver, this);
29855             btn.on("mouseout", this.onMouseOut, this);
29856             btn.on("mousedown", this.onMouseDown, this);
29857             btn.on("mouseup", this.onMouseUp, this);
29858         }
29859         btn.on(this.clickEvent, this.onClick, this);
29860         if(this.tooltip){
29861             if(typeof this.tooltip == 'object'){
29862                 Roo.QuickTips.tips(Roo.apply({
29863                       target: btnEl.id
29864                 }, this.tooltip));
29865             } else {
29866                 btnEl.dom[this.tooltipType] = this.tooltip;
29867             }
29868         }
29869         if(this.arrowTooltip){
29870             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29871         }
29872         if(this.hidden){
29873             this.hide();
29874         }
29875         if(this.disabled){
29876             this.disable();
29877         }
29878         if(this.pressed){
29879             this.el.addClass("x-btn-pressed");
29880         }
29881         if(Roo.isIE && !Roo.isIE7){
29882             this.autoWidth.defer(1, this);
29883         }else{
29884             this.autoWidth();
29885         }
29886         if(this.menu){
29887             this.menu.on("show", this.onMenuShow, this);
29888             this.menu.on("hide", this.onMenuHide, this);
29889         }
29890         this.fireEvent('render', this);
29891     },
29892
29893     // private
29894     autoWidth : function(){
29895         if(this.el){
29896             var tbl = this.el.child("table:first");
29897             var tbl2 = this.el.child("table:last");
29898             this.el.setWidth("auto");
29899             tbl.setWidth("auto");
29900             if(Roo.isIE7 && Roo.isStrict){
29901                 var ib = this.el.child('button:first');
29902                 if(ib && ib.getWidth() > 20){
29903                     ib.clip();
29904                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29905                 }
29906             }
29907             if(this.minWidth){
29908                 if(this.hidden){
29909                     this.el.beginMeasure();
29910                 }
29911                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29912                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29913                 }
29914                 if(this.hidden){
29915                     this.el.endMeasure();
29916                 }
29917             }
29918             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29919         } 
29920     },
29921     /**
29922      * Sets this button's click handler
29923      * @param {Function} handler The function to call when the button is clicked
29924      * @param {Object} scope (optional) Scope for the function passed above
29925      */
29926     setHandler : function(handler, scope){
29927         this.handler = handler;
29928         this.scope = scope;  
29929     },
29930     
29931     /**
29932      * Sets this button's arrow click handler
29933      * @param {Function} handler The function to call when the arrow is clicked
29934      * @param {Object} scope (optional) Scope for the function passed above
29935      */
29936     setArrowHandler : function(handler, scope){
29937         this.arrowHandler = handler;
29938         this.scope = scope;  
29939     },
29940     
29941     /**
29942      * Focus the button
29943      */
29944     focus : function(){
29945         if(this.el){
29946             this.el.child("button:first").focus();
29947         }
29948     },
29949
29950     // private
29951     onClick : function(e){
29952         e.preventDefault();
29953         if(!this.disabled){
29954             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29955                 if(this.menu && !this.menu.isVisible()){
29956                     this.menu.show(this.el, this.menuAlign);
29957                 }
29958                 this.fireEvent("arrowclick", this, e);
29959                 if(this.arrowHandler){
29960                     this.arrowHandler.call(this.scope || this, this, e);
29961                 }
29962             }else{
29963                 this.fireEvent("click", this, e);
29964                 if(this.handler){
29965                     this.handler.call(this.scope || this, this, e);
29966                 }
29967             }
29968         }
29969     },
29970     // private
29971     onMouseDown : function(e){
29972         if(!this.disabled){
29973             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29974         }
29975     },
29976     // private
29977     onMouseUp : function(e){
29978         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29979     }   
29980 });
29981
29982
29983 // backwards compat
29984 Roo.MenuButton = Roo.SplitButton;/*
29985  * Based on:
29986  * Ext JS Library 1.1.1
29987  * Copyright(c) 2006-2007, Ext JS, LLC.
29988  *
29989  * Originally Released Under LGPL - original licence link has changed is not relivant.
29990  *
29991  * Fork - LGPL
29992  * <script type="text/javascript">
29993  */
29994
29995 /**
29996  * @class Roo.Toolbar
29997  * Basic Toolbar class.
29998  * @constructor
29999  * Creates a new Toolbar
30000  * @param {Object} container The config object
30001  */ 
30002 Roo.Toolbar = function(container, buttons, config)
30003 {
30004     /// old consturctor format still supported..
30005     if(container instanceof Array){ // omit the container for later rendering
30006         buttons = container;
30007         config = buttons;
30008         container = null;
30009     }
30010     if (typeof(container) == 'object' && container.xtype) {
30011         config = container;
30012         container = config.container;
30013         buttons = config.buttons || []; // not really - use items!!
30014     }
30015     var xitems = [];
30016     if (config && config.items) {
30017         xitems = config.items;
30018         delete config.items;
30019     }
30020     Roo.apply(this, config);
30021     this.buttons = buttons;
30022     
30023     if(container){
30024         this.render(container);
30025     }
30026     this.xitems = xitems;
30027     Roo.each(xitems, function(b) {
30028         this.add(b);
30029     }, this);
30030     
30031 };
30032
30033 Roo.Toolbar.prototype = {
30034     /**
30035      * @cfg {Array} items
30036      * array of button configs or elements to add (will be converted to a MixedCollection)
30037      */
30038     
30039     /**
30040      * @cfg {String/HTMLElement/Element} container
30041      * The id or element that will contain the toolbar
30042      */
30043     // private
30044     render : function(ct){
30045         this.el = Roo.get(ct);
30046         if(this.cls){
30047             this.el.addClass(this.cls);
30048         }
30049         // using a table allows for vertical alignment
30050         // 100% width is needed by Safari...
30051         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30052         this.tr = this.el.child("tr", true);
30053         var autoId = 0;
30054         this.items = new Roo.util.MixedCollection(false, function(o){
30055             return o.id || ("item" + (++autoId));
30056         });
30057         if(this.buttons){
30058             this.add.apply(this, this.buttons);
30059             delete this.buttons;
30060         }
30061     },
30062
30063     /**
30064      * Adds element(s) to the toolbar -- this function takes a variable number of 
30065      * arguments of mixed type and adds them to the toolbar.
30066      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30067      * <ul>
30068      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30069      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30070      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30071      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30072      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30073      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30074      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30075      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30076      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30077      * </ul>
30078      * @param {Mixed} arg2
30079      * @param {Mixed} etc.
30080      */
30081     add : function(){
30082         var a = arguments, l = a.length;
30083         for(var i = 0; i < l; i++){
30084             this._add(a[i]);
30085         }
30086     },
30087     // private..
30088     _add : function(el) {
30089         
30090         if (el.xtype) {
30091             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30092         }
30093         
30094         if (el.applyTo){ // some kind of form field
30095             return this.addField(el);
30096         } 
30097         if (el.render){ // some kind of Toolbar.Item
30098             return this.addItem(el);
30099         }
30100         if (typeof el == "string"){ // string
30101             if(el == "separator" || el == "-"){
30102                 return this.addSeparator();
30103             }
30104             if (el == " "){
30105                 return this.addSpacer();
30106             }
30107             if(el == "->"){
30108                 return this.addFill();
30109             }
30110             return this.addText(el);
30111             
30112         }
30113         if(el.tagName){ // element
30114             return this.addElement(el);
30115         }
30116         if(typeof el == "object"){ // must be button config?
30117             return this.addButton(el);
30118         }
30119         // and now what?!?!
30120         return false;
30121         
30122     },
30123     
30124     /**
30125      * Add an Xtype element
30126      * @param {Object} xtype Xtype Object
30127      * @return {Object} created Object
30128      */
30129     addxtype : function(e){
30130         return this.add(e);  
30131     },
30132     
30133     /**
30134      * Returns the Element for this toolbar.
30135      * @return {Roo.Element}
30136      */
30137     getEl : function(){
30138         return this.el;  
30139     },
30140     
30141     /**
30142      * Adds a separator
30143      * @return {Roo.Toolbar.Item} The separator item
30144      */
30145     addSeparator : function(){
30146         return this.addItem(new Roo.Toolbar.Separator());
30147     },
30148
30149     /**
30150      * Adds a spacer element
30151      * @return {Roo.Toolbar.Spacer} The spacer item
30152      */
30153     addSpacer : function(){
30154         return this.addItem(new Roo.Toolbar.Spacer());
30155     },
30156
30157     /**
30158      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30159      * @return {Roo.Toolbar.Fill} The fill item
30160      */
30161     addFill : function(){
30162         return this.addItem(new Roo.Toolbar.Fill());
30163     },
30164
30165     /**
30166      * Adds any standard HTML element to the toolbar
30167      * @param {String/HTMLElement/Element} el The element or id of the element to add
30168      * @return {Roo.Toolbar.Item} The element's item
30169      */
30170     addElement : function(el){
30171         return this.addItem(new Roo.Toolbar.Item(el));
30172     },
30173     /**
30174      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30175      * @type Roo.util.MixedCollection  
30176      */
30177     items : false,
30178      
30179     /**
30180      * Adds any Toolbar.Item or subclass
30181      * @param {Roo.Toolbar.Item} item
30182      * @return {Roo.Toolbar.Item} The item
30183      */
30184     addItem : function(item){
30185         var td = this.nextBlock();
30186         item.render(td);
30187         this.items.add(item);
30188         return item;
30189     },
30190     
30191     /**
30192      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30193      * @param {Object/Array} config A button config or array of configs
30194      * @return {Roo.Toolbar.Button/Array}
30195      */
30196     addButton : function(config){
30197         if(config instanceof Array){
30198             var buttons = [];
30199             for(var i = 0, len = config.length; i < len; i++) {
30200                 buttons.push(this.addButton(config[i]));
30201             }
30202             return buttons;
30203         }
30204         var b = config;
30205         if(!(config instanceof Roo.Toolbar.Button)){
30206             b = config.split ?
30207                 new Roo.Toolbar.SplitButton(config) :
30208                 new Roo.Toolbar.Button(config);
30209         }
30210         var td = this.nextBlock();
30211         b.render(td);
30212         this.items.add(b);
30213         return b;
30214     },
30215     
30216     /**
30217      * Adds text to the toolbar
30218      * @param {String} text The text to add
30219      * @return {Roo.Toolbar.Item} The element's item
30220      */
30221     addText : function(text){
30222         return this.addItem(new Roo.Toolbar.TextItem(text));
30223     },
30224     
30225     /**
30226      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30227      * @param {Number} index The index where the item is to be inserted
30228      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30229      * @return {Roo.Toolbar.Button/Item}
30230      */
30231     insertButton : function(index, item){
30232         if(item instanceof Array){
30233             var buttons = [];
30234             for(var i = 0, len = item.length; i < len; i++) {
30235                buttons.push(this.insertButton(index + i, item[i]));
30236             }
30237             return buttons;
30238         }
30239         if (!(item instanceof Roo.Toolbar.Button)){
30240            item = new Roo.Toolbar.Button(item);
30241         }
30242         var td = document.createElement("td");
30243         this.tr.insertBefore(td, this.tr.childNodes[index]);
30244         item.render(td);
30245         this.items.insert(index, item);
30246         return item;
30247     },
30248     
30249     /**
30250      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30251      * @param {Object} config
30252      * @return {Roo.Toolbar.Item} The element's item
30253      */
30254     addDom : function(config, returnEl){
30255         var td = this.nextBlock();
30256         Roo.DomHelper.overwrite(td, config);
30257         var ti = new Roo.Toolbar.Item(td.firstChild);
30258         ti.render(td);
30259         this.items.add(ti);
30260         return ti;
30261     },
30262
30263     /**
30264      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30265      * @type Roo.util.MixedCollection  
30266      */
30267     fields : false,
30268     
30269     /**
30270      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30271      * Note: the field should not have been rendered yet. For a field that has already been
30272      * rendered, use {@link #addElement}.
30273      * @param {Roo.form.Field} field
30274      * @return {Roo.ToolbarItem}
30275      */
30276      
30277       
30278     addField : function(field) {
30279         if (!this.fields) {
30280             var autoId = 0;
30281             this.fields = new Roo.util.MixedCollection(false, function(o){
30282                 return o.id || ("item" + (++autoId));
30283             });
30284
30285         }
30286         
30287         var td = this.nextBlock();
30288         field.render(td);
30289         var ti = new Roo.Toolbar.Item(td.firstChild);
30290         ti.render(td);
30291         this.items.add(ti);
30292         this.fields.add(field);
30293         return ti;
30294     },
30295     /**
30296      * Hide the toolbar
30297      * @method hide
30298      */
30299      
30300       
30301     hide : function()
30302     {
30303         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30304         this.el.child('div').hide();
30305     },
30306     /**
30307      * Show the toolbar
30308      * @method show
30309      */
30310     show : function()
30311     {
30312         this.el.child('div').show();
30313     },
30314       
30315     // private
30316     nextBlock : function(){
30317         var td = document.createElement("td");
30318         this.tr.appendChild(td);
30319         return td;
30320     },
30321
30322     // private
30323     destroy : function(){
30324         if(this.items){ // rendered?
30325             Roo.destroy.apply(Roo, this.items.items);
30326         }
30327         if(this.fields){ // rendered?
30328             Roo.destroy.apply(Roo, this.fields.items);
30329         }
30330         Roo.Element.uncache(this.el, this.tr);
30331     }
30332 };
30333
30334 /**
30335  * @class Roo.Toolbar.Item
30336  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30337  * @constructor
30338  * Creates a new Item
30339  * @param {HTMLElement} el 
30340  */
30341 Roo.Toolbar.Item = function(el){
30342     var cfg = {};
30343     if (typeof (el.xtype) != 'undefined') {
30344         cfg = el;
30345         el = cfg.el;
30346     }
30347     
30348     this.el = Roo.getDom(el);
30349     this.id = Roo.id(this.el);
30350     this.hidden = false;
30351     
30352     this.addEvents({
30353          /**
30354              * @event render
30355              * Fires when the button is rendered
30356              * @param {Button} this
30357              */
30358         'render': true
30359     });
30360     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30361 };
30362 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30363 //Roo.Toolbar.Item.prototype = {
30364     
30365     /**
30366      * Get this item's HTML Element
30367      * @return {HTMLElement}
30368      */
30369     getEl : function(){
30370        return this.el;  
30371     },
30372
30373     // private
30374     render : function(td){
30375         
30376          this.td = td;
30377         td.appendChild(this.el);
30378         
30379         this.fireEvent('render', this);
30380     },
30381     
30382     /**
30383      * Removes and destroys this item.
30384      */
30385     destroy : function(){
30386         this.td.parentNode.removeChild(this.td);
30387     },
30388     
30389     /**
30390      * Shows this item.
30391      */
30392     show: function(){
30393         this.hidden = false;
30394         this.td.style.display = "";
30395     },
30396     
30397     /**
30398      * Hides this item.
30399      */
30400     hide: function(){
30401         this.hidden = true;
30402         this.td.style.display = "none";
30403     },
30404     
30405     /**
30406      * Convenience function for boolean show/hide.
30407      * @param {Boolean} visible true to show/false to hide
30408      */
30409     setVisible: function(visible){
30410         if(visible) {
30411             this.show();
30412         }else{
30413             this.hide();
30414         }
30415     },
30416     
30417     /**
30418      * Try to focus this item.
30419      */
30420     focus : function(){
30421         Roo.fly(this.el).focus();
30422     },
30423     
30424     /**
30425      * Disables this item.
30426      */
30427     disable : function(){
30428         Roo.fly(this.td).addClass("x-item-disabled");
30429         this.disabled = true;
30430         this.el.disabled = true;
30431     },
30432     
30433     /**
30434      * Enables this item.
30435      */
30436     enable : function(){
30437         Roo.fly(this.td).removeClass("x-item-disabled");
30438         this.disabled = false;
30439         this.el.disabled = false;
30440     }
30441 });
30442
30443
30444 /**
30445  * @class Roo.Toolbar.Separator
30446  * @extends Roo.Toolbar.Item
30447  * A simple toolbar separator class
30448  * @constructor
30449  * Creates a new Separator
30450  */
30451 Roo.Toolbar.Separator = function(cfg){
30452     
30453     var s = document.createElement("span");
30454     s.className = "ytb-sep";
30455     if (cfg) {
30456         cfg.el = s;
30457     }
30458     
30459     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30460 };
30461 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30462     enable:Roo.emptyFn,
30463     disable:Roo.emptyFn,
30464     focus:Roo.emptyFn
30465 });
30466
30467 /**
30468  * @class Roo.Toolbar.Spacer
30469  * @extends Roo.Toolbar.Item
30470  * A simple element that adds extra horizontal space to a toolbar.
30471  * @constructor
30472  * Creates a new Spacer
30473  */
30474 Roo.Toolbar.Spacer = function(cfg){
30475     var s = document.createElement("div");
30476     s.className = "ytb-spacer";
30477     if (cfg) {
30478         cfg.el = s;
30479     }
30480     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30481 };
30482 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30483     enable:Roo.emptyFn,
30484     disable:Roo.emptyFn,
30485     focus:Roo.emptyFn
30486 });
30487
30488 /**
30489  * @class Roo.Toolbar.Fill
30490  * @extends Roo.Toolbar.Spacer
30491  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30492  * @constructor
30493  * Creates a new Spacer
30494  */
30495 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30496     // private
30497     render : function(td){
30498         td.style.width = '100%';
30499         Roo.Toolbar.Fill.superclass.render.call(this, td);
30500     }
30501 });
30502
30503 /**
30504  * @class Roo.Toolbar.TextItem
30505  * @extends Roo.Toolbar.Item
30506  * A simple class that renders text directly into a toolbar.
30507  * @constructor
30508  * Creates a new TextItem
30509  * @cfg {string} text 
30510  */
30511 Roo.Toolbar.TextItem = function(cfg){
30512     var  text = cfg || "";
30513     if (typeof(cfg) == 'object') {
30514         text = cfg.text || "";
30515     }  else {
30516         cfg = null;
30517     }
30518     var s = document.createElement("span");
30519     s.className = "ytb-text";
30520     s.innerHTML = text;
30521     if (cfg) {
30522         cfg.el  = s;
30523     }
30524     
30525     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30526 };
30527 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30528     
30529      
30530     enable:Roo.emptyFn,
30531     disable:Roo.emptyFn,
30532     focus:Roo.emptyFn
30533 });
30534
30535 /**
30536  * @class Roo.Toolbar.Button
30537  * @extends Roo.Button
30538  * A button that renders into a toolbar.
30539  * @constructor
30540  * Creates a new Button
30541  * @param {Object} config A standard {@link Roo.Button} config object
30542  */
30543 Roo.Toolbar.Button = function(config){
30544     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30545 };
30546 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30547     render : function(td){
30548         this.td = td;
30549         Roo.Toolbar.Button.superclass.render.call(this, td);
30550     },
30551     
30552     /**
30553      * Removes and destroys this button
30554      */
30555     destroy : function(){
30556         Roo.Toolbar.Button.superclass.destroy.call(this);
30557         this.td.parentNode.removeChild(this.td);
30558     },
30559     
30560     /**
30561      * Shows this button
30562      */
30563     show: function(){
30564         this.hidden = false;
30565         this.td.style.display = "";
30566     },
30567     
30568     /**
30569      * Hides this button
30570      */
30571     hide: function(){
30572         this.hidden = true;
30573         this.td.style.display = "none";
30574     },
30575
30576     /**
30577      * Disables this item
30578      */
30579     disable : function(){
30580         Roo.fly(this.td).addClass("x-item-disabled");
30581         this.disabled = true;
30582     },
30583
30584     /**
30585      * Enables this item
30586      */
30587     enable : function(){
30588         Roo.fly(this.td).removeClass("x-item-disabled");
30589         this.disabled = false;
30590     }
30591 });
30592 // backwards compat
30593 Roo.ToolbarButton = Roo.Toolbar.Button;
30594
30595 /**
30596  * @class Roo.Toolbar.SplitButton
30597  * @extends Roo.SplitButton
30598  * A menu button that renders into a toolbar.
30599  * @constructor
30600  * Creates a new SplitButton
30601  * @param {Object} config A standard {@link Roo.SplitButton} config object
30602  */
30603 Roo.Toolbar.SplitButton = function(config){
30604     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30605 };
30606 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30607     render : function(td){
30608         this.td = td;
30609         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30610     },
30611     
30612     /**
30613      * Removes and destroys this button
30614      */
30615     destroy : function(){
30616         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30617         this.td.parentNode.removeChild(this.td);
30618     },
30619     
30620     /**
30621      * Shows this button
30622      */
30623     show: function(){
30624         this.hidden = false;
30625         this.td.style.display = "";
30626     },
30627     
30628     /**
30629      * Hides this button
30630      */
30631     hide: function(){
30632         this.hidden = true;
30633         this.td.style.display = "none";
30634     }
30635 });
30636
30637 // backwards compat
30638 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30639  * Based on:
30640  * Ext JS Library 1.1.1
30641  * Copyright(c) 2006-2007, Ext JS, LLC.
30642  *
30643  * Originally Released Under LGPL - original licence link has changed is not relivant.
30644  *
30645  * Fork - LGPL
30646  * <script type="text/javascript">
30647  */
30648  
30649 /**
30650  * @class Roo.PagingToolbar
30651  * @extends Roo.Toolbar
30652  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30653  * @constructor
30654  * Create a new PagingToolbar
30655  * @param {Object} config The config object
30656  */
30657 Roo.PagingToolbar = function(el, ds, config)
30658 {
30659     // old args format still supported... - xtype is prefered..
30660     if (typeof(el) == 'object' && el.xtype) {
30661         // created from xtype...
30662         config = el;
30663         ds = el.dataSource;
30664         el = config.container;
30665     }
30666     var items = [];
30667     if (config.items) {
30668         items = config.items;
30669         config.items = [];
30670     }
30671     
30672     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30673     this.ds = ds;
30674     this.cursor = 0;
30675     this.renderButtons(this.el);
30676     this.bind(ds);
30677     
30678     // supprot items array.
30679    
30680     Roo.each(items, function(e) {
30681         this.add(Roo.factory(e));
30682     },this);
30683     
30684 };
30685
30686 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30687     /**
30688      * @cfg {Roo.data.Store} dataSource
30689      * The underlying data store providing the paged data
30690      */
30691     /**
30692      * @cfg {String/HTMLElement/Element} container
30693      * container The id or element that will contain the toolbar
30694      */
30695     /**
30696      * @cfg {Boolean} displayInfo
30697      * True to display the displayMsg (defaults to false)
30698      */
30699     /**
30700      * @cfg {Number} pageSize
30701      * The number of records to display per page (defaults to 20)
30702      */
30703     pageSize: 20,
30704     /**
30705      * @cfg {String} displayMsg
30706      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30707      */
30708     displayMsg : 'Displaying {0} - {1} of {2}',
30709     /**
30710      * @cfg {String} emptyMsg
30711      * The message to display when no records are found (defaults to "No data to display")
30712      */
30713     emptyMsg : 'No data to display',
30714     /**
30715      * Customizable piece of the default paging text (defaults to "Page")
30716      * @type String
30717      */
30718     beforePageText : "Page",
30719     /**
30720      * Customizable piece of the default paging text (defaults to "of %0")
30721      * @type String
30722      */
30723     afterPageText : "of {0}",
30724     /**
30725      * Customizable piece of the default paging text (defaults to "First Page")
30726      * @type String
30727      */
30728     firstText : "First Page",
30729     /**
30730      * Customizable piece of the default paging text (defaults to "Previous Page")
30731      * @type String
30732      */
30733     prevText : "Previous Page",
30734     /**
30735      * Customizable piece of the default paging text (defaults to "Next Page")
30736      * @type String
30737      */
30738     nextText : "Next Page",
30739     /**
30740      * Customizable piece of the default paging text (defaults to "Last Page")
30741      * @type String
30742      */
30743     lastText : "Last Page",
30744     /**
30745      * Customizable piece of the default paging text (defaults to "Refresh")
30746      * @type String
30747      */
30748     refreshText : "Refresh",
30749
30750     // private
30751     renderButtons : function(el){
30752         Roo.PagingToolbar.superclass.render.call(this, el);
30753         this.first = this.addButton({
30754             tooltip: this.firstText,
30755             cls: "x-btn-icon x-grid-page-first",
30756             disabled: true,
30757             handler: this.onClick.createDelegate(this, ["first"])
30758         });
30759         this.prev = this.addButton({
30760             tooltip: this.prevText,
30761             cls: "x-btn-icon x-grid-page-prev",
30762             disabled: true,
30763             handler: this.onClick.createDelegate(this, ["prev"])
30764         });
30765         //this.addSeparator();
30766         this.add(this.beforePageText);
30767         this.field = Roo.get(this.addDom({
30768            tag: "input",
30769            type: "text",
30770            size: "3",
30771            value: "1",
30772            cls: "x-grid-page-number"
30773         }).el);
30774         this.field.on("keydown", this.onPagingKeydown, this);
30775         this.field.on("focus", function(){this.dom.select();});
30776         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30777         this.field.setHeight(18);
30778         //this.addSeparator();
30779         this.next = this.addButton({
30780             tooltip: this.nextText,
30781             cls: "x-btn-icon x-grid-page-next",
30782             disabled: true,
30783             handler: this.onClick.createDelegate(this, ["next"])
30784         });
30785         this.last = this.addButton({
30786             tooltip: this.lastText,
30787             cls: "x-btn-icon x-grid-page-last",
30788             disabled: true,
30789             handler: this.onClick.createDelegate(this, ["last"])
30790         });
30791         //this.addSeparator();
30792         this.loading = this.addButton({
30793             tooltip: this.refreshText,
30794             cls: "x-btn-icon x-grid-loading",
30795             handler: this.onClick.createDelegate(this, ["refresh"])
30796         });
30797
30798         if(this.displayInfo){
30799             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30800         }
30801     },
30802
30803     // private
30804     updateInfo : function(){
30805         if(this.displayEl){
30806             var count = this.ds.getCount();
30807             var msg = count == 0 ?
30808                 this.emptyMsg :
30809                 String.format(
30810                     this.displayMsg,
30811                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30812                 );
30813             this.displayEl.update(msg);
30814         }
30815     },
30816
30817     // private
30818     onLoad : function(ds, r, o){
30819        this.cursor = o.params ? o.params.start : 0;
30820        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30821
30822        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30823        this.field.dom.value = ap;
30824        this.first.setDisabled(ap == 1);
30825        this.prev.setDisabled(ap == 1);
30826        this.next.setDisabled(ap == ps);
30827        this.last.setDisabled(ap == ps);
30828        this.loading.enable();
30829        this.updateInfo();
30830     },
30831
30832     // private
30833     getPageData : function(){
30834         var total = this.ds.getTotalCount();
30835         return {
30836             total : total,
30837             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30838             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30839         };
30840     },
30841
30842     // private
30843     onLoadError : function(){
30844         this.loading.enable();
30845     },
30846
30847     // private
30848     onPagingKeydown : function(e){
30849         var k = e.getKey();
30850         var d = this.getPageData();
30851         if(k == e.RETURN){
30852             var v = this.field.dom.value, pageNum;
30853             if(!v || isNaN(pageNum = parseInt(v, 10))){
30854                 this.field.dom.value = d.activePage;
30855                 return;
30856             }
30857             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30858             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30859             e.stopEvent();
30860         }
30861         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))
30862         {
30863           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30864           this.field.dom.value = pageNum;
30865           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30866           e.stopEvent();
30867         }
30868         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30869         {
30870           var v = this.field.dom.value, pageNum; 
30871           var increment = (e.shiftKey) ? 10 : 1;
30872           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30873             increment *= -1;
30874           }
30875           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30876             this.field.dom.value = d.activePage;
30877             return;
30878           }
30879           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30880           {
30881             this.field.dom.value = parseInt(v, 10) + increment;
30882             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30883             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30884           }
30885           e.stopEvent();
30886         }
30887     },
30888
30889     // private
30890     beforeLoad : function(){
30891         if(this.loading){
30892             this.loading.disable();
30893         }
30894     },
30895
30896     // private
30897     onClick : function(which){
30898         var ds = this.ds;
30899         switch(which){
30900             case "first":
30901                 ds.load({params:{start: 0, limit: this.pageSize}});
30902             break;
30903             case "prev":
30904                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30905             break;
30906             case "next":
30907                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30908             break;
30909             case "last":
30910                 var total = ds.getTotalCount();
30911                 var extra = total % this.pageSize;
30912                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30913                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30914             break;
30915             case "refresh":
30916                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30917             break;
30918         }
30919     },
30920
30921     /**
30922      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30923      * @param {Roo.data.Store} store The data store to unbind
30924      */
30925     unbind : function(ds){
30926         ds.un("beforeload", this.beforeLoad, this);
30927         ds.un("load", this.onLoad, this);
30928         ds.un("loadexception", this.onLoadError, this);
30929         ds.un("remove", this.updateInfo, this);
30930         ds.un("add", this.updateInfo, this);
30931         this.ds = undefined;
30932     },
30933
30934     /**
30935      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30936      * @param {Roo.data.Store} store The data store to bind
30937      */
30938     bind : function(ds){
30939         ds.on("beforeload", this.beforeLoad, this);
30940         ds.on("load", this.onLoad, this);
30941         ds.on("loadexception", this.onLoadError, this);
30942         ds.on("remove", this.updateInfo, this);
30943         ds.on("add", this.updateInfo, this);
30944         this.ds = ds;
30945     }
30946 });/*
30947  * Based on:
30948  * Ext JS Library 1.1.1
30949  * Copyright(c) 2006-2007, Ext JS, LLC.
30950  *
30951  * Originally Released Under LGPL - original licence link has changed is not relivant.
30952  *
30953  * Fork - LGPL
30954  * <script type="text/javascript">
30955  */
30956
30957 /**
30958  * @class Roo.Resizable
30959  * @extends Roo.util.Observable
30960  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30961  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30962  * 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
30963  * the element will be wrapped for you automatically.</p>
30964  * <p>Here is the list of valid resize handles:</p>
30965  * <pre>
30966 Value   Description
30967 ------  -------------------
30968  'n'     north
30969  's'     south
30970  'e'     east
30971  'w'     west
30972  'nw'    northwest
30973  'sw'    southwest
30974  'se'    southeast
30975  'ne'    northeast
30976  'hd'    horizontal drag
30977  'all'   all
30978 </pre>
30979  * <p>Here's an example showing the creation of a typical Resizable:</p>
30980  * <pre><code>
30981 var resizer = new Roo.Resizable("element-id", {
30982     handles: 'all',
30983     minWidth: 200,
30984     minHeight: 100,
30985     maxWidth: 500,
30986     maxHeight: 400,
30987     pinned: true
30988 });
30989 resizer.on("resize", myHandler);
30990 </code></pre>
30991  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30992  * resizer.east.setDisplayed(false);</p>
30993  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30994  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30995  * resize operation's new size (defaults to [0, 0])
30996  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30997  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30998  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30999  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31000  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31001  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31002  * @cfg {Number} width The width of the element in pixels (defaults to null)
31003  * @cfg {Number} height The height of the element in pixels (defaults to null)
31004  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31005  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31006  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31007  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31008  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31009  * in favor of the handles config option (defaults to false)
31010  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31011  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31012  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31013  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31014  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31015  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31016  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31017  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31018  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31019  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31020  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31021  * @constructor
31022  * Create a new resizable component
31023  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31024  * @param {Object} config configuration options
31025   */
31026 Roo.Resizable = function(el, config)
31027 {
31028     this.el = Roo.get(el);
31029
31030     if(config && config.wrap){
31031         config.resizeChild = this.el;
31032         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31033         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31034         this.el.setStyle("overflow", "hidden");
31035         this.el.setPositioning(config.resizeChild.getPositioning());
31036         config.resizeChild.clearPositioning();
31037         if(!config.width || !config.height){
31038             var csize = config.resizeChild.getSize();
31039             this.el.setSize(csize.width, csize.height);
31040         }
31041         if(config.pinned && !config.adjustments){
31042             config.adjustments = "auto";
31043         }
31044     }
31045
31046     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31047     this.proxy.unselectable();
31048     this.proxy.enableDisplayMode('block');
31049
31050     Roo.apply(this, config);
31051
31052     if(this.pinned){
31053         this.disableTrackOver = true;
31054         this.el.addClass("x-resizable-pinned");
31055     }
31056     // if the element isn't positioned, make it relative
31057     var position = this.el.getStyle("position");
31058     if(position != "absolute" && position != "fixed"){
31059         this.el.setStyle("position", "relative");
31060     }
31061     if(!this.handles){ // no handles passed, must be legacy style
31062         this.handles = 's,e,se';
31063         if(this.multiDirectional){
31064             this.handles += ',n,w';
31065         }
31066     }
31067     if(this.handles == "all"){
31068         this.handles = "n s e w ne nw se sw";
31069     }
31070     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31071     var ps = Roo.Resizable.positions;
31072     for(var i = 0, len = hs.length; i < len; i++){
31073         if(hs[i] && ps[hs[i]]){
31074             var pos = ps[hs[i]];
31075             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31076         }
31077     }
31078     // legacy
31079     this.corner = this.southeast;
31080     
31081     // updateBox = the box can move..
31082     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31083         this.updateBox = true;
31084     }
31085
31086     this.activeHandle = null;
31087
31088     if(this.resizeChild){
31089         if(typeof this.resizeChild == "boolean"){
31090             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31091         }else{
31092             this.resizeChild = Roo.get(this.resizeChild, true);
31093         }
31094     }
31095     
31096     if(this.adjustments == "auto"){
31097         var rc = this.resizeChild;
31098         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31099         if(rc && (hw || hn)){
31100             rc.position("relative");
31101             rc.setLeft(hw ? hw.el.getWidth() : 0);
31102             rc.setTop(hn ? hn.el.getHeight() : 0);
31103         }
31104         this.adjustments = [
31105             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31106             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31107         ];
31108     }
31109
31110     if(this.draggable){
31111         this.dd = this.dynamic ?
31112             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31113         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31114     }
31115
31116     // public events
31117     this.addEvents({
31118         /**
31119          * @event beforeresize
31120          * Fired before resize is allowed. Set enabled to false to cancel resize.
31121          * @param {Roo.Resizable} this
31122          * @param {Roo.EventObject} e The mousedown event
31123          */
31124         "beforeresize" : true,
31125         /**
31126          * @event resizing
31127          * Fired a resizing.
31128          * @param {Roo.Resizable} this
31129          * @param {Number} x The new x position
31130          * @param {Number} y The new y position
31131          * @param {Number} w The new w width
31132          * @param {Number} h The new h hight
31133          * @param {Roo.EventObject} e The mouseup event
31134          */
31135         "resizing" : true,
31136         /**
31137          * @event resize
31138          * Fired after a resize.
31139          * @param {Roo.Resizable} this
31140          * @param {Number} width The new width
31141          * @param {Number} height The new height
31142          * @param {Roo.EventObject} e The mouseup event
31143          */
31144         "resize" : true
31145     });
31146
31147     if(this.width !== null && this.height !== null){
31148         this.resizeTo(this.width, this.height);
31149     }else{
31150         this.updateChildSize();
31151     }
31152     if(Roo.isIE){
31153         this.el.dom.style.zoom = 1;
31154     }
31155     Roo.Resizable.superclass.constructor.call(this);
31156 };
31157
31158 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31159         resizeChild : false,
31160         adjustments : [0, 0],
31161         minWidth : 5,
31162         minHeight : 5,
31163         maxWidth : 10000,
31164         maxHeight : 10000,
31165         enabled : true,
31166         animate : false,
31167         duration : .35,
31168         dynamic : false,
31169         handles : false,
31170         multiDirectional : false,
31171         disableTrackOver : false,
31172         easing : 'easeOutStrong',
31173         widthIncrement : 0,
31174         heightIncrement : 0,
31175         pinned : false,
31176         width : null,
31177         height : null,
31178         preserveRatio : false,
31179         transparent: false,
31180         minX: 0,
31181         minY: 0,
31182         draggable: false,
31183
31184         /**
31185          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31186          */
31187         constrainTo: undefined,
31188         /**
31189          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31190          */
31191         resizeRegion: undefined,
31192
31193
31194     /**
31195      * Perform a manual resize
31196      * @param {Number} width
31197      * @param {Number} height
31198      */
31199     resizeTo : function(width, height){
31200         this.el.setSize(width, height);
31201         this.updateChildSize();
31202         this.fireEvent("resize", this, width, height, null);
31203     },
31204
31205     // private
31206     startSizing : function(e, handle){
31207         this.fireEvent("beforeresize", this, e);
31208         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31209
31210             if(!this.overlay){
31211                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31212                 this.overlay.unselectable();
31213                 this.overlay.enableDisplayMode("block");
31214                 this.overlay.on("mousemove", this.onMouseMove, this);
31215                 this.overlay.on("mouseup", this.onMouseUp, this);
31216             }
31217             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31218
31219             this.resizing = true;
31220             this.startBox = this.el.getBox();
31221             this.startPoint = e.getXY();
31222             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31223                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31224
31225             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31226             this.overlay.show();
31227
31228             if(this.constrainTo) {
31229                 var ct = Roo.get(this.constrainTo);
31230                 this.resizeRegion = ct.getRegion().adjust(
31231                     ct.getFrameWidth('t'),
31232                     ct.getFrameWidth('l'),
31233                     -ct.getFrameWidth('b'),
31234                     -ct.getFrameWidth('r')
31235                 );
31236             }
31237
31238             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31239             this.proxy.show();
31240             this.proxy.setBox(this.startBox);
31241             if(!this.dynamic){
31242                 this.proxy.setStyle('visibility', 'visible');
31243             }
31244         }
31245     },
31246
31247     // private
31248     onMouseDown : function(handle, e){
31249         if(this.enabled){
31250             e.stopEvent();
31251             this.activeHandle = handle;
31252             this.startSizing(e, handle);
31253         }
31254     },
31255
31256     // private
31257     onMouseUp : function(e){
31258         var size = this.resizeElement();
31259         this.resizing = false;
31260         this.handleOut();
31261         this.overlay.hide();
31262         this.proxy.hide();
31263         this.fireEvent("resize", this, size.width, size.height, e);
31264     },
31265
31266     // private
31267     updateChildSize : function(){
31268         
31269         if(this.resizeChild){
31270             var el = this.el;
31271             var child = this.resizeChild;
31272             var adj = this.adjustments;
31273             if(el.dom.offsetWidth){
31274                 var b = el.getSize(true);
31275                 child.setSize(b.width+adj[0], b.height+adj[1]);
31276             }
31277             // Second call here for IE
31278             // The first call enables instant resizing and
31279             // the second call corrects scroll bars if they
31280             // exist
31281             if(Roo.isIE){
31282                 setTimeout(function(){
31283                     if(el.dom.offsetWidth){
31284                         var b = el.getSize(true);
31285                         child.setSize(b.width+adj[0], b.height+adj[1]);
31286                     }
31287                 }, 10);
31288             }
31289         }
31290     },
31291
31292     // private
31293     snap : function(value, inc, min){
31294         if(!inc || !value) {
31295             return value;
31296         }
31297         var newValue = value;
31298         var m = value % inc;
31299         if(m > 0){
31300             if(m > (inc/2)){
31301                 newValue = value + (inc-m);
31302             }else{
31303                 newValue = value - m;
31304             }
31305         }
31306         return Math.max(min, newValue);
31307     },
31308
31309     // private
31310     resizeElement : function(){
31311         var box = this.proxy.getBox();
31312         if(this.updateBox){
31313             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31314         }else{
31315             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31316         }
31317         this.updateChildSize();
31318         if(!this.dynamic){
31319             this.proxy.hide();
31320         }
31321         return box;
31322     },
31323
31324     // private
31325     constrain : function(v, diff, m, mx){
31326         if(v - diff < m){
31327             diff = v - m;
31328         }else if(v - diff > mx){
31329             diff = mx - v;
31330         }
31331         return diff;
31332     },
31333
31334     // private
31335     onMouseMove : function(e){
31336         
31337         if(this.enabled){
31338             try{// try catch so if something goes wrong the user doesn't get hung
31339
31340             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31341                 return;
31342             }
31343
31344             //var curXY = this.startPoint;
31345             var curSize = this.curSize || this.startBox;
31346             var x = this.startBox.x, y = this.startBox.y;
31347             var ox = x, oy = y;
31348             var w = curSize.width, h = curSize.height;
31349             var ow = w, oh = h;
31350             var mw = this.minWidth, mh = this.minHeight;
31351             var mxw = this.maxWidth, mxh = this.maxHeight;
31352             var wi = this.widthIncrement;
31353             var hi = this.heightIncrement;
31354
31355             var eventXY = e.getXY();
31356             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31357             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31358
31359             var pos = this.activeHandle.position;
31360
31361             switch(pos){
31362                 case "east":
31363                     w += diffX;
31364                     w = Math.min(Math.max(mw, w), mxw);
31365                     break;
31366              
31367                 case "south":
31368                     h += diffY;
31369                     h = Math.min(Math.max(mh, h), mxh);
31370                     break;
31371                 case "southeast":
31372                     w += diffX;
31373                     h += diffY;
31374                     w = Math.min(Math.max(mw, w), mxw);
31375                     h = Math.min(Math.max(mh, h), mxh);
31376                     break;
31377                 case "north":
31378                     diffY = this.constrain(h, diffY, mh, mxh);
31379                     y += diffY;
31380                     h -= diffY;
31381                     break;
31382                 case "hdrag":
31383                     
31384                     if (wi) {
31385                         var adiffX = Math.abs(diffX);
31386                         var sub = (adiffX % wi); // how much 
31387                         if (sub > (wi/2)) { // far enough to snap
31388                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31389                         } else {
31390                             // remove difference.. 
31391                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31392                         }
31393                     }
31394                     x += diffX;
31395                     x = Math.max(this.minX, x);
31396                     break;
31397                 case "west":
31398                     diffX = this.constrain(w, diffX, mw, mxw);
31399                     x += diffX;
31400                     w -= diffX;
31401                     break;
31402                 case "northeast":
31403                     w += diffX;
31404                     w = Math.min(Math.max(mw, w), mxw);
31405                     diffY = this.constrain(h, diffY, mh, mxh);
31406                     y += diffY;
31407                     h -= diffY;
31408                     break;
31409                 case "northwest":
31410                     diffX = this.constrain(w, diffX, mw, mxw);
31411                     diffY = this.constrain(h, diffY, mh, mxh);
31412                     y += diffY;
31413                     h -= diffY;
31414                     x += diffX;
31415                     w -= diffX;
31416                     break;
31417                case "southwest":
31418                     diffX = this.constrain(w, diffX, mw, mxw);
31419                     h += diffY;
31420                     h = Math.min(Math.max(mh, h), mxh);
31421                     x += diffX;
31422                     w -= diffX;
31423                     break;
31424             }
31425
31426             var sw = this.snap(w, wi, mw);
31427             var sh = this.snap(h, hi, mh);
31428             if(sw != w || sh != h){
31429                 switch(pos){
31430                     case "northeast":
31431                         y -= sh - h;
31432                     break;
31433                     case "north":
31434                         y -= sh - h;
31435                         break;
31436                     case "southwest":
31437                         x -= sw - w;
31438                     break;
31439                     case "west":
31440                         x -= sw - w;
31441                         break;
31442                     case "northwest":
31443                         x -= sw - w;
31444                         y -= sh - h;
31445                     break;
31446                 }
31447                 w = sw;
31448                 h = sh;
31449             }
31450
31451             if(this.preserveRatio){
31452                 switch(pos){
31453                     case "southeast":
31454                     case "east":
31455                         h = oh * (w/ow);
31456                         h = Math.min(Math.max(mh, h), mxh);
31457                         w = ow * (h/oh);
31458                        break;
31459                     case "south":
31460                         w = ow * (h/oh);
31461                         w = Math.min(Math.max(mw, w), mxw);
31462                         h = oh * (w/ow);
31463                         break;
31464                     case "northeast":
31465                         w = ow * (h/oh);
31466                         w = Math.min(Math.max(mw, w), mxw);
31467                         h = oh * (w/ow);
31468                     break;
31469                     case "north":
31470                         var tw = w;
31471                         w = ow * (h/oh);
31472                         w = Math.min(Math.max(mw, w), mxw);
31473                         h = oh * (w/ow);
31474                         x += (tw - w) / 2;
31475                         break;
31476                     case "southwest":
31477                         h = oh * (w/ow);
31478                         h = Math.min(Math.max(mh, h), mxh);
31479                         var tw = w;
31480                         w = ow * (h/oh);
31481                         x += tw - w;
31482                         break;
31483                     case "west":
31484                         var th = h;
31485                         h = oh * (w/ow);
31486                         h = Math.min(Math.max(mh, h), mxh);
31487                         y += (th - h) / 2;
31488                         var tw = w;
31489                         w = ow * (h/oh);
31490                         x += tw - w;
31491                        break;
31492                     case "northwest":
31493                         var tw = w;
31494                         var th = h;
31495                         h = oh * (w/ow);
31496                         h = Math.min(Math.max(mh, h), mxh);
31497                         w = ow * (h/oh);
31498                         y += th - h;
31499                         x += tw - w;
31500                        break;
31501
31502                 }
31503             }
31504             if (pos == 'hdrag') {
31505                 w = ow;
31506             }
31507             this.proxy.setBounds(x, y, w, h);
31508             if(this.dynamic){
31509                 this.resizeElement();
31510             }
31511             }catch(e){}
31512         }
31513         this.fireEvent("resizing", this, x, y, w, h, e);
31514     },
31515
31516     // private
31517     handleOver : function(){
31518         if(this.enabled){
31519             this.el.addClass("x-resizable-over");
31520         }
31521     },
31522
31523     // private
31524     handleOut : function(){
31525         if(!this.resizing){
31526             this.el.removeClass("x-resizable-over");
31527         }
31528     },
31529
31530     /**
31531      * Returns the element this component is bound to.
31532      * @return {Roo.Element}
31533      */
31534     getEl : function(){
31535         return this.el;
31536     },
31537
31538     /**
31539      * Returns the resizeChild element (or null).
31540      * @return {Roo.Element}
31541      */
31542     getResizeChild : function(){
31543         return this.resizeChild;
31544     },
31545     groupHandler : function()
31546     {
31547         
31548     },
31549     /**
31550      * Destroys this resizable. If the element was wrapped and
31551      * removeEl is not true then the element remains.
31552      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31553      */
31554     destroy : function(removeEl){
31555         this.proxy.remove();
31556         if(this.overlay){
31557             this.overlay.removeAllListeners();
31558             this.overlay.remove();
31559         }
31560         var ps = Roo.Resizable.positions;
31561         for(var k in ps){
31562             if(typeof ps[k] != "function" && this[ps[k]]){
31563                 var h = this[ps[k]];
31564                 h.el.removeAllListeners();
31565                 h.el.remove();
31566             }
31567         }
31568         if(removeEl){
31569             this.el.update("");
31570             this.el.remove();
31571         }
31572     }
31573 });
31574
31575 // private
31576 // hash to map config positions to true positions
31577 Roo.Resizable.positions = {
31578     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31579     hd: "hdrag"
31580 };
31581
31582 // private
31583 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31584     if(!this.tpl){
31585         // only initialize the template if resizable is used
31586         var tpl = Roo.DomHelper.createTemplate(
31587             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31588         );
31589         tpl.compile();
31590         Roo.Resizable.Handle.prototype.tpl = tpl;
31591     }
31592     this.position = pos;
31593     this.rz = rz;
31594     // show north drag fro topdra
31595     var handlepos = pos == 'hdrag' ? 'north' : pos;
31596     
31597     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31598     if (pos == 'hdrag') {
31599         this.el.setStyle('cursor', 'pointer');
31600     }
31601     this.el.unselectable();
31602     if(transparent){
31603         this.el.setOpacity(0);
31604     }
31605     this.el.on("mousedown", this.onMouseDown, this);
31606     if(!disableTrackOver){
31607         this.el.on("mouseover", this.onMouseOver, this);
31608         this.el.on("mouseout", this.onMouseOut, this);
31609     }
31610 };
31611
31612 // private
31613 Roo.Resizable.Handle.prototype = {
31614     afterResize : function(rz){
31615         Roo.log('after?');
31616         // do nothing
31617     },
31618     // private
31619     onMouseDown : function(e){
31620         this.rz.onMouseDown(this, e);
31621     },
31622     // private
31623     onMouseOver : function(e){
31624         this.rz.handleOver(this, e);
31625     },
31626     // private
31627     onMouseOut : function(e){
31628         this.rz.handleOut(this, e);
31629     }
31630 };/*
31631  * Based on:
31632  * Ext JS Library 1.1.1
31633  * Copyright(c) 2006-2007, Ext JS, LLC.
31634  *
31635  * Originally Released Under LGPL - original licence link has changed is not relivant.
31636  *
31637  * Fork - LGPL
31638  * <script type="text/javascript">
31639  */
31640
31641 /**
31642  * @class Roo.Editor
31643  * @extends Roo.Component
31644  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31645  * @constructor
31646  * Create a new Editor
31647  * @param {Roo.form.Field} field The Field object (or descendant)
31648  * @param {Object} config The config object
31649  */
31650 Roo.Editor = function(field, config){
31651     Roo.Editor.superclass.constructor.call(this, config);
31652     this.field = field;
31653     this.addEvents({
31654         /**
31655              * @event beforestartedit
31656              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31657              * false from the handler of this event.
31658              * @param {Editor} this
31659              * @param {Roo.Element} boundEl The underlying element bound to this editor
31660              * @param {Mixed} value The field value being set
31661              */
31662         "beforestartedit" : true,
31663         /**
31664              * @event startedit
31665              * Fires when this editor is displayed
31666              * @param {Roo.Element} boundEl The underlying element bound to this editor
31667              * @param {Mixed} value The starting field value
31668              */
31669         "startedit" : true,
31670         /**
31671              * @event beforecomplete
31672              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31673              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31674              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31675              * event will not fire since no edit actually occurred.
31676              * @param {Editor} this
31677              * @param {Mixed} value The current field value
31678              * @param {Mixed} startValue The original field value
31679              */
31680         "beforecomplete" : true,
31681         /**
31682              * @event complete
31683              * Fires after editing is complete and any changed value has been written to the underlying field.
31684              * @param {Editor} this
31685              * @param {Mixed} value The current field value
31686              * @param {Mixed} startValue The original field value
31687              */
31688         "complete" : true,
31689         /**
31690          * @event specialkey
31691          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31692          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31693          * @param {Roo.form.Field} this
31694          * @param {Roo.EventObject} e The event object
31695          */
31696         "specialkey" : true
31697     });
31698 };
31699
31700 Roo.extend(Roo.Editor, Roo.Component, {
31701     /**
31702      * @cfg {Boolean/String} autosize
31703      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31704      * or "height" to adopt the height only (defaults to false)
31705      */
31706     /**
31707      * @cfg {Boolean} revertInvalid
31708      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31709      * validation fails (defaults to true)
31710      */
31711     /**
31712      * @cfg {Boolean} ignoreNoChange
31713      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31714      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31715      * will never be ignored.
31716      */
31717     /**
31718      * @cfg {Boolean} hideEl
31719      * False to keep the bound element visible while the editor is displayed (defaults to true)
31720      */
31721     /**
31722      * @cfg {Mixed} value
31723      * The data value of the underlying field (defaults to "")
31724      */
31725     value : "",
31726     /**
31727      * @cfg {String} alignment
31728      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31729      */
31730     alignment: "c-c?",
31731     /**
31732      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31733      * for bottom-right shadow (defaults to "frame")
31734      */
31735     shadow : "frame",
31736     /**
31737      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31738      */
31739     constrain : false,
31740     /**
31741      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31742      */
31743     completeOnEnter : false,
31744     /**
31745      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31746      */
31747     cancelOnEsc : false,
31748     /**
31749      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31750      */
31751     updateEl : false,
31752
31753     // private
31754     onRender : function(ct, position){
31755         this.el = new Roo.Layer({
31756             shadow: this.shadow,
31757             cls: "x-editor",
31758             parentEl : ct,
31759             shim : this.shim,
31760             shadowOffset:4,
31761             id: this.id,
31762             constrain: this.constrain
31763         });
31764         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31765         if(this.field.msgTarget != 'title'){
31766             this.field.msgTarget = 'qtip';
31767         }
31768         this.field.render(this.el);
31769         if(Roo.isGecko){
31770             this.field.el.dom.setAttribute('autocomplete', 'off');
31771         }
31772         this.field.on("specialkey", this.onSpecialKey, this);
31773         if(this.swallowKeys){
31774             this.field.el.swallowEvent(['keydown','keypress']);
31775         }
31776         this.field.show();
31777         this.field.on("blur", this.onBlur, this);
31778         if(this.field.grow){
31779             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31780         }
31781     },
31782
31783     onSpecialKey : function(field, e)
31784     {
31785         //Roo.log('editor onSpecialKey');
31786         if(this.completeOnEnter && e.getKey() == e.ENTER){
31787             e.stopEvent();
31788             this.completeEdit();
31789             return;
31790         }
31791         // do not fire special key otherwise it might hide close the editor...
31792         if(e.getKey() == e.ENTER){    
31793             return;
31794         }
31795         if(this.cancelOnEsc && e.getKey() == e.ESC){
31796             this.cancelEdit();
31797             return;
31798         } 
31799         this.fireEvent('specialkey', field, e);
31800     
31801     },
31802
31803     /**
31804      * Starts the editing process and shows the editor.
31805      * @param {String/HTMLElement/Element} el The element to edit
31806      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31807       * to the innerHTML of el.
31808      */
31809     startEdit : function(el, value){
31810         if(this.editing){
31811             this.completeEdit();
31812         }
31813         this.boundEl = Roo.get(el);
31814         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31815         if(!this.rendered){
31816             this.render(this.parentEl || document.body);
31817         }
31818         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31819             return;
31820         }
31821         this.startValue = v;
31822         this.field.setValue(v);
31823         if(this.autoSize){
31824             var sz = this.boundEl.getSize();
31825             switch(this.autoSize){
31826                 case "width":
31827                 this.setSize(sz.width,  "");
31828                 break;
31829                 case "height":
31830                 this.setSize("",  sz.height);
31831                 break;
31832                 default:
31833                 this.setSize(sz.width,  sz.height);
31834             }
31835         }
31836         this.el.alignTo(this.boundEl, this.alignment);
31837         this.editing = true;
31838         if(Roo.QuickTips){
31839             Roo.QuickTips.disable();
31840         }
31841         this.show();
31842     },
31843
31844     /**
31845      * Sets the height and width of this editor.
31846      * @param {Number} width The new width
31847      * @param {Number} height The new height
31848      */
31849     setSize : function(w, h){
31850         this.field.setSize(w, h);
31851         if(this.el){
31852             this.el.sync();
31853         }
31854     },
31855
31856     /**
31857      * Realigns the editor to the bound field based on the current alignment config value.
31858      */
31859     realign : function(){
31860         this.el.alignTo(this.boundEl, this.alignment);
31861     },
31862
31863     /**
31864      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31865      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31866      */
31867     completeEdit : function(remainVisible){
31868         if(!this.editing){
31869             return;
31870         }
31871         var v = this.getValue();
31872         if(this.revertInvalid !== false && !this.field.isValid()){
31873             v = this.startValue;
31874             this.cancelEdit(true);
31875         }
31876         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31877             this.editing = false;
31878             this.hide();
31879             return;
31880         }
31881         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31882             this.editing = false;
31883             if(this.updateEl && this.boundEl){
31884                 this.boundEl.update(v);
31885             }
31886             if(remainVisible !== true){
31887                 this.hide();
31888             }
31889             this.fireEvent("complete", this, v, this.startValue);
31890         }
31891     },
31892
31893     // private
31894     onShow : function(){
31895         this.el.show();
31896         if(this.hideEl !== false){
31897             this.boundEl.hide();
31898         }
31899         this.field.show();
31900         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31901             this.fixIEFocus = true;
31902             this.deferredFocus.defer(50, this);
31903         }else{
31904             this.field.focus();
31905         }
31906         this.fireEvent("startedit", this.boundEl, this.startValue);
31907     },
31908
31909     deferredFocus : function(){
31910         if(this.editing){
31911             this.field.focus();
31912         }
31913     },
31914
31915     /**
31916      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31917      * reverted to the original starting value.
31918      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31919      * cancel (defaults to false)
31920      */
31921     cancelEdit : function(remainVisible){
31922         if(this.editing){
31923             this.setValue(this.startValue);
31924             if(remainVisible !== true){
31925                 this.hide();
31926             }
31927         }
31928     },
31929
31930     // private
31931     onBlur : function(){
31932         if(this.allowBlur !== true && this.editing){
31933             this.completeEdit();
31934         }
31935     },
31936
31937     // private
31938     onHide : function(){
31939         if(this.editing){
31940             this.completeEdit();
31941             return;
31942         }
31943         this.field.blur();
31944         if(this.field.collapse){
31945             this.field.collapse();
31946         }
31947         this.el.hide();
31948         if(this.hideEl !== false){
31949             this.boundEl.show();
31950         }
31951         if(Roo.QuickTips){
31952             Roo.QuickTips.enable();
31953         }
31954     },
31955
31956     /**
31957      * Sets the data value of the editor
31958      * @param {Mixed} value Any valid value supported by the underlying field
31959      */
31960     setValue : function(v){
31961         this.field.setValue(v);
31962     },
31963
31964     /**
31965      * Gets the data value of the editor
31966      * @return {Mixed} The data value
31967      */
31968     getValue : function(){
31969         return this.field.getValue();
31970     }
31971 });/*
31972  * Based on:
31973  * Ext JS Library 1.1.1
31974  * Copyright(c) 2006-2007, Ext JS, LLC.
31975  *
31976  * Originally Released Under LGPL - original licence link has changed is not relivant.
31977  *
31978  * Fork - LGPL
31979  * <script type="text/javascript">
31980  */
31981  
31982 /**
31983  * @class Roo.BasicDialog
31984  * @extends Roo.util.Observable
31985  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31986  * <pre><code>
31987 var dlg = new Roo.BasicDialog("my-dlg", {
31988     height: 200,
31989     width: 300,
31990     minHeight: 100,
31991     minWidth: 150,
31992     modal: true,
31993     proxyDrag: true,
31994     shadow: true
31995 });
31996 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31997 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31998 dlg.addButton('Cancel', dlg.hide, dlg);
31999 dlg.show();
32000 </code></pre>
32001   <b>A Dialog should always be a direct child of the body element.</b>
32002  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32003  * @cfg {String} title Default text to display in the title bar (defaults to null)
32004  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32005  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32006  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32007  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32008  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32009  * (defaults to null with no animation)
32010  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32011  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32012  * property for valid values (defaults to 'all')
32013  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32014  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32015  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32016  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32017  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32018  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32019  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32020  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32021  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32022  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32023  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32024  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32025  * draggable = true (defaults to false)
32026  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32027  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32028  * shadow (defaults to false)
32029  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32030  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32031  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32032  * @cfg {Array} buttons Array of buttons
32033  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32034  * @constructor
32035  * Create a new BasicDialog.
32036  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32037  * @param {Object} config Configuration options
32038  */
32039 Roo.BasicDialog = function(el, config){
32040     this.el = Roo.get(el);
32041     var dh = Roo.DomHelper;
32042     if(!this.el && config && config.autoCreate){
32043         if(typeof config.autoCreate == "object"){
32044             if(!config.autoCreate.id){
32045                 config.autoCreate.id = el;
32046             }
32047             this.el = dh.append(document.body,
32048                         config.autoCreate, true);
32049         }else{
32050             this.el = dh.append(document.body,
32051                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32052         }
32053     }
32054     el = this.el;
32055     el.setDisplayed(true);
32056     el.hide = this.hideAction;
32057     this.id = el.id;
32058     el.addClass("x-dlg");
32059
32060     Roo.apply(this, config);
32061
32062     this.proxy = el.createProxy("x-dlg-proxy");
32063     this.proxy.hide = this.hideAction;
32064     this.proxy.setOpacity(.5);
32065     this.proxy.hide();
32066
32067     if(config.width){
32068         el.setWidth(config.width);
32069     }
32070     if(config.height){
32071         el.setHeight(config.height);
32072     }
32073     this.size = el.getSize();
32074     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32075         this.xy = [config.x,config.y];
32076     }else{
32077         this.xy = el.getCenterXY(true);
32078     }
32079     /** The header element @type Roo.Element */
32080     this.header = el.child("> .x-dlg-hd");
32081     /** The body element @type Roo.Element */
32082     this.body = el.child("> .x-dlg-bd");
32083     /** The footer element @type Roo.Element */
32084     this.footer = el.child("> .x-dlg-ft");
32085
32086     if(!this.header){
32087         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32088     }
32089     if(!this.body){
32090         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32091     }
32092
32093     this.header.unselectable();
32094     if(this.title){
32095         this.header.update(this.title);
32096     }
32097     // this element allows the dialog to be focused for keyboard event
32098     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32099     this.focusEl.swallowEvent("click", true);
32100
32101     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32102
32103     // wrap the body and footer for special rendering
32104     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32105     if(this.footer){
32106         this.bwrap.dom.appendChild(this.footer.dom);
32107     }
32108
32109     this.bg = this.el.createChild({
32110         tag: "div", cls:"x-dlg-bg",
32111         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32112     });
32113     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32114
32115
32116     if(this.autoScroll !== false && !this.autoTabs){
32117         this.body.setStyle("overflow", "auto");
32118     }
32119
32120     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32121
32122     if(this.closable !== false){
32123         this.el.addClass("x-dlg-closable");
32124         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32125         this.close.on("click", this.closeClick, this);
32126         this.close.addClassOnOver("x-dlg-close-over");
32127     }
32128     if(this.collapsible !== false){
32129         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32130         this.collapseBtn.on("click", this.collapseClick, this);
32131         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32132         this.header.on("dblclick", this.collapseClick, this);
32133     }
32134     if(this.resizable !== false){
32135         this.el.addClass("x-dlg-resizable");
32136         this.resizer = new Roo.Resizable(el, {
32137             minWidth: this.minWidth || 80,
32138             minHeight:this.minHeight || 80,
32139             handles: this.resizeHandles || "all",
32140             pinned: true
32141         });
32142         this.resizer.on("beforeresize", this.beforeResize, this);
32143         this.resizer.on("resize", this.onResize, this);
32144     }
32145     if(this.draggable !== false){
32146         el.addClass("x-dlg-draggable");
32147         if (!this.proxyDrag) {
32148             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32149         }
32150         else {
32151             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32152         }
32153         dd.setHandleElId(this.header.id);
32154         dd.endDrag = this.endMove.createDelegate(this);
32155         dd.startDrag = this.startMove.createDelegate(this);
32156         dd.onDrag = this.onDrag.createDelegate(this);
32157         dd.scroll = false;
32158         this.dd = dd;
32159     }
32160     if(this.modal){
32161         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32162         this.mask.enableDisplayMode("block");
32163         this.mask.hide();
32164         this.el.addClass("x-dlg-modal");
32165     }
32166     if(this.shadow){
32167         this.shadow = new Roo.Shadow({
32168             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32169             offset : this.shadowOffset
32170         });
32171     }else{
32172         this.shadowOffset = 0;
32173     }
32174     if(Roo.useShims && this.shim !== false){
32175         this.shim = this.el.createShim();
32176         this.shim.hide = this.hideAction;
32177         this.shim.hide();
32178     }else{
32179         this.shim = false;
32180     }
32181     if(this.autoTabs){
32182         this.initTabs();
32183     }
32184     if (this.buttons) { 
32185         var bts= this.buttons;
32186         this.buttons = [];
32187         Roo.each(bts, function(b) {
32188             this.addButton(b);
32189         }, this);
32190     }
32191     
32192     
32193     this.addEvents({
32194         /**
32195          * @event keydown
32196          * Fires when a key is pressed
32197          * @param {Roo.BasicDialog} this
32198          * @param {Roo.EventObject} e
32199          */
32200         "keydown" : true,
32201         /**
32202          * @event move
32203          * Fires when this dialog is moved by the user.
32204          * @param {Roo.BasicDialog} this
32205          * @param {Number} x The new page X
32206          * @param {Number} y The new page Y
32207          */
32208         "move" : true,
32209         /**
32210          * @event resize
32211          * Fires when this dialog is resized by the user.
32212          * @param {Roo.BasicDialog} this
32213          * @param {Number} width The new width
32214          * @param {Number} height The new height
32215          */
32216         "resize" : true,
32217         /**
32218          * @event beforehide
32219          * Fires before this dialog is hidden.
32220          * @param {Roo.BasicDialog} this
32221          */
32222         "beforehide" : true,
32223         /**
32224          * @event hide
32225          * Fires when this dialog is hidden.
32226          * @param {Roo.BasicDialog} this
32227          */
32228         "hide" : true,
32229         /**
32230          * @event beforeshow
32231          * Fires before this dialog is shown.
32232          * @param {Roo.BasicDialog} this
32233          */
32234         "beforeshow" : true,
32235         /**
32236          * @event show
32237          * Fires when this dialog is shown.
32238          * @param {Roo.BasicDialog} this
32239          */
32240         "show" : true
32241     });
32242     el.on("keydown", this.onKeyDown, this);
32243     el.on("mousedown", this.toFront, this);
32244     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32245     this.el.hide();
32246     Roo.DialogManager.register(this);
32247     Roo.BasicDialog.superclass.constructor.call(this);
32248 };
32249
32250 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32251     shadowOffset: Roo.isIE ? 6 : 5,
32252     minHeight: 80,
32253     minWidth: 200,
32254     minButtonWidth: 75,
32255     defaultButton: null,
32256     buttonAlign: "right",
32257     tabTag: 'div',
32258     firstShow: true,
32259
32260     /**
32261      * Sets the dialog title text
32262      * @param {String} text The title text to display
32263      * @return {Roo.BasicDialog} this
32264      */
32265     setTitle : function(text){
32266         this.header.update(text);
32267         return this;
32268     },
32269
32270     // private
32271     closeClick : function(){
32272         this.hide();
32273     },
32274
32275     // private
32276     collapseClick : function(){
32277         this[this.collapsed ? "expand" : "collapse"]();
32278     },
32279
32280     /**
32281      * Collapses the dialog to its minimized state (only the title bar is visible).
32282      * Equivalent to the user clicking the collapse dialog button.
32283      */
32284     collapse : function(){
32285         if(!this.collapsed){
32286             this.collapsed = true;
32287             this.el.addClass("x-dlg-collapsed");
32288             this.restoreHeight = this.el.getHeight();
32289             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32290         }
32291     },
32292
32293     /**
32294      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32295      * clicking the expand dialog button.
32296      */
32297     expand : function(){
32298         if(this.collapsed){
32299             this.collapsed = false;
32300             this.el.removeClass("x-dlg-collapsed");
32301             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32302         }
32303     },
32304
32305     /**
32306      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32307      * @return {Roo.TabPanel} The tabs component
32308      */
32309     initTabs : function(){
32310         var tabs = this.getTabs();
32311         while(tabs.getTab(0)){
32312             tabs.removeTab(0);
32313         }
32314         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32315             var dom = el.dom;
32316             tabs.addTab(Roo.id(dom), dom.title);
32317             dom.title = "";
32318         });
32319         tabs.activate(0);
32320         return tabs;
32321     },
32322
32323     // private
32324     beforeResize : function(){
32325         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32326     },
32327
32328     // private
32329     onResize : function(){
32330         this.refreshSize();
32331         this.syncBodyHeight();
32332         this.adjustAssets();
32333         this.focus();
32334         this.fireEvent("resize", this, this.size.width, this.size.height);
32335     },
32336
32337     // private
32338     onKeyDown : function(e){
32339         if(this.isVisible()){
32340             this.fireEvent("keydown", this, e);
32341         }
32342     },
32343
32344     /**
32345      * Resizes the dialog.
32346      * @param {Number} width
32347      * @param {Number} height
32348      * @return {Roo.BasicDialog} this
32349      */
32350     resizeTo : function(width, height){
32351         this.el.setSize(width, height);
32352         this.size = {width: width, height: height};
32353         this.syncBodyHeight();
32354         if(this.fixedcenter){
32355             this.center();
32356         }
32357         if(this.isVisible()){
32358             this.constrainXY();
32359             this.adjustAssets();
32360         }
32361         this.fireEvent("resize", this, width, height);
32362         return this;
32363     },
32364
32365
32366     /**
32367      * Resizes the dialog to fit the specified content size.
32368      * @param {Number} width
32369      * @param {Number} height
32370      * @return {Roo.BasicDialog} this
32371      */
32372     setContentSize : function(w, h){
32373         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32374         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32375         //if(!this.el.isBorderBox()){
32376             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32377             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32378         //}
32379         if(this.tabs){
32380             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32381             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32382         }
32383         this.resizeTo(w, h);
32384         return this;
32385     },
32386
32387     /**
32388      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32389      * executed in response to a particular key being pressed while the dialog is active.
32390      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32391      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32392      * @param {Function} fn The function to call
32393      * @param {Object} scope (optional) The scope of the function
32394      * @return {Roo.BasicDialog} this
32395      */
32396     addKeyListener : function(key, fn, scope){
32397         var keyCode, shift, ctrl, alt;
32398         if(typeof key == "object" && !(key instanceof Array)){
32399             keyCode = key["key"];
32400             shift = key["shift"];
32401             ctrl = key["ctrl"];
32402             alt = key["alt"];
32403         }else{
32404             keyCode = key;
32405         }
32406         var handler = function(dlg, e){
32407             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32408                 var k = e.getKey();
32409                 if(keyCode instanceof Array){
32410                     for(var i = 0, len = keyCode.length; i < len; i++){
32411                         if(keyCode[i] == k){
32412                           fn.call(scope || window, dlg, k, e);
32413                           return;
32414                         }
32415                     }
32416                 }else{
32417                     if(k == keyCode){
32418                         fn.call(scope || window, dlg, k, e);
32419                     }
32420                 }
32421             }
32422         };
32423         this.on("keydown", handler);
32424         return this;
32425     },
32426
32427     /**
32428      * Returns the TabPanel component (creates it if it doesn't exist).
32429      * Note: If you wish to simply check for the existence of tabs without creating them,
32430      * check for a null 'tabs' property.
32431      * @return {Roo.TabPanel} The tabs component
32432      */
32433     getTabs : function(){
32434         if(!this.tabs){
32435             this.el.addClass("x-dlg-auto-tabs");
32436             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32437             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32438         }
32439         return this.tabs;
32440     },
32441
32442     /**
32443      * Adds a button to the footer section of the dialog.
32444      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32445      * object or a valid Roo.DomHelper element config
32446      * @param {Function} handler The function called when the button is clicked
32447      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32448      * @return {Roo.Button} The new button
32449      */
32450     addButton : function(config, handler, scope){
32451         var dh = Roo.DomHelper;
32452         if(!this.footer){
32453             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32454         }
32455         if(!this.btnContainer){
32456             var tb = this.footer.createChild({
32457
32458                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32459                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32460             }, null, true);
32461             this.btnContainer = tb.firstChild.firstChild.firstChild;
32462         }
32463         var bconfig = {
32464             handler: handler,
32465             scope: scope,
32466             minWidth: this.minButtonWidth,
32467             hideParent:true
32468         };
32469         if(typeof config == "string"){
32470             bconfig.text = config;
32471         }else{
32472             if(config.tag){
32473                 bconfig.dhconfig = config;
32474             }else{
32475                 Roo.apply(bconfig, config);
32476             }
32477         }
32478         var fc = false;
32479         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32480             bconfig.position = Math.max(0, bconfig.position);
32481             fc = this.btnContainer.childNodes[bconfig.position];
32482         }
32483          
32484         var btn = new Roo.Button(
32485             fc ? 
32486                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32487                 : this.btnContainer.appendChild(document.createElement("td")),
32488             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32489             bconfig
32490         );
32491         this.syncBodyHeight();
32492         if(!this.buttons){
32493             /**
32494              * Array of all the buttons that have been added to this dialog via addButton
32495              * @type Array
32496              */
32497             this.buttons = [];
32498         }
32499         this.buttons.push(btn);
32500         return btn;
32501     },
32502
32503     /**
32504      * Sets the default button to be focused when the dialog is displayed.
32505      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32506      * @return {Roo.BasicDialog} this
32507      */
32508     setDefaultButton : function(btn){
32509         this.defaultButton = btn;
32510         return this;
32511     },
32512
32513     // private
32514     getHeaderFooterHeight : function(safe){
32515         var height = 0;
32516         if(this.header){
32517            height += this.header.getHeight();
32518         }
32519         if(this.footer){
32520            var fm = this.footer.getMargins();
32521             height += (this.footer.getHeight()+fm.top+fm.bottom);
32522         }
32523         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32524         height += this.centerBg.getPadding("tb");
32525         return height;
32526     },
32527
32528     // private
32529     syncBodyHeight : function()
32530     {
32531         var bd = this.body, // the text
32532             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32533             bw = this.bwrap;
32534         var height = this.size.height - this.getHeaderFooterHeight(false);
32535         bd.setHeight(height-bd.getMargins("tb"));
32536         var hh = this.header.getHeight();
32537         var h = this.size.height-hh;
32538         cb.setHeight(h);
32539         
32540         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32541         bw.setHeight(h-cb.getPadding("tb"));
32542         
32543         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32544         bd.setWidth(bw.getWidth(true));
32545         if(this.tabs){
32546             this.tabs.syncHeight();
32547             if(Roo.isIE){
32548                 this.tabs.el.repaint();
32549             }
32550         }
32551     },
32552
32553     /**
32554      * Restores the previous state of the dialog if Roo.state is configured.
32555      * @return {Roo.BasicDialog} this
32556      */
32557     restoreState : function(){
32558         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32559         if(box && box.width){
32560             this.xy = [box.x, box.y];
32561             this.resizeTo(box.width, box.height);
32562         }
32563         return this;
32564     },
32565
32566     // private
32567     beforeShow : function(){
32568         this.expand();
32569         if(this.fixedcenter){
32570             this.xy = this.el.getCenterXY(true);
32571         }
32572         if(this.modal){
32573             Roo.get(document.body).addClass("x-body-masked");
32574             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32575             this.mask.show();
32576         }
32577         this.constrainXY();
32578     },
32579
32580     // private
32581     animShow : function(){
32582         var b = Roo.get(this.animateTarget).getBox();
32583         this.proxy.setSize(b.width, b.height);
32584         this.proxy.setLocation(b.x, b.y);
32585         this.proxy.show();
32586         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32587                     true, .35, this.showEl.createDelegate(this));
32588     },
32589
32590     /**
32591      * Shows the dialog.
32592      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32593      * @return {Roo.BasicDialog} this
32594      */
32595     show : function(animateTarget){
32596         if (this.fireEvent("beforeshow", this) === false){
32597             return;
32598         }
32599         if(this.syncHeightBeforeShow){
32600             this.syncBodyHeight();
32601         }else if(this.firstShow){
32602             this.firstShow = false;
32603             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32604         }
32605         this.animateTarget = animateTarget || this.animateTarget;
32606         if(!this.el.isVisible()){
32607             this.beforeShow();
32608             if(this.animateTarget && Roo.get(this.animateTarget)){
32609                 this.animShow();
32610             }else{
32611                 this.showEl();
32612             }
32613         }
32614         return this;
32615     },
32616
32617     // private
32618     showEl : function(){
32619         this.proxy.hide();
32620         this.el.setXY(this.xy);
32621         this.el.show();
32622         this.adjustAssets(true);
32623         this.toFront();
32624         this.focus();
32625         // IE peekaboo bug - fix found by Dave Fenwick
32626         if(Roo.isIE){
32627             this.el.repaint();
32628         }
32629         this.fireEvent("show", this);
32630     },
32631
32632     /**
32633      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32634      * dialog itself will receive focus.
32635      */
32636     focus : function(){
32637         if(this.defaultButton){
32638             this.defaultButton.focus();
32639         }else{
32640             this.focusEl.focus();
32641         }
32642     },
32643
32644     // private
32645     constrainXY : function(){
32646         if(this.constraintoviewport !== false){
32647             if(!this.viewSize){
32648                 if(this.container){
32649                     var s = this.container.getSize();
32650                     this.viewSize = [s.width, s.height];
32651                 }else{
32652                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32653                 }
32654             }
32655             var s = Roo.get(this.container||document).getScroll();
32656
32657             var x = this.xy[0], y = this.xy[1];
32658             var w = this.size.width, h = this.size.height;
32659             var vw = this.viewSize[0], vh = this.viewSize[1];
32660             // only move it if it needs it
32661             var moved = false;
32662             // first validate right/bottom
32663             if(x + w > vw+s.left){
32664                 x = vw - w;
32665                 moved = true;
32666             }
32667             if(y + h > vh+s.top){
32668                 y = vh - h;
32669                 moved = true;
32670             }
32671             // then make sure top/left isn't negative
32672             if(x < s.left){
32673                 x = s.left;
32674                 moved = true;
32675             }
32676             if(y < s.top){
32677                 y = s.top;
32678                 moved = true;
32679             }
32680             if(moved){
32681                 // cache xy
32682                 this.xy = [x, y];
32683                 if(this.isVisible()){
32684                     this.el.setLocation(x, y);
32685                     this.adjustAssets();
32686                 }
32687             }
32688         }
32689     },
32690
32691     // private
32692     onDrag : function(){
32693         if(!this.proxyDrag){
32694             this.xy = this.el.getXY();
32695             this.adjustAssets();
32696         }
32697     },
32698
32699     // private
32700     adjustAssets : function(doShow){
32701         var x = this.xy[0], y = this.xy[1];
32702         var w = this.size.width, h = this.size.height;
32703         if(doShow === true){
32704             if(this.shadow){
32705                 this.shadow.show(this.el);
32706             }
32707             if(this.shim){
32708                 this.shim.show();
32709             }
32710         }
32711         if(this.shadow && this.shadow.isVisible()){
32712             this.shadow.show(this.el);
32713         }
32714         if(this.shim && this.shim.isVisible()){
32715             this.shim.setBounds(x, y, w, h);
32716         }
32717     },
32718
32719     // private
32720     adjustViewport : function(w, h){
32721         if(!w || !h){
32722             w = Roo.lib.Dom.getViewWidth();
32723             h = Roo.lib.Dom.getViewHeight();
32724         }
32725         // cache the size
32726         this.viewSize = [w, h];
32727         if(this.modal && this.mask.isVisible()){
32728             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32729             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32730         }
32731         if(this.isVisible()){
32732             this.constrainXY();
32733         }
32734     },
32735
32736     /**
32737      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32738      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32739      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32740      */
32741     destroy : function(removeEl){
32742         if(this.isVisible()){
32743             this.animateTarget = null;
32744             this.hide();
32745         }
32746         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32747         if(this.tabs){
32748             this.tabs.destroy(removeEl);
32749         }
32750         Roo.destroy(
32751              this.shim,
32752              this.proxy,
32753              this.resizer,
32754              this.close,
32755              this.mask
32756         );
32757         if(this.dd){
32758             this.dd.unreg();
32759         }
32760         if(this.buttons){
32761            for(var i = 0, len = this.buttons.length; i < len; i++){
32762                this.buttons[i].destroy();
32763            }
32764         }
32765         this.el.removeAllListeners();
32766         if(removeEl === true){
32767             this.el.update("");
32768             this.el.remove();
32769         }
32770         Roo.DialogManager.unregister(this);
32771     },
32772
32773     // private
32774     startMove : function(){
32775         if(this.proxyDrag){
32776             this.proxy.show();
32777         }
32778         if(this.constraintoviewport !== false){
32779             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32780         }
32781     },
32782
32783     // private
32784     endMove : function(){
32785         if(!this.proxyDrag){
32786             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32787         }else{
32788             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32789             this.proxy.hide();
32790         }
32791         this.refreshSize();
32792         this.adjustAssets();
32793         this.focus();
32794         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32795     },
32796
32797     /**
32798      * Brings this dialog to the front of any other visible dialogs
32799      * @return {Roo.BasicDialog} this
32800      */
32801     toFront : function(){
32802         Roo.DialogManager.bringToFront(this);
32803         return this;
32804     },
32805
32806     /**
32807      * Sends this dialog to the back (under) of any other visible dialogs
32808      * @return {Roo.BasicDialog} this
32809      */
32810     toBack : function(){
32811         Roo.DialogManager.sendToBack(this);
32812         return this;
32813     },
32814
32815     /**
32816      * Centers this dialog in the viewport
32817      * @return {Roo.BasicDialog} this
32818      */
32819     center : function(){
32820         var xy = this.el.getCenterXY(true);
32821         this.moveTo(xy[0], xy[1]);
32822         return this;
32823     },
32824
32825     /**
32826      * Moves the dialog's top-left corner to the specified point
32827      * @param {Number} x
32828      * @param {Number} y
32829      * @return {Roo.BasicDialog} this
32830      */
32831     moveTo : function(x, y){
32832         this.xy = [x,y];
32833         if(this.isVisible()){
32834             this.el.setXY(this.xy);
32835             this.adjustAssets();
32836         }
32837         return this;
32838     },
32839
32840     /**
32841      * Aligns the dialog to the specified element
32842      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32843      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32844      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32845      * @return {Roo.BasicDialog} this
32846      */
32847     alignTo : function(element, position, offsets){
32848         this.xy = this.el.getAlignToXY(element, position, offsets);
32849         if(this.isVisible()){
32850             this.el.setXY(this.xy);
32851             this.adjustAssets();
32852         }
32853         return this;
32854     },
32855
32856     /**
32857      * Anchors an element to another element and realigns it when the window is resized.
32858      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32859      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32860      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32861      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32862      * is a number, it is used as the buffer delay (defaults to 50ms).
32863      * @return {Roo.BasicDialog} this
32864      */
32865     anchorTo : function(el, alignment, offsets, monitorScroll){
32866         var action = function(){
32867             this.alignTo(el, alignment, offsets);
32868         };
32869         Roo.EventManager.onWindowResize(action, this);
32870         var tm = typeof monitorScroll;
32871         if(tm != 'undefined'){
32872             Roo.EventManager.on(window, 'scroll', action, this,
32873                 {buffer: tm == 'number' ? monitorScroll : 50});
32874         }
32875         action.call(this);
32876         return this;
32877     },
32878
32879     /**
32880      * Returns true if the dialog is visible
32881      * @return {Boolean}
32882      */
32883     isVisible : function(){
32884         return this.el.isVisible();
32885     },
32886
32887     // private
32888     animHide : function(callback){
32889         var b = Roo.get(this.animateTarget).getBox();
32890         this.proxy.show();
32891         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32892         this.el.hide();
32893         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32894                     this.hideEl.createDelegate(this, [callback]));
32895     },
32896
32897     /**
32898      * Hides the dialog.
32899      * @param {Function} callback (optional) Function to call when the dialog is hidden
32900      * @return {Roo.BasicDialog} this
32901      */
32902     hide : function(callback){
32903         if (this.fireEvent("beforehide", this) === false){
32904             return;
32905         }
32906         if(this.shadow){
32907             this.shadow.hide();
32908         }
32909         if(this.shim) {
32910           this.shim.hide();
32911         }
32912         // sometimes animateTarget seems to get set.. causing problems...
32913         // this just double checks..
32914         if(this.animateTarget && Roo.get(this.animateTarget)) {
32915            this.animHide(callback);
32916         }else{
32917             this.el.hide();
32918             this.hideEl(callback);
32919         }
32920         return this;
32921     },
32922
32923     // private
32924     hideEl : function(callback){
32925         this.proxy.hide();
32926         if(this.modal){
32927             this.mask.hide();
32928             Roo.get(document.body).removeClass("x-body-masked");
32929         }
32930         this.fireEvent("hide", this);
32931         if(typeof callback == "function"){
32932             callback();
32933         }
32934     },
32935
32936     // private
32937     hideAction : function(){
32938         this.setLeft("-10000px");
32939         this.setTop("-10000px");
32940         this.setStyle("visibility", "hidden");
32941     },
32942
32943     // private
32944     refreshSize : function(){
32945         this.size = this.el.getSize();
32946         this.xy = this.el.getXY();
32947         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32948     },
32949
32950     // private
32951     // z-index is managed by the DialogManager and may be overwritten at any time
32952     setZIndex : function(index){
32953         if(this.modal){
32954             this.mask.setStyle("z-index", index);
32955         }
32956         if(this.shim){
32957             this.shim.setStyle("z-index", ++index);
32958         }
32959         if(this.shadow){
32960             this.shadow.setZIndex(++index);
32961         }
32962         this.el.setStyle("z-index", ++index);
32963         if(this.proxy){
32964             this.proxy.setStyle("z-index", ++index);
32965         }
32966         if(this.resizer){
32967             this.resizer.proxy.setStyle("z-index", ++index);
32968         }
32969
32970         this.lastZIndex = index;
32971     },
32972
32973     /**
32974      * Returns the element for this dialog
32975      * @return {Roo.Element} The underlying dialog Element
32976      */
32977     getEl : function(){
32978         return this.el;
32979     }
32980 });
32981
32982 /**
32983  * @class Roo.DialogManager
32984  * Provides global access to BasicDialogs that have been created and
32985  * support for z-indexing (layering) multiple open dialogs.
32986  */
32987 Roo.DialogManager = function(){
32988     var list = {};
32989     var accessList = [];
32990     var front = null;
32991
32992     // private
32993     var sortDialogs = function(d1, d2){
32994         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32995     };
32996
32997     // private
32998     var orderDialogs = function(){
32999         accessList.sort(sortDialogs);
33000         var seed = Roo.DialogManager.zseed;
33001         for(var i = 0, len = accessList.length; i < len; i++){
33002             var dlg = accessList[i];
33003             if(dlg){
33004                 dlg.setZIndex(seed + (i*10));
33005             }
33006         }
33007     };
33008
33009     return {
33010         /**
33011          * The starting z-index for BasicDialogs (defaults to 9000)
33012          * @type Number The z-index value
33013          */
33014         zseed : 9000,
33015
33016         // private
33017         register : function(dlg){
33018             list[dlg.id] = dlg;
33019             accessList.push(dlg);
33020         },
33021
33022         // private
33023         unregister : function(dlg){
33024             delete list[dlg.id];
33025             var i=0;
33026             var len=0;
33027             if(!accessList.indexOf){
33028                 for(  i = 0, len = accessList.length; i < len; i++){
33029                     if(accessList[i] == dlg){
33030                         accessList.splice(i, 1);
33031                         return;
33032                     }
33033                 }
33034             }else{
33035                  i = accessList.indexOf(dlg);
33036                 if(i != -1){
33037                     accessList.splice(i, 1);
33038                 }
33039             }
33040         },
33041
33042         /**
33043          * Gets a registered dialog by id
33044          * @param {String/Object} id The id of the dialog or a dialog
33045          * @return {Roo.BasicDialog} this
33046          */
33047         get : function(id){
33048             return typeof id == "object" ? id : list[id];
33049         },
33050
33051         /**
33052          * Brings the specified dialog to the front
33053          * @param {String/Object} dlg The id of the dialog or a dialog
33054          * @return {Roo.BasicDialog} this
33055          */
33056         bringToFront : function(dlg){
33057             dlg = this.get(dlg);
33058             if(dlg != front){
33059                 front = dlg;
33060                 dlg._lastAccess = new Date().getTime();
33061                 orderDialogs();
33062             }
33063             return dlg;
33064         },
33065
33066         /**
33067          * Sends the specified dialog to the back
33068          * @param {String/Object} dlg The id of the dialog or a dialog
33069          * @return {Roo.BasicDialog} this
33070          */
33071         sendToBack : function(dlg){
33072             dlg = this.get(dlg);
33073             dlg._lastAccess = -(new Date().getTime());
33074             orderDialogs();
33075             return dlg;
33076         },
33077
33078         /**
33079          * Hides all dialogs
33080          */
33081         hideAll : function(){
33082             for(var id in list){
33083                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33084                     list[id].hide();
33085                 }
33086             }
33087         }
33088     };
33089 }();
33090
33091 /**
33092  * @class Roo.LayoutDialog
33093  * @extends Roo.BasicDialog
33094  * Dialog which provides adjustments for working with a layout in a Dialog.
33095  * Add your necessary layout config options to the dialog's config.<br>
33096  * Example usage (including a nested layout):
33097  * <pre><code>
33098 if(!dialog){
33099     dialog = new Roo.LayoutDialog("download-dlg", {
33100         modal: true,
33101         width:600,
33102         height:450,
33103         shadow:true,
33104         minWidth:500,
33105         minHeight:350,
33106         autoTabs:true,
33107         proxyDrag:true,
33108         // layout config merges with the dialog config
33109         center:{
33110             tabPosition: "top",
33111             alwaysShowTabs: true
33112         }
33113     });
33114     dialog.addKeyListener(27, dialog.hide, dialog);
33115     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33116     dialog.addButton("Build It!", this.getDownload, this);
33117
33118     // we can even add nested layouts
33119     var innerLayout = new Roo.BorderLayout("dl-inner", {
33120         east: {
33121             initialSize: 200,
33122             autoScroll:true,
33123             split:true
33124         },
33125         center: {
33126             autoScroll:true
33127         }
33128     });
33129     innerLayout.beginUpdate();
33130     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33131     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33132     innerLayout.endUpdate(true);
33133
33134     var layout = dialog.getLayout();
33135     layout.beginUpdate();
33136     layout.add("center", new Roo.ContentPanel("standard-panel",
33137                         {title: "Download the Source", fitToFrame:true}));
33138     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33139                {title: "Build your own roo.js"}));
33140     layout.getRegion("center").showPanel(sp);
33141     layout.endUpdate();
33142 }
33143 </code></pre>
33144     * @constructor
33145     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33146     * @param {Object} config configuration options
33147   */
33148 Roo.LayoutDialog = function(el, cfg){
33149     
33150     var config=  cfg;
33151     if (typeof(cfg) == 'undefined') {
33152         config = Roo.apply({}, el);
33153         // not sure why we use documentElement here.. - it should always be body.
33154         // IE7 borks horribly if we use documentElement.
33155         // webkit also does not like documentElement - it creates a body element...
33156         el = Roo.get( document.body || document.documentElement ).createChild();
33157         //config.autoCreate = true;
33158     }
33159     
33160     
33161     config.autoTabs = false;
33162     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33163     this.body.setStyle({overflow:"hidden", position:"relative"});
33164     this.layout = new Roo.BorderLayout(this.body.dom, config);
33165     this.layout.monitorWindowResize = false;
33166     this.el.addClass("x-dlg-auto-layout");
33167     // fix case when center region overwrites center function
33168     this.center = Roo.BasicDialog.prototype.center;
33169     this.on("show", this.layout.layout, this.layout, true);
33170     if (config.items) {
33171         var xitems = config.items;
33172         delete config.items;
33173         Roo.each(xitems, this.addxtype, this);
33174     }
33175     
33176     
33177 };
33178 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33179     /**
33180      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33181      * @deprecated
33182      */
33183     endUpdate : function(){
33184         this.layout.endUpdate();
33185     },
33186
33187     /**
33188      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33189      *  @deprecated
33190      */
33191     beginUpdate : function(){
33192         this.layout.beginUpdate();
33193     },
33194
33195     /**
33196      * Get the BorderLayout for this dialog
33197      * @return {Roo.BorderLayout}
33198      */
33199     getLayout : function(){
33200         return this.layout;
33201     },
33202
33203     showEl : function(){
33204         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33205         if(Roo.isIE7){
33206             this.layout.layout();
33207         }
33208     },
33209
33210     // private
33211     // Use the syncHeightBeforeShow config option to control this automatically
33212     syncBodyHeight : function(){
33213         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33214         if(this.layout){this.layout.layout();}
33215     },
33216     
33217       /**
33218      * Add an xtype element (actually adds to the layout.)
33219      * @return {Object} xdata xtype object data.
33220      */
33221     
33222     addxtype : function(c) {
33223         return this.layout.addxtype(c);
33224     }
33225 });/*
33226  * Based on:
33227  * Ext JS Library 1.1.1
33228  * Copyright(c) 2006-2007, Ext JS, LLC.
33229  *
33230  * Originally Released Under LGPL - original licence link has changed is not relivant.
33231  *
33232  * Fork - LGPL
33233  * <script type="text/javascript">
33234  */
33235  
33236 /**
33237  * @class Roo.MessageBox
33238  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33239  * Example usage:
33240  *<pre><code>
33241 // Basic alert:
33242 Roo.Msg.alert('Status', 'Changes saved successfully.');
33243
33244 // Prompt for user data:
33245 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33246     if (btn == 'ok'){
33247         // process text value...
33248     }
33249 });
33250
33251 // Show a dialog using config options:
33252 Roo.Msg.show({
33253    title:'Save Changes?',
33254    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33255    buttons: Roo.Msg.YESNOCANCEL,
33256    fn: processResult,
33257    animEl: 'elId'
33258 });
33259 </code></pre>
33260  * @singleton
33261  */
33262 Roo.MessageBox = function(){
33263     var dlg, opt, mask, waitTimer;
33264     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33265     var buttons, activeTextEl, bwidth;
33266
33267     // private
33268     var handleButton = function(button){
33269         dlg.hide();
33270         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33271     };
33272
33273     // private
33274     var handleHide = function(){
33275         if(opt && opt.cls){
33276             dlg.el.removeClass(opt.cls);
33277         }
33278         if(waitTimer){
33279             Roo.TaskMgr.stop(waitTimer);
33280             waitTimer = null;
33281         }
33282     };
33283
33284     // private
33285     var updateButtons = function(b){
33286         var width = 0;
33287         if(!b){
33288             buttons["ok"].hide();
33289             buttons["cancel"].hide();
33290             buttons["yes"].hide();
33291             buttons["no"].hide();
33292             dlg.footer.dom.style.display = 'none';
33293             return width;
33294         }
33295         dlg.footer.dom.style.display = '';
33296         for(var k in buttons){
33297             if(typeof buttons[k] != "function"){
33298                 if(b[k]){
33299                     buttons[k].show();
33300                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33301                     width += buttons[k].el.getWidth()+15;
33302                 }else{
33303                     buttons[k].hide();
33304                 }
33305             }
33306         }
33307         return width;
33308     };
33309
33310     // private
33311     var handleEsc = function(d, k, e){
33312         if(opt && opt.closable !== false){
33313             dlg.hide();
33314         }
33315         if(e){
33316             e.stopEvent();
33317         }
33318     };
33319
33320     return {
33321         /**
33322          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33323          * @return {Roo.BasicDialog} The BasicDialog element
33324          */
33325         getDialog : function(){
33326            if(!dlg){
33327                 dlg = new Roo.BasicDialog("x-msg-box", {
33328                     autoCreate : true,
33329                     shadow: true,
33330                     draggable: true,
33331                     resizable:false,
33332                     constraintoviewport:false,
33333                     fixedcenter:true,
33334                     collapsible : false,
33335                     shim:true,
33336                     modal: true,
33337                     width:400, height:100,
33338                     buttonAlign:"center",
33339                     closeClick : function(){
33340                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33341                             handleButton("no");
33342                         }else{
33343                             handleButton("cancel");
33344                         }
33345                     }
33346                 });
33347                 dlg.on("hide", handleHide);
33348                 mask = dlg.mask;
33349                 dlg.addKeyListener(27, handleEsc);
33350                 buttons = {};
33351                 var bt = this.buttonText;
33352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33356                 bodyEl = dlg.body.createChild({
33357
33358                     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>'
33359                 });
33360                 msgEl = bodyEl.dom.firstChild;
33361                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33362                 textboxEl.enableDisplayMode();
33363                 textboxEl.addKeyListener([10,13], function(){
33364                     if(dlg.isVisible() && opt && opt.buttons){
33365                         if(opt.buttons.ok){
33366                             handleButton("ok");
33367                         }else if(opt.buttons.yes){
33368                             handleButton("yes");
33369                         }
33370                     }
33371                 });
33372                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33373                 textareaEl.enableDisplayMode();
33374                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33375                 progressEl.enableDisplayMode();
33376                 var pf = progressEl.dom.firstChild;
33377                 if (pf) {
33378                     pp = Roo.get(pf.firstChild);
33379                     pp.setHeight(pf.offsetHeight);
33380                 }
33381                 
33382             }
33383             return dlg;
33384         },
33385
33386         /**
33387          * Updates the message box body text
33388          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33389          * the XHTML-compliant non-breaking space character '&amp;#160;')
33390          * @return {Roo.MessageBox} This message box
33391          */
33392         updateText : function(text){
33393             if(!dlg.isVisible() && !opt.width){
33394                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33395             }
33396             msgEl.innerHTML = text || '&#160;';
33397       
33398             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33399             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33400             var w = Math.max(
33401                     Math.min(opt.width || cw , this.maxWidth), 
33402                     Math.max(opt.minWidth || this.minWidth, bwidth)
33403             );
33404             if(opt.prompt){
33405                 activeTextEl.setWidth(w);
33406             }
33407             if(dlg.isVisible()){
33408                 dlg.fixedcenter = false;
33409             }
33410             // to big, make it scroll. = But as usual stupid IE does not support
33411             // !important..
33412             
33413             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33414                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33415                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33416             } else {
33417                 bodyEl.dom.style.height = '';
33418                 bodyEl.dom.style.overflowY = '';
33419             }
33420             if (cw > w) {
33421                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33422             } else {
33423                 bodyEl.dom.style.overflowX = '';
33424             }
33425             
33426             dlg.setContentSize(w, bodyEl.getHeight());
33427             if(dlg.isVisible()){
33428                 dlg.fixedcenter = true;
33429             }
33430             return this;
33431         },
33432
33433         /**
33434          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33435          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33436          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33437          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33438          * @return {Roo.MessageBox} This message box
33439          */
33440         updateProgress : function(value, text){
33441             if(text){
33442                 this.updateText(text);
33443             }
33444             if (pp) { // weird bug on my firefox - for some reason this is not defined
33445                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33446             }
33447             return this;
33448         },        
33449
33450         /**
33451          * Returns true if the message box is currently displayed
33452          * @return {Boolean} True if the message box is visible, else false
33453          */
33454         isVisible : function(){
33455             return dlg && dlg.isVisible();  
33456         },
33457
33458         /**
33459          * Hides the message box if it is displayed
33460          */
33461         hide : function(){
33462             if(this.isVisible()){
33463                 dlg.hide();
33464             }  
33465         },
33466
33467         /**
33468          * Displays a new message box, or reinitializes an existing message box, based on the config options
33469          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33470          * The following config object properties are supported:
33471          * <pre>
33472 Property    Type             Description
33473 ----------  ---------------  ------------------------------------------------------------------------------------
33474 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33475                                    closes (defaults to undefined)
33476 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33477                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33478 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33479                                    progress and wait dialogs will ignore this property and always hide the
33480                                    close button as they can only be closed programmatically.
33481 cls               String           A custom CSS class to apply to the message box element
33482 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33483                                    displayed (defaults to 75)
33484 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33485                                    function will be btn (the name of the button that was clicked, if applicable,
33486                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33487                                    Progress and wait dialogs will ignore this option since they do not respond to
33488                                    user actions and can only be closed programmatically, so any required function
33489                                    should be called by the same code after it closes the dialog.
33490 icon              String           A CSS class that provides a background image to be used as an icon for
33491                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33492 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33493 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33494 modal             Boolean          False to allow user interaction with the page while the message box is
33495                                    displayed (defaults to true)
33496 msg               String           A string that will replace the existing message box body text (defaults
33497                                    to the XHTML-compliant non-breaking space character '&#160;')
33498 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33499 progress          Boolean          True to display a progress bar (defaults to false)
33500 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33501 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33502 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33503 title             String           The title text
33504 value             String           The string value to set into the active textbox element if displayed
33505 wait              Boolean          True to display a progress bar (defaults to false)
33506 width             Number           The width of the dialog in pixels
33507 </pre>
33508          *
33509          * Example usage:
33510          * <pre><code>
33511 Roo.Msg.show({
33512    title: 'Address',
33513    msg: 'Please enter your address:',
33514    width: 300,
33515    buttons: Roo.MessageBox.OKCANCEL,
33516    multiline: true,
33517    fn: saveAddress,
33518    animEl: 'addAddressBtn'
33519 });
33520 </code></pre>
33521          * @param {Object} config Configuration options
33522          * @return {Roo.MessageBox} This message box
33523          */
33524         show : function(options)
33525         {
33526             
33527             // this causes nightmares if you show one dialog after another
33528             // especially on callbacks..
33529              
33530             if(this.isVisible()){
33531                 
33532                 this.hide();
33533                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33534                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33535                 Roo.log("New Dialog Message:" +  options.msg )
33536                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33537                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33538                 
33539             }
33540             var d = this.getDialog();
33541             opt = options;
33542             d.setTitle(opt.title || "&#160;");
33543             d.close.setDisplayed(opt.closable !== false);
33544             activeTextEl = textboxEl;
33545             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33546             if(opt.prompt){
33547                 if(opt.multiline){
33548                     textboxEl.hide();
33549                     textareaEl.show();
33550                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33551                         opt.multiline : this.defaultTextHeight);
33552                     activeTextEl = textareaEl;
33553                 }else{
33554                     textboxEl.show();
33555                     textareaEl.hide();
33556                 }
33557             }else{
33558                 textboxEl.hide();
33559                 textareaEl.hide();
33560             }
33561             progressEl.setDisplayed(opt.progress === true);
33562             this.updateProgress(0);
33563             activeTextEl.dom.value = opt.value || "";
33564             if(opt.prompt){
33565                 dlg.setDefaultButton(activeTextEl);
33566             }else{
33567                 var bs = opt.buttons;
33568                 var db = null;
33569                 if(bs && bs.ok){
33570                     db = buttons["ok"];
33571                 }else if(bs && bs.yes){
33572                     db = buttons["yes"];
33573                 }
33574                 dlg.setDefaultButton(db);
33575             }
33576             bwidth = updateButtons(opt.buttons);
33577             this.updateText(opt.msg);
33578             if(opt.cls){
33579                 d.el.addClass(opt.cls);
33580             }
33581             d.proxyDrag = opt.proxyDrag === true;
33582             d.modal = opt.modal !== false;
33583             d.mask = opt.modal !== false ? mask : false;
33584             if(!d.isVisible()){
33585                 // force it to the end of the z-index stack so it gets a cursor in FF
33586                 document.body.appendChild(dlg.el.dom);
33587                 d.animateTarget = null;
33588                 d.show(options.animEl);
33589             }
33590             return this;
33591         },
33592
33593         /**
33594          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33595          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33596          * and closing the message box when the process is complete.
33597          * @param {String} title The title bar text
33598          * @param {String} msg The message box body text
33599          * @return {Roo.MessageBox} This message box
33600          */
33601         progress : function(title, msg){
33602             this.show({
33603                 title : title,
33604                 msg : msg,
33605                 buttons: false,
33606                 progress:true,
33607                 closable:false,
33608                 minWidth: this.minProgressWidth,
33609                 modal : true
33610             });
33611             return this;
33612         },
33613
33614         /**
33615          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33616          * If a callback function is passed it will be called after the user clicks the button, and the
33617          * id of the button that was clicked will be passed as the only parameter to the callback
33618          * (could also be the top-right close button).
33619          * @param {String} title The title bar text
33620          * @param {String} msg The message box body text
33621          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33622          * @param {Object} scope (optional) The scope of the callback function
33623          * @return {Roo.MessageBox} This message box
33624          */
33625         alert : function(title, msg, fn, scope){
33626             this.show({
33627                 title : title,
33628                 msg : msg,
33629                 buttons: this.OK,
33630                 fn: fn,
33631                 scope : scope,
33632                 modal : true
33633             });
33634             return this;
33635         },
33636
33637         /**
33638          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33639          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33640          * You are responsible for closing the message box when the process is complete.
33641          * @param {String} msg The message box body text
33642          * @param {String} title (optional) The title bar text
33643          * @return {Roo.MessageBox} This message box
33644          */
33645         wait : function(msg, title){
33646             this.show({
33647                 title : title,
33648                 msg : msg,
33649                 buttons: false,
33650                 closable:false,
33651                 progress:true,
33652                 modal:true,
33653                 width:300,
33654                 wait:true
33655             });
33656             waitTimer = Roo.TaskMgr.start({
33657                 run: function(i){
33658                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33659                 },
33660                 interval: 1000
33661             });
33662             return this;
33663         },
33664
33665         /**
33666          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33667          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33668          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33669          * @param {String} title The title bar text
33670          * @param {String} msg The message box body text
33671          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33672          * @param {Object} scope (optional) The scope of the callback function
33673          * @return {Roo.MessageBox} This message box
33674          */
33675         confirm : function(title, msg, fn, scope){
33676             this.show({
33677                 title : title,
33678                 msg : msg,
33679                 buttons: this.YESNO,
33680                 fn: fn,
33681                 scope : scope,
33682                 modal : true
33683             });
33684             return this;
33685         },
33686
33687         /**
33688          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33689          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33690          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33691          * (could also be the top-right close button) and the text that was entered will be passed as the two
33692          * parameters to the callback.
33693          * @param {String} title The title bar text
33694          * @param {String} msg The message box body text
33695          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33696          * @param {Object} scope (optional) The scope of the callback function
33697          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33698          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33699          * @return {Roo.MessageBox} This message box
33700          */
33701         prompt : function(title, msg, fn, scope, multiline){
33702             this.show({
33703                 title : title,
33704                 msg : msg,
33705                 buttons: this.OKCANCEL,
33706                 fn: fn,
33707                 minWidth:250,
33708                 scope : scope,
33709                 prompt:true,
33710                 multiline: multiline,
33711                 modal : true
33712             });
33713             return this;
33714         },
33715
33716         /**
33717          * Button config that displays a single OK button
33718          * @type Object
33719          */
33720         OK : {ok:true},
33721         /**
33722          * Button config that displays Yes and No buttons
33723          * @type Object
33724          */
33725         YESNO : {yes:true, no:true},
33726         /**
33727          * Button config that displays OK and Cancel buttons
33728          * @type Object
33729          */
33730         OKCANCEL : {ok:true, cancel:true},
33731         /**
33732          * Button config that displays Yes, No and Cancel buttons
33733          * @type Object
33734          */
33735         YESNOCANCEL : {yes:true, no:true, cancel:true},
33736
33737         /**
33738          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33739          * @type Number
33740          */
33741         defaultTextHeight : 75,
33742         /**
33743          * The maximum width in pixels of the message box (defaults to 600)
33744          * @type Number
33745          */
33746         maxWidth : 600,
33747         /**
33748          * The minimum width in pixels of the message box (defaults to 100)
33749          * @type Number
33750          */
33751         minWidth : 100,
33752         /**
33753          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33754          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33755          * @type Number
33756          */
33757         minProgressWidth : 250,
33758         /**
33759          * An object containing the default button text strings that can be overriden for localized language support.
33760          * Supported properties are: ok, cancel, yes and no.
33761          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33762          * @type Object
33763          */
33764         buttonText : {
33765             ok : "OK",
33766             cancel : "Cancel",
33767             yes : "Yes",
33768             no : "No"
33769         }
33770     };
33771 }();
33772
33773 /**
33774  * Shorthand for {@link Roo.MessageBox}
33775  */
33776 Roo.Msg = Roo.MessageBox;/*
33777  * Based on:
33778  * Ext JS Library 1.1.1
33779  * Copyright(c) 2006-2007, Ext JS, LLC.
33780  *
33781  * Originally Released Under LGPL - original licence link has changed is not relivant.
33782  *
33783  * Fork - LGPL
33784  * <script type="text/javascript">
33785  */
33786 /**
33787  * @class Roo.QuickTips
33788  * Provides attractive and customizable tooltips for any element.
33789  * @singleton
33790  */
33791 Roo.QuickTips = function(){
33792     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33793     var ce, bd, xy, dd;
33794     var visible = false, disabled = true, inited = false;
33795     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33796     
33797     var onOver = function(e){
33798         if(disabled){
33799             return;
33800         }
33801         var t = e.getTarget();
33802         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33803             return;
33804         }
33805         if(ce && t == ce.el){
33806             clearTimeout(hideProc);
33807             return;
33808         }
33809         if(t && tagEls[t.id]){
33810             tagEls[t.id].el = t;
33811             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33812             return;
33813         }
33814         var ttp, et = Roo.fly(t);
33815         var ns = cfg.namespace;
33816         if(tm.interceptTitles && t.title){
33817             ttp = t.title;
33818             t.qtip = ttp;
33819             t.removeAttribute("title");
33820             e.preventDefault();
33821         }else{
33822             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33823         }
33824         if(ttp){
33825             showProc = show.defer(tm.showDelay, tm, [{
33826                 el: t, 
33827                 text: ttp.replace(/\\n/g,'<br/>'),
33828                 width: et.getAttributeNS(ns, cfg.width),
33829                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33830                 title: et.getAttributeNS(ns, cfg.title),
33831                     cls: et.getAttributeNS(ns, cfg.cls)
33832             }]);
33833         }
33834     };
33835     
33836     var onOut = function(e){
33837         clearTimeout(showProc);
33838         var t = e.getTarget();
33839         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33840             hideProc = setTimeout(hide, tm.hideDelay);
33841         }
33842     };
33843     
33844     var onMove = function(e){
33845         if(disabled){
33846             return;
33847         }
33848         xy = e.getXY();
33849         xy[1] += 18;
33850         if(tm.trackMouse && ce){
33851             el.setXY(xy);
33852         }
33853     };
33854     
33855     var onDown = function(e){
33856         clearTimeout(showProc);
33857         clearTimeout(hideProc);
33858         if(!e.within(el)){
33859             if(tm.hideOnClick){
33860                 hide();
33861                 tm.disable();
33862                 tm.enable.defer(100, tm);
33863             }
33864         }
33865     };
33866     
33867     var getPad = function(){
33868         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33869     };
33870
33871     var show = function(o){
33872         if(disabled){
33873             return;
33874         }
33875         clearTimeout(dismissProc);
33876         ce = o;
33877         if(removeCls){ // in case manually hidden
33878             el.removeClass(removeCls);
33879             removeCls = null;
33880         }
33881         if(ce.cls){
33882             el.addClass(ce.cls);
33883             removeCls = ce.cls;
33884         }
33885         if(ce.title){
33886             tipTitle.update(ce.title);
33887             tipTitle.show();
33888         }else{
33889             tipTitle.update('');
33890             tipTitle.hide();
33891         }
33892         el.dom.style.width  = tm.maxWidth+'px';
33893         //tipBody.dom.style.width = '';
33894         tipBodyText.update(o.text);
33895         var p = getPad(), w = ce.width;
33896         if(!w){
33897             var td = tipBodyText.dom;
33898             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33899             if(aw > tm.maxWidth){
33900                 w = tm.maxWidth;
33901             }else if(aw < tm.minWidth){
33902                 w = tm.minWidth;
33903             }else{
33904                 w = aw;
33905             }
33906         }
33907         //tipBody.setWidth(w);
33908         el.setWidth(parseInt(w, 10) + p);
33909         if(ce.autoHide === false){
33910             close.setDisplayed(true);
33911             if(dd){
33912                 dd.unlock();
33913             }
33914         }else{
33915             close.setDisplayed(false);
33916             if(dd){
33917                 dd.lock();
33918             }
33919         }
33920         if(xy){
33921             el.avoidY = xy[1]-18;
33922             el.setXY(xy);
33923         }
33924         if(tm.animate){
33925             el.setOpacity(.1);
33926             el.setStyle("visibility", "visible");
33927             el.fadeIn({callback: afterShow});
33928         }else{
33929             afterShow();
33930         }
33931     };
33932     
33933     var afterShow = function(){
33934         if(ce){
33935             el.show();
33936             esc.enable();
33937             if(tm.autoDismiss && ce.autoHide !== false){
33938                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33939             }
33940         }
33941     };
33942     
33943     var hide = function(noanim){
33944         clearTimeout(dismissProc);
33945         clearTimeout(hideProc);
33946         ce = null;
33947         if(el.isVisible()){
33948             esc.disable();
33949             if(noanim !== true && tm.animate){
33950                 el.fadeOut({callback: afterHide});
33951             }else{
33952                 afterHide();
33953             } 
33954         }
33955     };
33956     
33957     var afterHide = function(){
33958         el.hide();
33959         if(removeCls){
33960             el.removeClass(removeCls);
33961             removeCls = null;
33962         }
33963     };
33964     
33965     return {
33966         /**
33967         * @cfg {Number} minWidth
33968         * The minimum width of the quick tip (defaults to 40)
33969         */
33970        minWidth : 40,
33971         /**
33972         * @cfg {Number} maxWidth
33973         * The maximum width of the quick tip (defaults to 300)
33974         */
33975        maxWidth : 300,
33976         /**
33977         * @cfg {Boolean} interceptTitles
33978         * True to automatically use the element's DOM title value if available (defaults to false)
33979         */
33980        interceptTitles : false,
33981         /**
33982         * @cfg {Boolean} trackMouse
33983         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33984         */
33985        trackMouse : false,
33986         /**
33987         * @cfg {Boolean} hideOnClick
33988         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33989         */
33990        hideOnClick : true,
33991         /**
33992         * @cfg {Number} showDelay
33993         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33994         */
33995        showDelay : 500,
33996         /**
33997         * @cfg {Number} hideDelay
33998         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33999         */
34000        hideDelay : 200,
34001         /**
34002         * @cfg {Boolean} autoHide
34003         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34004         * Used in conjunction with hideDelay.
34005         */
34006        autoHide : true,
34007         /**
34008         * @cfg {Boolean}
34009         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34010         * (defaults to true).  Used in conjunction with autoDismissDelay.
34011         */
34012        autoDismiss : true,
34013         /**
34014         * @cfg {Number}
34015         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34016         */
34017        autoDismissDelay : 5000,
34018        /**
34019         * @cfg {Boolean} animate
34020         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34021         */
34022        animate : false,
34023
34024        /**
34025         * @cfg {String} title
34026         * Title text to display (defaults to '').  This can be any valid HTML markup.
34027         */
34028         title: '',
34029        /**
34030         * @cfg {String} text
34031         * Body text to display (defaults to '').  This can be any valid HTML markup.
34032         */
34033         text : '',
34034        /**
34035         * @cfg {String} cls
34036         * A CSS class to apply to the base quick tip element (defaults to '').
34037         */
34038         cls : '',
34039        /**
34040         * @cfg {Number} width
34041         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34042         * minWidth or maxWidth.
34043         */
34044         width : null,
34045
34046     /**
34047      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34048      * or display QuickTips in a page.
34049      */
34050        init : function(){
34051           tm = Roo.QuickTips;
34052           cfg = tm.tagConfig;
34053           if(!inited){
34054               if(!Roo.isReady){ // allow calling of init() before onReady
34055                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34056                   return;
34057               }
34058               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34059               el.fxDefaults = {stopFx: true};
34060               // maximum custom styling
34061               //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>');
34062               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>');              
34063               tipTitle = el.child('h3');
34064               tipTitle.enableDisplayMode("block");
34065               tipBody = el.child('div.x-tip-bd');
34066               tipBodyText = el.child('div.x-tip-bd-inner');
34067               //bdLeft = el.child('div.x-tip-bd-left');
34068               //bdRight = el.child('div.x-tip-bd-right');
34069               close = el.child('div.x-tip-close');
34070               close.enableDisplayMode("block");
34071               close.on("click", hide);
34072               var d = Roo.get(document);
34073               d.on("mousedown", onDown);
34074               d.on("mouseover", onOver);
34075               d.on("mouseout", onOut);
34076               d.on("mousemove", onMove);
34077               esc = d.addKeyListener(27, hide);
34078               esc.disable();
34079               if(Roo.dd.DD){
34080                   dd = el.initDD("default", null, {
34081                       onDrag : function(){
34082                           el.sync();  
34083                       }
34084                   });
34085                   dd.setHandleElId(tipTitle.id);
34086                   dd.lock();
34087               }
34088               inited = true;
34089           }
34090           this.enable(); 
34091        },
34092
34093     /**
34094      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34095      * are supported:
34096      * <pre>
34097 Property    Type                   Description
34098 ----------  ---------------------  ------------------------------------------------------------------------
34099 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34100      * </ul>
34101      * @param {Object} config The config object
34102      */
34103        register : function(config){
34104            var cs = config instanceof Array ? config : arguments;
34105            for(var i = 0, len = cs.length; i < len; i++) {
34106                var c = cs[i];
34107                var target = c.target;
34108                if(target){
34109                    if(target instanceof Array){
34110                        for(var j = 0, jlen = target.length; j < jlen; j++){
34111                            tagEls[target[j]] = c;
34112                        }
34113                    }else{
34114                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34115                    }
34116                }
34117            }
34118        },
34119
34120     /**
34121      * Removes this quick tip from its element and destroys it.
34122      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34123      */
34124        unregister : function(el){
34125            delete tagEls[Roo.id(el)];
34126        },
34127
34128     /**
34129      * Enable this quick tip.
34130      */
34131        enable : function(){
34132            if(inited && disabled){
34133                locks.pop();
34134                if(locks.length < 1){
34135                    disabled = false;
34136                }
34137            }
34138        },
34139
34140     /**
34141      * Disable this quick tip.
34142      */
34143        disable : function(){
34144           disabled = true;
34145           clearTimeout(showProc);
34146           clearTimeout(hideProc);
34147           clearTimeout(dismissProc);
34148           if(ce){
34149               hide(true);
34150           }
34151           locks.push(1);
34152        },
34153
34154     /**
34155      * Returns true if the quick tip is enabled, else false.
34156      */
34157        isEnabled : function(){
34158             return !disabled;
34159        },
34160
34161         // private
34162        tagConfig : {
34163            namespace : "roo", // was ext?? this may break..
34164            alt_namespace : "ext",
34165            attribute : "qtip",
34166            width : "width",
34167            target : "target",
34168            title : "qtitle",
34169            hide : "hide",
34170            cls : "qclass"
34171        }
34172    };
34173 }();
34174
34175 // backwards compat
34176 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34177  * Based on:
34178  * Ext JS Library 1.1.1
34179  * Copyright(c) 2006-2007, Ext JS, LLC.
34180  *
34181  * Originally Released Under LGPL - original licence link has changed is not relivant.
34182  *
34183  * Fork - LGPL
34184  * <script type="text/javascript">
34185  */
34186  
34187
34188 /**
34189  * @class Roo.tree.TreePanel
34190  * @extends Roo.data.Tree
34191
34192  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34193  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34194  * @cfg {Boolean} enableDD true to enable drag and drop
34195  * @cfg {Boolean} enableDrag true to enable just drag
34196  * @cfg {Boolean} enableDrop true to enable just drop
34197  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34198  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34199  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34200  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34201  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34202  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34203  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34204  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34205  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34206  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34207  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34208  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34209  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34210  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34211  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / 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>
34212  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / 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>
34213  * 
34214  * @constructor
34215  * @param {String/HTMLElement/Element} el The container element
34216  * @param {Object} config
34217  */
34218 Roo.tree.TreePanel = function(el, config){
34219     var root = false;
34220     var loader = false;
34221     if (config.root) {
34222         root = config.root;
34223         delete config.root;
34224     }
34225     if (config.loader) {
34226         loader = config.loader;
34227         delete config.loader;
34228     }
34229     
34230     Roo.apply(this, config);
34231     Roo.tree.TreePanel.superclass.constructor.call(this);
34232     this.el = Roo.get(el);
34233     this.el.addClass('x-tree');
34234     //console.log(root);
34235     if (root) {
34236         this.setRootNode( Roo.factory(root, Roo.tree));
34237     }
34238     if (loader) {
34239         this.loader = Roo.factory(loader, Roo.tree);
34240     }
34241    /**
34242     * Read-only. The id of the container element becomes this TreePanel's id.
34243     */
34244     this.id = this.el.id;
34245     this.addEvents({
34246         /**
34247         * @event beforeload
34248         * Fires before a node is loaded, return false to cancel
34249         * @param {Node} node The node being loaded
34250         */
34251         "beforeload" : true,
34252         /**
34253         * @event load
34254         * Fires when a node is loaded
34255         * @param {Node} node The node that was loaded
34256         */
34257         "load" : true,
34258         /**
34259         * @event textchange
34260         * Fires when the text for a node is changed
34261         * @param {Node} node The node
34262         * @param {String} text The new text
34263         * @param {String} oldText The old text
34264         */
34265         "textchange" : true,
34266         /**
34267         * @event beforeexpand
34268         * Fires before a node is expanded, return false to cancel.
34269         * @param {Node} node The node
34270         * @param {Boolean} deep
34271         * @param {Boolean} anim
34272         */
34273         "beforeexpand" : true,
34274         /**
34275         * @event beforecollapse
34276         * Fires before a node is collapsed, return false to cancel.
34277         * @param {Node} node The node
34278         * @param {Boolean} deep
34279         * @param {Boolean} anim
34280         */
34281         "beforecollapse" : true,
34282         /**
34283         * @event expand
34284         * Fires when a node is expanded
34285         * @param {Node} node The node
34286         */
34287         "expand" : true,
34288         /**
34289         * @event disabledchange
34290         * Fires when the disabled status of a node changes
34291         * @param {Node} node The node
34292         * @param {Boolean} disabled
34293         */
34294         "disabledchange" : true,
34295         /**
34296         * @event collapse
34297         * Fires when a node is collapsed
34298         * @param {Node} node The node
34299         */
34300         "collapse" : true,
34301         /**
34302         * @event beforeclick
34303         * Fires before click processing on a node. Return false to cancel the default action.
34304         * @param {Node} node The node
34305         * @param {Roo.EventObject} e The event object
34306         */
34307         "beforeclick":true,
34308         /**
34309         * @event checkchange
34310         * Fires when a node with a checkbox's checked property changes
34311         * @param {Node} this This node
34312         * @param {Boolean} checked
34313         */
34314         "checkchange":true,
34315         /**
34316         * @event click
34317         * Fires when a node is clicked
34318         * @param {Node} node The node
34319         * @param {Roo.EventObject} e The event object
34320         */
34321         "click":true,
34322         /**
34323         * @event dblclick
34324         * Fires when a node is double clicked
34325         * @param {Node} node The node
34326         * @param {Roo.EventObject} e The event object
34327         */
34328         "dblclick":true,
34329         /**
34330         * @event contextmenu
34331         * Fires when a node is right clicked
34332         * @param {Node} node The node
34333         * @param {Roo.EventObject} e The event object
34334         */
34335         "contextmenu":true,
34336         /**
34337         * @event beforechildrenrendered
34338         * Fires right before the child nodes for a node are rendered
34339         * @param {Node} node The node
34340         */
34341         "beforechildrenrendered":true,
34342         /**
34343         * @event startdrag
34344         * Fires when a node starts being dragged
34345         * @param {Roo.tree.TreePanel} this
34346         * @param {Roo.tree.TreeNode} node
34347         * @param {event} e The raw browser event
34348         */ 
34349        "startdrag" : true,
34350        /**
34351         * @event enddrag
34352         * Fires when a drag operation is complete
34353         * @param {Roo.tree.TreePanel} this
34354         * @param {Roo.tree.TreeNode} node
34355         * @param {event} e The raw browser event
34356         */
34357        "enddrag" : true,
34358        /**
34359         * @event dragdrop
34360         * Fires when a dragged node is dropped on a valid DD target
34361         * @param {Roo.tree.TreePanel} this
34362         * @param {Roo.tree.TreeNode} node
34363         * @param {DD} dd The dd it was dropped on
34364         * @param {event} e The raw browser event
34365         */
34366        "dragdrop" : true,
34367        /**
34368         * @event beforenodedrop
34369         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34370         * passed to handlers has the following properties:<br />
34371         * <ul style="padding:5px;padding-left:16px;">
34372         * <li>tree - The TreePanel</li>
34373         * <li>target - The node being targeted for the drop</li>
34374         * <li>data - The drag data from the drag source</li>
34375         * <li>point - The point of the drop - append, above or below</li>
34376         * <li>source - The drag source</li>
34377         * <li>rawEvent - Raw mouse event</li>
34378         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34379         * to be inserted by setting them on this object.</li>
34380         * <li>cancel - Set this to true to cancel the drop.</li>
34381         * </ul>
34382         * @param {Object} dropEvent
34383         */
34384        "beforenodedrop" : true,
34385        /**
34386         * @event nodedrop
34387         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34388         * passed to handlers has the following properties:<br />
34389         * <ul style="padding:5px;padding-left:16px;">
34390         * <li>tree - The TreePanel</li>
34391         * <li>target - The node being targeted for the drop</li>
34392         * <li>data - The drag data from the drag source</li>
34393         * <li>point - The point of the drop - append, above or below</li>
34394         * <li>source - The drag source</li>
34395         * <li>rawEvent - Raw mouse event</li>
34396         * <li>dropNode - Dropped node(s).</li>
34397         * </ul>
34398         * @param {Object} dropEvent
34399         */
34400        "nodedrop" : true,
34401         /**
34402         * @event nodedragover
34403         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34404         * passed to handlers has the following properties:<br />
34405         * <ul style="padding:5px;padding-left:16px;">
34406         * <li>tree - The TreePanel</li>
34407         * <li>target - The node being targeted for the drop</li>
34408         * <li>data - The drag data from the drag source</li>
34409         * <li>point - The point of the drop - append, above or below</li>
34410         * <li>source - The drag source</li>
34411         * <li>rawEvent - Raw mouse event</li>
34412         * <li>dropNode - Drop node(s) provided by the source.</li>
34413         * <li>cancel - Set this to true to signal drop not allowed.</li>
34414         * </ul>
34415         * @param {Object} dragOverEvent
34416         */
34417        "nodedragover" : true,
34418        /**
34419         * @event appendnode
34420         * Fires when append node to the tree
34421         * @param {Roo.tree.TreePanel} this
34422         * @param {Roo.tree.TreeNode} node
34423         * @param {Number} index The index of the newly appended node
34424         */
34425        "appendnode" : true
34426         
34427     });
34428     if(this.singleExpand){
34429        this.on("beforeexpand", this.restrictExpand, this);
34430     }
34431     if (this.editor) {
34432         this.editor.tree = this;
34433         this.editor = Roo.factory(this.editor, Roo.tree);
34434     }
34435     
34436     if (this.selModel) {
34437         this.selModel = Roo.factory(this.selModel, Roo.tree);
34438     }
34439    
34440 };
34441 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34442     rootVisible : true,
34443     animate: Roo.enableFx,
34444     lines : true,
34445     enableDD : false,
34446     hlDrop : Roo.enableFx,
34447   
34448     renderer: false,
34449     
34450     rendererTip: false,
34451     // private
34452     restrictExpand : function(node){
34453         var p = node.parentNode;
34454         if(p){
34455             if(p.expandedChild && p.expandedChild.parentNode == p){
34456                 p.expandedChild.collapse();
34457             }
34458             p.expandedChild = node;
34459         }
34460     },
34461
34462     // private override
34463     setRootNode : function(node){
34464         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34465         if(!this.rootVisible){
34466             node.ui = new Roo.tree.RootTreeNodeUI(node);
34467         }
34468         return node;
34469     },
34470
34471     /**
34472      * Returns the container element for this TreePanel
34473      */
34474     getEl : function(){
34475         return this.el;
34476     },
34477
34478     /**
34479      * Returns the default TreeLoader for this TreePanel
34480      */
34481     getLoader : function(){
34482         return this.loader;
34483     },
34484
34485     /**
34486      * Expand all nodes
34487      */
34488     expandAll : function(){
34489         this.root.expand(true);
34490     },
34491
34492     /**
34493      * Collapse all nodes
34494      */
34495     collapseAll : function(){
34496         this.root.collapse(true);
34497     },
34498
34499     /**
34500      * Returns the selection model used by this TreePanel
34501      */
34502     getSelectionModel : function(){
34503         if(!this.selModel){
34504             this.selModel = new Roo.tree.DefaultSelectionModel();
34505         }
34506         return this.selModel;
34507     },
34508
34509     /**
34510      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34511      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34512      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34513      * @return {Array}
34514      */
34515     getChecked : function(a, startNode){
34516         startNode = startNode || this.root;
34517         var r = [];
34518         var f = function(){
34519             if(this.attributes.checked){
34520                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34521             }
34522         }
34523         startNode.cascade(f);
34524         return r;
34525     },
34526
34527     /**
34528      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34529      * @param {String} path
34530      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34531      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34532      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34533      */
34534     expandPath : function(path, attr, callback){
34535         attr = attr || "id";
34536         var keys = path.split(this.pathSeparator);
34537         var curNode = this.root;
34538         if(curNode.attributes[attr] != keys[1]){ // invalid root
34539             if(callback){
34540                 callback(false, null);
34541             }
34542             return;
34543         }
34544         var index = 1;
34545         var f = function(){
34546             if(++index == keys.length){
34547                 if(callback){
34548                     callback(true, curNode);
34549                 }
34550                 return;
34551             }
34552             var c = curNode.findChild(attr, keys[index]);
34553             if(!c){
34554                 if(callback){
34555                     callback(false, curNode);
34556                 }
34557                 return;
34558             }
34559             curNode = c;
34560             c.expand(false, false, f);
34561         };
34562         curNode.expand(false, false, f);
34563     },
34564
34565     /**
34566      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34567      * @param {String} path
34568      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34569      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34570      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34571      */
34572     selectPath : function(path, attr, callback){
34573         attr = attr || "id";
34574         var keys = path.split(this.pathSeparator);
34575         var v = keys.pop();
34576         if(keys.length > 0){
34577             var f = function(success, node){
34578                 if(success && node){
34579                     var n = node.findChild(attr, v);
34580                     if(n){
34581                         n.select();
34582                         if(callback){
34583                             callback(true, n);
34584                         }
34585                     }else if(callback){
34586                         callback(false, n);
34587                     }
34588                 }else{
34589                     if(callback){
34590                         callback(false, n);
34591                     }
34592                 }
34593             };
34594             this.expandPath(keys.join(this.pathSeparator), attr, f);
34595         }else{
34596             this.root.select();
34597             if(callback){
34598                 callback(true, this.root);
34599             }
34600         }
34601     },
34602
34603     getTreeEl : function(){
34604         return this.el;
34605     },
34606
34607     /**
34608      * Trigger rendering of this TreePanel
34609      */
34610     render : function(){
34611         if (this.innerCt) {
34612             return this; // stop it rendering more than once!!
34613         }
34614         
34615         this.innerCt = this.el.createChild({tag:"ul",
34616                cls:"x-tree-root-ct " +
34617                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34618
34619         if(this.containerScroll){
34620             Roo.dd.ScrollManager.register(this.el);
34621         }
34622         if((this.enableDD || this.enableDrop) && !this.dropZone){
34623            /**
34624             * The dropZone used by this tree if drop is enabled
34625             * @type Roo.tree.TreeDropZone
34626             */
34627              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34628                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34629            });
34630         }
34631         if((this.enableDD || this.enableDrag) && !this.dragZone){
34632            /**
34633             * The dragZone used by this tree if drag is enabled
34634             * @type Roo.tree.TreeDragZone
34635             */
34636             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34637                ddGroup: this.ddGroup || "TreeDD",
34638                scroll: this.ddScroll
34639            });
34640         }
34641         this.getSelectionModel().init(this);
34642         if (!this.root) {
34643             Roo.log("ROOT not set in tree");
34644             return this;
34645         }
34646         this.root.render();
34647         if(!this.rootVisible){
34648             this.root.renderChildren();
34649         }
34650         return this;
34651     }
34652 });/*
34653  * Based on:
34654  * Ext JS Library 1.1.1
34655  * Copyright(c) 2006-2007, Ext JS, LLC.
34656  *
34657  * Originally Released Under LGPL - original licence link has changed is not relivant.
34658  *
34659  * Fork - LGPL
34660  * <script type="text/javascript">
34661  */
34662  
34663
34664 /**
34665  * @class Roo.tree.DefaultSelectionModel
34666  * @extends Roo.util.Observable
34667  * The default single selection for a TreePanel.
34668  * @param {Object} cfg Configuration
34669  */
34670 Roo.tree.DefaultSelectionModel = function(cfg){
34671    this.selNode = null;
34672    
34673    
34674    
34675    this.addEvents({
34676        /**
34677         * @event selectionchange
34678         * Fires when the selected node changes
34679         * @param {DefaultSelectionModel} this
34680         * @param {TreeNode} node the new selection
34681         */
34682        "selectionchange" : true,
34683
34684        /**
34685         * @event beforeselect
34686         * Fires before the selected node changes, return false to cancel the change
34687         * @param {DefaultSelectionModel} this
34688         * @param {TreeNode} node the new selection
34689         * @param {TreeNode} node the old selection
34690         */
34691        "beforeselect" : true
34692    });
34693    
34694     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34695 };
34696
34697 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34698     init : function(tree){
34699         this.tree = tree;
34700         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34701         tree.on("click", this.onNodeClick, this);
34702     },
34703     
34704     onNodeClick : function(node, e){
34705         if (e.ctrlKey && this.selNode == node)  {
34706             this.unselect(node);
34707             return;
34708         }
34709         this.select(node);
34710     },
34711     
34712     /**
34713      * Select a node.
34714      * @param {TreeNode} node The node to select
34715      * @return {TreeNode} The selected node
34716      */
34717     select : function(node){
34718         var last = this.selNode;
34719         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34720             if(last){
34721                 last.ui.onSelectedChange(false);
34722             }
34723             this.selNode = node;
34724             node.ui.onSelectedChange(true);
34725             this.fireEvent("selectionchange", this, node, last);
34726         }
34727         return node;
34728     },
34729     
34730     /**
34731      * Deselect a node.
34732      * @param {TreeNode} node The node to unselect
34733      */
34734     unselect : function(node){
34735         if(this.selNode == node){
34736             this.clearSelections();
34737         }    
34738     },
34739     
34740     /**
34741      * Clear all selections
34742      */
34743     clearSelections : function(){
34744         var n = this.selNode;
34745         if(n){
34746             n.ui.onSelectedChange(false);
34747             this.selNode = null;
34748             this.fireEvent("selectionchange", this, null);
34749         }
34750         return n;
34751     },
34752     
34753     /**
34754      * Get the selected node
34755      * @return {TreeNode} The selected node
34756      */
34757     getSelectedNode : function(){
34758         return this.selNode;    
34759     },
34760     
34761     /**
34762      * Returns true if the node is selected
34763      * @param {TreeNode} node The node to check
34764      * @return {Boolean}
34765      */
34766     isSelected : function(node){
34767         return this.selNode == node;  
34768     },
34769
34770     /**
34771      * Selects the node above the selected node in the tree, intelligently walking the nodes
34772      * @return TreeNode The new selection
34773      */
34774     selectPrevious : function(){
34775         var s = this.selNode || this.lastSelNode;
34776         if(!s){
34777             return null;
34778         }
34779         var ps = s.previousSibling;
34780         if(ps){
34781             if(!ps.isExpanded() || ps.childNodes.length < 1){
34782                 return this.select(ps);
34783             } else{
34784                 var lc = ps.lastChild;
34785                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34786                     lc = lc.lastChild;
34787                 }
34788                 return this.select(lc);
34789             }
34790         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34791             return this.select(s.parentNode);
34792         }
34793         return null;
34794     },
34795
34796     /**
34797      * Selects the node above the selected node in the tree, intelligently walking the nodes
34798      * @return TreeNode The new selection
34799      */
34800     selectNext : function(){
34801         var s = this.selNode || this.lastSelNode;
34802         if(!s){
34803             return null;
34804         }
34805         if(s.firstChild && s.isExpanded()){
34806              return this.select(s.firstChild);
34807          }else if(s.nextSibling){
34808              return this.select(s.nextSibling);
34809          }else if(s.parentNode){
34810             var newS = null;
34811             s.parentNode.bubble(function(){
34812                 if(this.nextSibling){
34813                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34814                     return false;
34815                 }
34816             });
34817             return newS;
34818          }
34819         return null;
34820     },
34821
34822     onKeyDown : function(e){
34823         var s = this.selNode || this.lastSelNode;
34824         // undesirable, but required
34825         var sm = this;
34826         if(!s){
34827             return;
34828         }
34829         var k = e.getKey();
34830         switch(k){
34831              case e.DOWN:
34832                  e.stopEvent();
34833                  this.selectNext();
34834              break;
34835              case e.UP:
34836                  e.stopEvent();
34837                  this.selectPrevious();
34838              break;
34839              case e.RIGHT:
34840                  e.preventDefault();
34841                  if(s.hasChildNodes()){
34842                      if(!s.isExpanded()){
34843                          s.expand();
34844                      }else if(s.firstChild){
34845                          this.select(s.firstChild, e);
34846                      }
34847                  }
34848              break;
34849              case e.LEFT:
34850                  e.preventDefault();
34851                  if(s.hasChildNodes() && s.isExpanded()){
34852                      s.collapse();
34853                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34854                      this.select(s.parentNode, e);
34855                  }
34856              break;
34857         };
34858     }
34859 });
34860
34861 /**
34862  * @class Roo.tree.MultiSelectionModel
34863  * @extends Roo.util.Observable
34864  * Multi selection for a TreePanel.
34865  * @param {Object} cfg Configuration
34866  */
34867 Roo.tree.MultiSelectionModel = function(){
34868    this.selNodes = [];
34869    this.selMap = {};
34870    this.addEvents({
34871        /**
34872         * @event selectionchange
34873         * Fires when the selected nodes change
34874         * @param {MultiSelectionModel} this
34875         * @param {Array} nodes Array of the selected nodes
34876         */
34877        "selectionchange" : true
34878    });
34879    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34880    
34881 };
34882
34883 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34884     init : function(tree){
34885         this.tree = tree;
34886         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34887         tree.on("click", this.onNodeClick, this);
34888     },
34889     
34890     onNodeClick : function(node, e){
34891         this.select(node, e, e.ctrlKey);
34892     },
34893     
34894     /**
34895      * Select a node.
34896      * @param {TreeNode} node The node to select
34897      * @param {EventObject} e (optional) An event associated with the selection
34898      * @param {Boolean} keepExisting True to retain existing selections
34899      * @return {TreeNode} The selected node
34900      */
34901     select : function(node, e, keepExisting){
34902         if(keepExisting !== true){
34903             this.clearSelections(true);
34904         }
34905         if(this.isSelected(node)){
34906             this.lastSelNode = node;
34907             return node;
34908         }
34909         this.selNodes.push(node);
34910         this.selMap[node.id] = node;
34911         this.lastSelNode = node;
34912         node.ui.onSelectedChange(true);
34913         this.fireEvent("selectionchange", this, this.selNodes);
34914         return node;
34915     },
34916     
34917     /**
34918      * Deselect a node.
34919      * @param {TreeNode} node The node to unselect
34920      */
34921     unselect : function(node){
34922         if(this.selMap[node.id]){
34923             node.ui.onSelectedChange(false);
34924             var sn = this.selNodes;
34925             var index = -1;
34926             if(sn.indexOf){
34927                 index = sn.indexOf(node);
34928             }else{
34929                 for(var i = 0, len = sn.length; i < len; i++){
34930                     if(sn[i] == node){
34931                         index = i;
34932                         break;
34933                     }
34934                 }
34935             }
34936             if(index != -1){
34937                 this.selNodes.splice(index, 1);
34938             }
34939             delete this.selMap[node.id];
34940             this.fireEvent("selectionchange", this, this.selNodes);
34941         }
34942     },
34943     
34944     /**
34945      * Clear all selections
34946      */
34947     clearSelections : function(suppressEvent){
34948         var sn = this.selNodes;
34949         if(sn.length > 0){
34950             for(var i = 0, len = sn.length; i < len; i++){
34951                 sn[i].ui.onSelectedChange(false);
34952             }
34953             this.selNodes = [];
34954             this.selMap = {};
34955             if(suppressEvent !== true){
34956                 this.fireEvent("selectionchange", this, this.selNodes);
34957             }
34958         }
34959     },
34960     
34961     /**
34962      * Returns true if the node is selected
34963      * @param {TreeNode} node The node to check
34964      * @return {Boolean}
34965      */
34966     isSelected : function(node){
34967         return this.selMap[node.id] ? true : false;  
34968     },
34969     
34970     /**
34971      * Returns an array of the selected nodes
34972      * @return {Array}
34973      */
34974     getSelectedNodes : function(){
34975         return this.selNodes;    
34976     },
34977
34978     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34979
34980     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34981
34982     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34983 });/*
34984  * Based on:
34985  * Ext JS Library 1.1.1
34986  * Copyright(c) 2006-2007, Ext JS, LLC.
34987  *
34988  * Originally Released Under LGPL - original licence link has changed is not relivant.
34989  *
34990  * Fork - LGPL
34991  * <script type="text/javascript">
34992  */
34993  
34994 /**
34995  * @class Roo.tree.TreeNode
34996  * @extends Roo.data.Node
34997  * @cfg {String} text The text for this node
34998  * @cfg {Boolean} expanded true to start the node expanded
34999  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35000  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35001  * @cfg {Boolean} disabled true to start the node disabled
35002  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35003  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35004  * @cfg {String} cls A css class to be added to the node
35005  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35006  * @cfg {String} href URL of the link used for the node (defaults to #)
35007  * @cfg {String} hrefTarget target frame for the link
35008  * @cfg {String} qtip An Ext QuickTip for the node
35009  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35010  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35011  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35012  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35013  * (defaults to undefined with no checkbox rendered)
35014  * @constructor
35015  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35016  */
35017 Roo.tree.TreeNode = function(attributes){
35018     attributes = attributes || {};
35019     if(typeof attributes == "string"){
35020         attributes = {text: attributes};
35021     }
35022     this.childrenRendered = false;
35023     this.rendered = false;
35024     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35025     this.expanded = attributes.expanded === true;
35026     this.isTarget = attributes.isTarget !== false;
35027     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35028     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35029
35030     /**
35031      * Read-only. The text for this node. To change it use setText().
35032      * @type String
35033      */
35034     this.text = attributes.text;
35035     /**
35036      * True if this node is disabled.
35037      * @type Boolean
35038      */
35039     this.disabled = attributes.disabled === true;
35040
35041     this.addEvents({
35042         /**
35043         * @event textchange
35044         * Fires when the text for this node is changed
35045         * @param {Node} this This node
35046         * @param {String} text The new text
35047         * @param {String} oldText The old text
35048         */
35049         "textchange" : true,
35050         /**
35051         * @event beforeexpand
35052         * Fires before this node is expanded, return false to cancel.
35053         * @param {Node} this This node
35054         * @param {Boolean} deep
35055         * @param {Boolean} anim
35056         */
35057         "beforeexpand" : true,
35058         /**
35059         * @event beforecollapse
35060         * Fires before this node is collapsed, return false to cancel.
35061         * @param {Node} this This node
35062         * @param {Boolean} deep
35063         * @param {Boolean} anim
35064         */
35065         "beforecollapse" : true,
35066         /**
35067         * @event expand
35068         * Fires when this node is expanded
35069         * @param {Node} this This node
35070         */
35071         "expand" : true,
35072         /**
35073         * @event disabledchange
35074         * Fires when the disabled status of this node changes
35075         * @param {Node} this This node
35076         * @param {Boolean} disabled
35077         */
35078         "disabledchange" : true,
35079         /**
35080         * @event collapse
35081         * Fires when this node is collapsed
35082         * @param {Node} this This node
35083         */
35084         "collapse" : true,
35085         /**
35086         * @event beforeclick
35087         * Fires before click processing. Return false to cancel the default action.
35088         * @param {Node} this This node
35089         * @param {Roo.EventObject} e The event object
35090         */
35091         "beforeclick":true,
35092         /**
35093         * @event checkchange
35094         * Fires when a node with a checkbox's checked property changes
35095         * @param {Node} this This node
35096         * @param {Boolean} checked
35097         */
35098         "checkchange":true,
35099         /**
35100         * @event click
35101         * Fires when this node is clicked
35102         * @param {Node} this This node
35103         * @param {Roo.EventObject} e The event object
35104         */
35105         "click":true,
35106         /**
35107         * @event dblclick
35108         * Fires when this node is double clicked
35109         * @param {Node} this This node
35110         * @param {Roo.EventObject} e The event object
35111         */
35112         "dblclick":true,
35113         /**
35114         * @event contextmenu
35115         * Fires when this node is right clicked
35116         * @param {Node} this This node
35117         * @param {Roo.EventObject} e The event object
35118         */
35119         "contextmenu":true,
35120         /**
35121         * @event beforechildrenrendered
35122         * Fires right before the child nodes for this node are rendered
35123         * @param {Node} this This node
35124         */
35125         "beforechildrenrendered":true
35126     });
35127
35128     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35129
35130     /**
35131      * Read-only. The UI for this node
35132      * @type TreeNodeUI
35133      */
35134     this.ui = new uiClass(this);
35135     
35136     // finally support items[]
35137     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35138         return;
35139     }
35140     
35141     
35142     Roo.each(this.attributes.items, function(c) {
35143         this.appendChild(Roo.factory(c,Roo.Tree));
35144     }, this);
35145     delete this.attributes.items;
35146     
35147     
35148     
35149 };
35150 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35151     preventHScroll: true,
35152     /**
35153      * Returns true if this node is expanded
35154      * @return {Boolean}
35155      */
35156     isExpanded : function(){
35157         return this.expanded;
35158     },
35159
35160     /**
35161      * Returns the UI object for this node
35162      * @return {TreeNodeUI}
35163      */
35164     getUI : function(){
35165         return this.ui;
35166     },
35167
35168     // private override
35169     setFirstChild : function(node){
35170         var of = this.firstChild;
35171         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35172         if(this.childrenRendered && of && node != of){
35173             of.renderIndent(true, true);
35174         }
35175         if(this.rendered){
35176             this.renderIndent(true, true);
35177         }
35178     },
35179
35180     // private override
35181     setLastChild : function(node){
35182         var ol = this.lastChild;
35183         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35184         if(this.childrenRendered && ol && node != ol){
35185             ol.renderIndent(true, true);
35186         }
35187         if(this.rendered){
35188             this.renderIndent(true, true);
35189         }
35190     },
35191
35192     // these methods are overridden to provide lazy rendering support
35193     // private override
35194     appendChild : function()
35195     {
35196         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35197         if(node && this.childrenRendered){
35198             node.render();
35199         }
35200         this.ui.updateExpandIcon();
35201         return node;
35202     },
35203
35204     // private override
35205     removeChild : function(node){
35206         this.ownerTree.getSelectionModel().unselect(node);
35207         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35208         // if it's been rendered remove dom node
35209         if(this.childrenRendered){
35210             node.ui.remove();
35211         }
35212         if(this.childNodes.length < 1){
35213             this.collapse(false, false);
35214         }else{
35215             this.ui.updateExpandIcon();
35216         }
35217         if(!this.firstChild) {
35218             this.childrenRendered = false;
35219         }
35220         return node;
35221     },
35222
35223     // private override
35224     insertBefore : function(node, refNode){
35225         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35226         if(newNode && refNode && this.childrenRendered){
35227             node.render();
35228         }
35229         this.ui.updateExpandIcon();
35230         return newNode;
35231     },
35232
35233     /**
35234      * Sets the text for this node
35235      * @param {String} text
35236      */
35237     setText : function(text){
35238         var oldText = this.text;
35239         this.text = text;
35240         this.attributes.text = text;
35241         if(this.rendered){ // event without subscribing
35242             this.ui.onTextChange(this, text, oldText);
35243         }
35244         this.fireEvent("textchange", this, text, oldText);
35245     },
35246
35247     /**
35248      * Triggers selection of this node
35249      */
35250     select : function(){
35251         this.getOwnerTree().getSelectionModel().select(this);
35252     },
35253
35254     /**
35255      * Triggers deselection of this node
35256      */
35257     unselect : function(){
35258         this.getOwnerTree().getSelectionModel().unselect(this);
35259     },
35260
35261     /**
35262      * Returns true if this node is selected
35263      * @return {Boolean}
35264      */
35265     isSelected : function(){
35266         return this.getOwnerTree().getSelectionModel().isSelected(this);
35267     },
35268
35269     /**
35270      * Expand this node.
35271      * @param {Boolean} deep (optional) True to expand all children as well
35272      * @param {Boolean} anim (optional) false to cancel the default animation
35273      * @param {Function} callback (optional) A callback to be called when
35274      * expanding this node completes (does not wait for deep expand to complete).
35275      * Called with 1 parameter, this node.
35276      */
35277     expand : function(deep, anim, callback){
35278         if(!this.expanded){
35279             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35280                 return;
35281             }
35282             if(!this.childrenRendered){
35283                 this.renderChildren();
35284             }
35285             this.expanded = true;
35286             
35287             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35288                 this.ui.animExpand(function(){
35289                     this.fireEvent("expand", this);
35290                     if(typeof callback == "function"){
35291                         callback(this);
35292                     }
35293                     if(deep === true){
35294                         this.expandChildNodes(true);
35295                     }
35296                 }.createDelegate(this));
35297                 return;
35298             }else{
35299                 this.ui.expand();
35300                 this.fireEvent("expand", this);
35301                 if(typeof callback == "function"){
35302                     callback(this);
35303                 }
35304             }
35305         }else{
35306            if(typeof callback == "function"){
35307                callback(this);
35308            }
35309         }
35310         if(deep === true){
35311             this.expandChildNodes(true);
35312         }
35313     },
35314
35315     isHiddenRoot : function(){
35316         return this.isRoot && !this.getOwnerTree().rootVisible;
35317     },
35318
35319     /**
35320      * Collapse this node.
35321      * @param {Boolean} deep (optional) True to collapse all children as well
35322      * @param {Boolean} anim (optional) false to cancel the default animation
35323      */
35324     collapse : function(deep, anim){
35325         if(this.expanded && !this.isHiddenRoot()){
35326             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35327                 return;
35328             }
35329             this.expanded = false;
35330             if((this.getOwnerTree().animate && anim !== false) || anim){
35331                 this.ui.animCollapse(function(){
35332                     this.fireEvent("collapse", this);
35333                     if(deep === true){
35334                         this.collapseChildNodes(true);
35335                     }
35336                 }.createDelegate(this));
35337                 return;
35338             }else{
35339                 this.ui.collapse();
35340                 this.fireEvent("collapse", this);
35341             }
35342         }
35343         if(deep === true){
35344             var cs = this.childNodes;
35345             for(var i = 0, len = cs.length; i < len; i++) {
35346                 cs[i].collapse(true, false);
35347             }
35348         }
35349     },
35350
35351     // private
35352     delayedExpand : function(delay){
35353         if(!this.expandProcId){
35354             this.expandProcId = this.expand.defer(delay, this);
35355         }
35356     },
35357
35358     // private
35359     cancelExpand : function(){
35360         if(this.expandProcId){
35361             clearTimeout(this.expandProcId);
35362         }
35363         this.expandProcId = false;
35364     },
35365
35366     /**
35367      * Toggles expanded/collapsed state of the node
35368      */
35369     toggle : function(){
35370         if(this.expanded){
35371             this.collapse();
35372         }else{
35373             this.expand();
35374         }
35375     },
35376
35377     /**
35378      * Ensures all parent nodes are expanded
35379      */
35380     ensureVisible : function(callback){
35381         var tree = this.getOwnerTree();
35382         tree.expandPath(this.parentNode.getPath(), false, function(){
35383             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35384             Roo.callback(callback);
35385         }.createDelegate(this));
35386     },
35387
35388     /**
35389      * Expand all child nodes
35390      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35391      */
35392     expandChildNodes : function(deep){
35393         var cs = this.childNodes;
35394         for(var i = 0, len = cs.length; i < len; i++) {
35395                 cs[i].expand(deep);
35396         }
35397     },
35398
35399     /**
35400      * Collapse all child nodes
35401      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35402      */
35403     collapseChildNodes : function(deep){
35404         var cs = this.childNodes;
35405         for(var i = 0, len = cs.length; i < len; i++) {
35406                 cs[i].collapse(deep);
35407         }
35408     },
35409
35410     /**
35411      * Disables this node
35412      */
35413     disable : function(){
35414         this.disabled = true;
35415         this.unselect();
35416         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35417             this.ui.onDisableChange(this, true);
35418         }
35419         this.fireEvent("disabledchange", this, true);
35420     },
35421
35422     /**
35423      * Enables this node
35424      */
35425     enable : function(){
35426         this.disabled = false;
35427         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35428             this.ui.onDisableChange(this, false);
35429         }
35430         this.fireEvent("disabledchange", this, false);
35431     },
35432
35433     // private
35434     renderChildren : function(suppressEvent){
35435         if(suppressEvent !== false){
35436             this.fireEvent("beforechildrenrendered", this);
35437         }
35438         var cs = this.childNodes;
35439         for(var i = 0, len = cs.length; i < len; i++){
35440             cs[i].render(true);
35441         }
35442         this.childrenRendered = true;
35443     },
35444
35445     // private
35446     sort : function(fn, scope){
35447         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35448         if(this.childrenRendered){
35449             var cs = this.childNodes;
35450             for(var i = 0, len = cs.length; i < len; i++){
35451                 cs[i].render(true);
35452             }
35453         }
35454     },
35455
35456     // private
35457     render : function(bulkRender){
35458         this.ui.render(bulkRender);
35459         if(!this.rendered){
35460             this.rendered = true;
35461             if(this.expanded){
35462                 this.expanded = false;
35463                 this.expand(false, false);
35464             }
35465         }
35466     },
35467
35468     // private
35469     renderIndent : function(deep, refresh){
35470         if(refresh){
35471             this.ui.childIndent = null;
35472         }
35473         this.ui.renderIndent();
35474         if(deep === true && this.childrenRendered){
35475             var cs = this.childNodes;
35476             for(var i = 0, len = cs.length; i < len; i++){
35477                 cs[i].renderIndent(true, refresh);
35478             }
35479         }
35480     }
35481 });/*
35482  * Based on:
35483  * Ext JS Library 1.1.1
35484  * Copyright(c) 2006-2007, Ext JS, LLC.
35485  *
35486  * Originally Released Under LGPL - original licence link has changed is not relivant.
35487  *
35488  * Fork - LGPL
35489  * <script type="text/javascript">
35490  */
35491  
35492 /**
35493  * @class Roo.tree.AsyncTreeNode
35494  * @extends Roo.tree.TreeNode
35495  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35496  * @constructor
35497  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35498  */
35499  Roo.tree.AsyncTreeNode = function(config){
35500     this.loaded = false;
35501     this.loading = false;
35502     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35503     /**
35504     * @event beforeload
35505     * Fires before this node is loaded, return false to cancel
35506     * @param {Node} this This node
35507     */
35508     this.addEvents({'beforeload':true, 'load': true});
35509     /**
35510     * @event load
35511     * Fires when this node is loaded
35512     * @param {Node} this This node
35513     */
35514     /**
35515      * The loader used by this node (defaults to using the tree's defined loader)
35516      * @type TreeLoader
35517      * @property loader
35518      */
35519 };
35520 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35521     expand : function(deep, anim, callback){
35522         if(this.loading){ // if an async load is already running, waiting til it's done
35523             var timer;
35524             var f = function(){
35525                 if(!this.loading){ // done loading
35526                     clearInterval(timer);
35527                     this.expand(deep, anim, callback);
35528                 }
35529             }.createDelegate(this);
35530             timer = setInterval(f, 200);
35531             return;
35532         }
35533         if(!this.loaded){
35534             if(this.fireEvent("beforeload", this) === false){
35535                 return;
35536             }
35537             this.loading = true;
35538             this.ui.beforeLoad(this);
35539             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35540             if(loader){
35541                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35542                 return;
35543             }
35544         }
35545         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35546     },
35547     
35548     /**
35549      * Returns true if this node is currently loading
35550      * @return {Boolean}
35551      */
35552     isLoading : function(){
35553         return this.loading;  
35554     },
35555     
35556     loadComplete : function(deep, anim, callback){
35557         this.loading = false;
35558         this.loaded = true;
35559         this.ui.afterLoad(this);
35560         this.fireEvent("load", this);
35561         this.expand(deep, anim, callback);
35562     },
35563     
35564     /**
35565      * Returns true if this node has been loaded
35566      * @return {Boolean}
35567      */
35568     isLoaded : function(){
35569         return this.loaded;
35570     },
35571     
35572     hasChildNodes : function(){
35573         if(!this.isLeaf() && !this.loaded){
35574             return true;
35575         }else{
35576             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35577         }
35578     },
35579
35580     /**
35581      * Trigger a reload for this node
35582      * @param {Function} callback
35583      */
35584     reload : function(callback){
35585         this.collapse(false, false);
35586         while(this.firstChild){
35587             this.removeChild(this.firstChild);
35588         }
35589         this.childrenRendered = false;
35590         this.loaded = false;
35591         if(this.isHiddenRoot()){
35592             this.expanded = false;
35593         }
35594         this.expand(false, false, callback);
35595     }
35596 });/*
35597  * Based on:
35598  * Ext JS Library 1.1.1
35599  * Copyright(c) 2006-2007, Ext JS, LLC.
35600  *
35601  * Originally Released Under LGPL - original licence link has changed is not relivant.
35602  *
35603  * Fork - LGPL
35604  * <script type="text/javascript">
35605  */
35606  
35607 /**
35608  * @class Roo.tree.TreeNodeUI
35609  * @constructor
35610  * @param {Object} node The node to render
35611  * The TreeNode UI implementation is separate from the
35612  * tree implementation. Unless you are customizing the tree UI,
35613  * you should never have to use this directly.
35614  */
35615 Roo.tree.TreeNodeUI = function(node){
35616     this.node = node;
35617     this.rendered = false;
35618     this.animating = false;
35619     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35620 };
35621
35622 Roo.tree.TreeNodeUI.prototype = {
35623     removeChild : function(node){
35624         if(this.rendered){
35625             this.ctNode.removeChild(node.ui.getEl());
35626         }
35627     },
35628
35629     beforeLoad : function(){
35630          this.addClass("x-tree-node-loading");
35631     },
35632
35633     afterLoad : function(){
35634          this.removeClass("x-tree-node-loading");
35635     },
35636
35637     onTextChange : function(node, text, oldText){
35638         if(this.rendered){
35639             this.textNode.innerHTML = text;
35640         }
35641     },
35642
35643     onDisableChange : function(node, state){
35644         this.disabled = state;
35645         if(state){
35646             this.addClass("x-tree-node-disabled");
35647         }else{
35648             this.removeClass("x-tree-node-disabled");
35649         }
35650     },
35651
35652     onSelectedChange : function(state){
35653         if(state){
35654             this.focus();
35655             this.addClass("x-tree-selected");
35656         }else{
35657             //this.blur();
35658             this.removeClass("x-tree-selected");
35659         }
35660     },
35661
35662     onMove : function(tree, node, oldParent, newParent, index, refNode){
35663         this.childIndent = null;
35664         if(this.rendered){
35665             var targetNode = newParent.ui.getContainer();
35666             if(!targetNode){//target not rendered
35667                 this.holder = document.createElement("div");
35668                 this.holder.appendChild(this.wrap);
35669                 return;
35670             }
35671             var insertBefore = refNode ? refNode.ui.getEl() : null;
35672             if(insertBefore){
35673                 targetNode.insertBefore(this.wrap, insertBefore);
35674             }else{
35675                 targetNode.appendChild(this.wrap);
35676             }
35677             this.node.renderIndent(true);
35678         }
35679     },
35680
35681     addClass : function(cls){
35682         if(this.elNode){
35683             Roo.fly(this.elNode).addClass(cls);
35684         }
35685     },
35686
35687     removeClass : function(cls){
35688         if(this.elNode){
35689             Roo.fly(this.elNode).removeClass(cls);
35690         }
35691     },
35692
35693     remove : function(){
35694         if(this.rendered){
35695             this.holder = document.createElement("div");
35696             this.holder.appendChild(this.wrap);
35697         }
35698     },
35699
35700     fireEvent : function(){
35701         return this.node.fireEvent.apply(this.node, arguments);
35702     },
35703
35704     initEvents : function(){
35705         this.node.on("move", this.onMove, this);
35706         var E = Roo.EventManager;
35707         var a = this.anchor;
35708
35709         var el = Roo.fly(a, '_treeui');
35710
35711         if(Roo.isOpera){ // opera render bug ignores the CSS
35712             el.setStyle("text-decoration", "none");
35713         }
35714
35715         el.on("click", this.onClick, this);
35716         el.on("dblclick", this.onDblClick, this);
35717
35718         if(this.checkbox){
35719             Roo.EventManager.on(this.checkbox,
35720                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35721         }
35722
35723         el.on("contextmenu", this.onContextMenu, this);
35724
35725         var icon = Roo.fly(this.iconNode);
35726         icon.on("click", this.onClick, this);
35727         icon.on("dblclick", this.onDblClick, this);
35728         icon.on("contextmenu", this.onContextMenu, this);
35729         E.on(this.ecNode, "click", this.ecClick, this, true);
35730
35731         if(this.node.disabled){
35732             this.addClass("x-tree-node-disabled");
35733         }
35734         if(this.node.hidden){
35735             this.addClass("x-tree-node-disabled");
35736         }
35737         var ot = this.node.getOwnerTree();
35738         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35739         if(dd && (!this.node.isRoot || ot.rootVisible)){
35740             Roo.dd.Registry.register(this.elNode, {
35741                 node: this.node,
35742                 handles: this.getDDHandles(),
35743                 isHandle: false
35744             });
35745         }
35746     },
35747
35748     getDDHandles : function(){
35749         return [this.iconNode, this.textNode];
35750     },
35751
35752     hide : function(){
35753         if(this.rendered){
35754             this.wrap.style.display = "none";
35755         }
35756     },
35757
35758     show : function(){
35759         if(this.rendered){
35760             this.wrap.style.display = "";
35761         }
35762     },
35763
35764     onContextMenu : function(e){
35765         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35766             e.preventDefault();
35767             this.focus();
35768             this.fireEvent("contextmenu", this.node, e);
35769         }
35770     },
35771
35772     onClick : function(e){
35773         if(this.dropping){
35774             e.stopEvent();
35775             return;
35776         }
35777         if(this.fireEvent("beforeclick", this.node, e) !== false){
35778             if(!this.disabled && this.node.attributes.href){
35779                 this.fireEvent("click", this.node, e);
35780                 return;
35781             }
35782             e.preventDefault();
35783             if(this.disabled){
35784                 return;
35785             }
35786
35787             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35788                 this.node.toggle();
35789             }
35790
35791             this.fireEvent("click", this.node, e);
35792         }else{
35793             e.stopEvent();
35794         }
35795     },
35796
35797     onDblClick : function(e){
35798         e.preventDefault();
35799         if(this.disabled){
35800             return;
35801         }
35802         if(this.checkbox){
35803             this.toggleCheck();
35804         }
35805         if(!this.animating && this.node.hasChildNodes()){
35806             this.node.toggle();
35807         }
35808         this.fireEvent("dblclick", this.node, e);
35809     },
35810
35811     onCheckChange : function(){
35812         var checked = this.checkbox.checked;
35813         this.node.attributes.checked = checked;
35814         this.fireEvent('checkchange', this.node, checked);
35815     },
35816
35817     ecClick : function(e){
35818         if(!this.animating && this.node.hasChildNodes()){
35819             this.node.toggle();
35820         }
35821     },
35822
35823     startDrop : function(){
35824         this.dropping = true;
35825     },
35826
35827     // delayed drop so the click event doesn't get fired on a drop
35828     endDrop : function(){
35829        setTimeout(function(){
35830            this.dropping = false;
35831        }.createDelegate(this), 50);
35832     },
35833
35834     expand : function(){
35835         this.updateExpandIcon();
35836         this.ctNode.style.display = "";
35837     },
35838
35839     focus : function(){
35840         if(!this.node.preventHScroll){
35841             try{this.anchor.focus();
35842             }catch(e){}
35843         }else if(!Roo.isIE){
35844             try{
35845                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35846                 var l = noscroll.scrollLeft;
35847                 this.anchor.focus();
35848                 noscroll.scrollLeft = l;
35849             }catch(e){}
35850         }
35851     },
35852
35853     toggleCheck : function(value){
35854         var cb = this.checkbox;
35855         if(cb){
35856             cb.checked = (value === undefined ? !cb.checked : value);
35857         }
35858     },
35859
35860     blur : function(){
35861         try{
35862             this.anchor.blur();
35863         }catch(e){}
35864     },
35865
35866     animExpand : function(callback){
35867         var ct = Roo.get(this.ctNode);
35868         ct.stopFx();
35869         if(!this.node.hasChildNodes()){
35870             this.updateExpandIcon();
35871             this.ctNode.style.display = "";
35872             Roo.callback(callback);
35873             return;
35874         }
35875         this.animating = true;
35876         this.updateExpandIcon();
35877
35878         ct.slideIn('t', {
35879            callback : function(){
35880                this.animating = false;
35881                Roo.callback(callback);
35882             },
35883             scope: this,
35884             duration: this.node.ownerTree.duration || .25
35885         });
35886     },
35887
35888     highlight : function(){
35889         var tree = this.node.getOwnerTree();
35890         Roo.fly(this.wrap).highlight(
35891             tree.hlColor || "C3DAF9",
35892             {endColor: tree.hlBaseColor}
35893         );
35894     },
35895
35896     collapse : function(){
35897         this.updateExpandIcon();
35898         this.ctNode.style.display = "none";
35899     },
35900
35901     animCollapse : function(callback){
35902         var ct = Roo.get(this.ctNode);
35903         ct.enableDisplayMode('block');
35904         ct.stopFx();
35905
35906         this.animating = true;
35907         this.updateExpandIcon();
35908
35909         ct.slideOut('t', {
35910             callback : function(){
35911                this.animating = false;
35912                Roo.callback(callback);
35913             },
35914             scope: this,
35915             duration: this.node.ownerTree.duration || .25
35916         });
35917     },
35918
35919     getContainer : function(){
35920         return this.ctNode;
35921     },
35922
35923     getEl : function(){
35924         return this.wrap;
35925     },
35926
35927     appendDDGhost : function(ghostNode){
35928         ghostNode.appendChild(this.elNode.cloneNode(true));
35929     },
35930
35931     getDDRepairXY : function(){
35932         return Roo.lib.Dom.getXY(this.iconNode);
35933     },
35934
35935     onRender : function(){
35936         this.render();
35937     },
35938
35939     render : function(bulkRender){
35940         var n = this.node, a = n.attributes;
35941         var targetNode = n.parentNode ?
35942               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35943
35944         if(!this.rendered){
35945             this.rendered = true;
35946
35947             this.renderElements(n, a, targetNode, bulkRender);
35948
35949             if(a.qtip){
35950                if(this.textNode.setAttributeNS){
35951                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35952                    if(a.qtipTitle){
35953                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35954                    }
35955                }else{
35956                    this.textNode.setAttribute("ext:qtip", a.qtip);
35957                    if(a.qtipTitle){
35958                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35959                    }
35960                }
35961             }else if(a.qtipCfg){
35962                 a.qtipCfg.target = Roo.id(this.textNode);
35963                 Roo.QuickTips.register(a.qtipCfg);
35964             }
35965             this.initEvents();
35966             if(!this.node.expanded){
35967                 this.updateExpandIcon();
35968             }
35969         }else{
35970             if(bulkRender === true) {
35971                 targetNode.appendChild(this.wrap);
35972             }
35973         }
35974     },
35975
35976     renderElements : function(n, a, targetNode, bulkRender)
35977     {
35978         // add some indent caching, this helps performance when rendering a large tree
35979         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35980         var t = n.getOwnerTree();
35981         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35982         if (typeof(n.attributes.html) != 'undefined') {
35983             txt = n.attributes.html;
35984         }
35985         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35986         var cb = typeof a.checked == 'boolean';
35987         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35988         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35989             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35990             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35991             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35992             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35993             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35994              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35995                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35996             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35997             "</li>"];
35998
35999         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36000             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36001                                 n.nextSibling.ui.getEl(), buf.join(""));
36002         }else{
36003             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36004         }
36005
36006         this.elNode = this.wrap.childNodes[0];
36007         this.ctNode = this.wrap.childNodes[1];
36008         var cs = this.elNode.childNodes;
36009         this.indentNode = cs[0];
36010         this.ecNode = cs[1];
36011         this.iconNode = cs[2];
36012         var index = 3;
36013         if(cb){
36014             this.checkbox = cs[3];
36015             index++;
36016         }
36017         this.anchor = cs[index];
36018         this.textNode = cs[index].firstChild;
36019     },
36020
36021     getAnchor : function(){
36022         return this.anchor;
36023     },
36024
36025     getTextEl : function(){
36026         return this.textNode;
36027     },
36028
36029     getIconEl : function(){
36030         return this.iconNode;
36031     },
36032
36033     isChecked : function(){
36034         return this.checkbox ? this.checkbox.checked : false;
36035     },
36036
36037     updateExpandIcon : function(){
36038         if(this.rendered){
36039             var n = this.node, c1, c2;
36040             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36041             var hasChild = n.hasChildNodes();
36042             if(hasChild){
36043                 if(n.expanded){
36044                     cls += "-minus";
36045                     c1 = "x-tree-node-collapsed";
36046                     c2 = "x-tree-node-expanded";
36047                 }else{
36048                     cls += "-plus";
36049                     c1 = "x-tree-node-expanded";
36050                     c2 = "x-tree-node-collapsed";
36051                 }
36052                 if(this.wasLeaf){
36053                     this.removeClass("x-tree-node-leaf");
36054                     this.wasLeaf = false;
36055                 }
36056                 if(this.c1 != c1 || this.c2 != c2){
36057                     Roo.fly(this.elNode).replaceClass(c1, c2);
36058                     this.c1 = c1; this.c2 = c2;
36059                 }
36060             }else{
36061                 // this changes non-leafs into leafs if they have no children.
36062                 // it's not very rational behaviour..
36063                 
36064                 if(!this.wasLeaf && this.node.leaf){
36065                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36066                     delete this.c1;
36067                     delete this.c2;
36068                     this.wasLeaf = true;
36069                 }
36070             }
36071             var ecc = "x-tree-ec-icon "+cls;
36072             if(this.ecc != ecc){
36073                 this.ecNode.className = ecc;
36074                 this.ecc = ecc;
36075             }
36076         }
36077     },
36078
36079     getChildIndent : function(){
36080         if(!this.childIndent){
36081             var buf = [];
36082             var p = this.node;
36083             while(p){
36084                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36085                     if(!p.isLast()) {
36086                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36087                     } else {
36088                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36089                     }
36090                 }
36091                 p = p.parentNode;
36092             }
36093             this.childIndent = buf.join("");
36094         }
36095         return this.childIndent;
36096     },
36097
36098     renderIndent : function(){
36099         if(this.rendered){
36100             var indent = "";
36101             var p = this.node.parentNode;
36102             if(p){
36103                 indent = p.ui.getChildIndent();
36104             }
36105             if(this.indentMarkup != indent){ // don't rerender if not required
36106                 this.indentNode.innerHTML = indent;
36107                 this.indentMarkup = indent;
36108             }
36109             this.updateExpandIcon();
36110         }
36111     }
36112 };
36113
36114 Roo.tree.RootTreeNodeUI = function(){
36115     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36116 };
36117 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36118     render : function(){
36119         if(!this.rendered){
36120             var targetNode = this.node.ownerTree.innerCt.dom;
36121             this.node.expanded = true;
36122             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36123             this.wrap = this.ctNode = targetNode.firstChild;
36124         }
36125     },
36126     collapse : function(){
36127     },
36128     expand : function(){
36129     }
36130 });/*
36131  * Based on:
36132  * Ext JS Library 1.1.1
36133  * Copyright(c) 2006-2007, Ext JS, LLC.
36134  *
36135  * Originally Released Under LGPL - original licence link has changed is not relivant.
36136  *
36137  * Fork - LGPL
36138  * <script type="text/javascript">
36139  */
36140 /**
36141  * @class Roo.tree.TreeLoader
36142  * @extends Roo.util.Observable
36143  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36144  * nodes from a specified URL. The response must be a javascript Array definition
36145  * who's elements are node definition objects. eg:
36146  * <pre><code>
36147 {  success : true,
36148    data :      [
36149    
36150     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36151     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36152     ]
36153 }
36154
36155
36156 </code></pre>
36157  * <br><br>
36158  * The old style respose with just an array is still supported, but not recommended.
36159  * <br><br>
36160  *
36161  * A server request is sent, and child nodes are loaded only when a node is expanded.
36162  * The loading node's id is passed to the server under the parameter name "node" to
36163  * enable the server to produce the correct child nodes.
36164  * <br><br>
36165  * To pass extra parameters, an event handler may be attached to the "beforeload"
36166  * event, and the parameters specified in the TreeLoader's baseParams property:
36167  * <pre><code>
36168     myTreeLoader.on("beforeload", function(treeLoader, node) {
36169         this.baseParams.category = node.attributes.category;
36170     }, this);
36171     
36172 </code></pre>
36173  *
36174  * This would pass an HTTP parameter called "category" to the server containing
36175  * the value of the Node's "category" attribute.
36176  * @constructor
36177  * Creates a new Treeloader.
36178  * @param {Object} config A config object containing config properties.
36179  */
36180 Roo.tree.TreeLoader = function(config){
36181     this.baseParams = {};
36182     this.requestMethod = "POST";
36183     Roo.apply(this, config);
36184
36185     this.addEvents({
36186     
36187         /**
36188          * @event beforeload
36189          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36190          * @param {Object} This TreeLoader object.
36191          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36192          * @param {Object} callback The callback function specified in the {@link #load} call.
36193          */
36194         beforeload : true,
36195         /**
36196          * @event load
36197          * Fires when the node has been successfuly loaded.
36198          * @param {Object} This TreeLoader object.
36199          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36200          * @param {Object} response The response object containing the data from the server.
36201          */
36202         load : true,
36203         /**
36204          * @event loadexception
36205          * Fires if the network request failed.
36206          * @param {Object} This TreeLoader object.
36207          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36208          * @param {Object} response The response object containing the data from the server.
36209          */
36210         loadexception : true,
36211         /**
36212          * @event create
36213          * Fires before a node is created, enabling you to return custom Node types 
36214          * @param {Object} This TreeLoader object.
36215          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36216          */
36217         create : true
36218     });
36219
36220     Roo.tree.TreeLoader.superclass.constructor.call(this);
36221 };
36222
36223 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36224     /**
36225     * @cfg {String} dataUrl The URL from which to request a Json string which
36226     * specifies an array of node definition object representing the child nodes
36227     * to be loaded.
36228     */
36229     /**
36230     * @cfg {String} requestMethod either GET or POST
36231     * defaults to POST (due to BC)
36232     * to be loaded.
36233     */
36234     /**
36235     * @cfg {Object} baseParams (optional) An object containing properties which
36236     * specify HTTP parameters to be passed to each request for child nodes.
36237     */
36238     /**
36239     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36240     * created by this loader. If the attributes sent by the server have an attribute in this object,
36241     * they take priority.
36242     */
36243     /**
36244     * @cfg {Object} uiProviders (optional) An object containing properties which
36245     * 
36246     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36247     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36248     * <i>uiProvider</i> attribute of a returned child node is a string rather
36249     * than a reference to a TreeNodeUI implementation, this that string value
36250     * is used as a property name in the uiProviders object. You can define the provider named
36251     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36252     */
36253     uiProviders : {},
36254
36255     /**
36256     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36257     * child nodes before loading.
36258     */
36259     clearOnLoad : true,
36260
36261     /**
36262     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36263     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36264     * Grid query { data : [ .....] }
36265     */
36266     
36267     root : false,
36268      /**
36269     * @cfg {String} queryParam (optional) 
36270     * Name of the query as it will be passed on the querystring (defaults to 'node')
36271     * eg. the request will be ?node=[id]
36272     */
36273     
36274     
36275     queryParam: false,
36276     
36277     /**
36278      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36279      * This is called automatically when a node is expanded, but may be used to reload
36280      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36281      * @param {Roo.tree.TreeNode} node
36282      * @param {Function} callback
36283      */
36284     load : function(node, callback){
36285         if(this.clearOnLoad){
36286             while(node.firstChild){
36287                 node.removeChild(node.firstChild);
36288             }
36289         }
36290         if(node.attributes.children){ // preloaded json children
36291             var cs = node.attributes.children;
36292             for(var i = 0, len = cs.length; i < len; i++){
36293                 node.appendChild(this.createNode(cs[i]));
36294             }
36295             if(typeof callback == "function"){
36296                 callback();
36297             }
36298         }else if(this.dataUrl){
36299             this.requestData(node, callback);
36300         }
36301     },
36302
36303     getParams: function(node){
36304         var buf = [], bp = this.baseParams;
36305         for(var key in bp){
36306             if(typeof bp[key] != "function"){
36307                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36308             }
36309         }
36310         var n = this.queryParam === false ? 'node' : this.queryParam;
36311         buf.push(n + "=", encodeURIComponent(node.id));
36312         return buf.join("");
36313     },
36314
36315     requestData : function(node, callback){
36316         if(this.fireEvent("beforeload", this, node, callback) !== false){
36317             this.transId = Roo.Ajax.request({
36318                 method:this.requestMethod,
36319                 url: this.dataUrl||this.url,
36320                 success: this.handleResponse,
36321                 failure: this.handleFailure,
36322                 scope: this,
36323                 argument: {callback: callback, node: node},
36324                 params: this.getParams(node)
36325             });
36326         }else{
36327             // if the load is cancelled, make sure we notify
36328             // the node that we are done
36329             if(typeof callback == "function"){
36330                 callback();
36331             }
36332         }
36333     },
36334
36335     isLoading : function(){
36336         return this.transId ? true : false;
36337     },
36338
36339     abort : function(){
36340         if(this.isLoading()){
36341             Roo.Ajax.abort(this.transId);
36342         }
36343     },
36344
36345     // private
36346     createNode : function(attr)
36347     {
36348         // apply baseAttrs, nice idea Corey!
36349         if(this.baseAttrs){
36350             Roo.applyIf(attr, this.baseAttrs);
36351         }
36352         if(this.applyLoader !== false){
36353             attr.loader = this;
36354         }
36355         // uiProvider = depreciated..
36356         
36357         if(typeof(attr.uiProvider) == 'string'){
36358            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36359                 /**  eval:var:attr */ eval(attr.uiProvider);
36360         }
36361         if(typeof(this.uiProviders['default']) != 'undefined') {
36362             attr.uiProvider = this.uiProviders['default'];
36363         }
36364         
36365         this.fireEvent('create', this, attr);
36366         
36367         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36368         return(attr.leaf ?
36369                         new Roo.tree.TreeNode(attr) :
36370                         new Roo.tree.AsyncTreeNode(attr));
36371     },
36372
36373     processResponse : function(response, node, callback)
36374     {
36375         var json = response.responseText;
36376         try {
36377             
36378             var o = Roo.decode(json);
36379             
36380             if (this.root === false && typeof(o.success) != undefined) {
36381                 this.root = 'data'; // the default behaviour for list like data..
36382                 }
36383                 
36384             if (this.root !== false &&  !o.success) {
36385                 // it's a failure condition.
36386                 var a = response.argument;
36387                 this.fireEvent("loadexception", this, a.node, response);
36388                 Roo.log("Load failed - should have a handler really");
36389                 return;
36390             }
36391             
36392             
36393             
36394             if (this.root !== false) {
36395                  o = o[this.root];
36396             }
36397             
36398             for(var i = 0, len = o.length; i < len; i++){
36399                 var n = this.createNode(o[i]);
36400                 if(n){
36401                     node.appendChild(n);
36402                 }
36403             }
36404             if(typeof callback == "function"){
36405                 callback(this, node);
36406             }
36407         }catch(e){
36408             this.handleFailure(response);
36409         }
36410     },
36411
36412     handleResponse : function(response){
36413         this.transId = false;
36414         var a = response.argument;
36415         this.processResponse(response, a.node, a.callback);
36416         this.fireEvent("load", this, a.node, response);
36417     },
36418
36419     handleFailure : function(response)
36420     {
36421         // should handle failure better..
36422         this.transId = false;
36423         var a = response.argument;
36424         this.fireEvent("loadexception", this, a.node, response);
36425         if(typeof a.callback == "function"){
36426             a.callback(this, a.node);
36427         }
36428     }
36429 });/*
36430  * Based on:
36431  * Ext JS Library 1.1.1
36432  * Copyright(c) 2006-2007, Ext JS, LLC.
36433  *
36434  * Originally Released Under LGPL - original licence link has changed is not relivant.
36435  *
36436  * Fork - LGPL
36437  * <script type="text/javascript">
36438  */
36439
36440 /**
36441 * @class Roo.tree.TreeFilter
36442 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36443 * @param {TreePanel} tree
36444 * @param {Object} config (optional)
36445  */
36446 Roo.tree.TreeFilter = function(tree, config){
36447     this.tree = tree;
36448     this.filtered = {};
36449     Roo.apply(this, config);
36450 };
36451
36452 Roo.tree.TreeFilter.prototype = {
36453     clearBlank:false,
36454     reverse:false,
36455     autoClear:false,
36456     remove:false,
36457
36458      /**
36459      * Filter the data by a specific attribute.
36460      * @param {String/RegExp} value Either string that the attribute value
36461      * should start with or a RegExp to test against the attribute
36462      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36463      * @param {TreeNode} startNode (optional) The node to start the filter at.
36464      */
36465     filter : function(value, attr, startNode){
36466         attr = attr || "text";
36467         var f;
36468         if(typeof value == "string"){
36469             var vlen = value.length;
36470             // auto clear empty filter
36471             if(vlen == 0 && this.clearBlank){
36472                 this.clear();
36473                 return;
36474             }
36475             value = value.toLowerCase();
36476             f = function(n){
36477                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36478             };
36479         }else if(value.exec){ // regex?
36480             f = function(n){
36481                 return value.test(n.attributes[attr]);
36482             };
36483         }else{
36484             throw 'Illegal filter type, must be string or regex';
36485         }
36486         this.filterBy(f, null, startNode);
36487         },
36488
36489     /**
36490      * Filter by a function. The passed function will be called with each
36491      * node in the tree (or from the startNode). If the function returns true, the node is kept
36492      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36493      * @param {Function} fn The filter function
36494      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36495      */
36496     filterBy : function(fn, scope, startNode){
36497         startNode = startNode || this.tree.root;
36498         if(this.autoClear){
36499             this.clear();
36500         }
36501         var af = this.filtered, rv = this.reverse;
36502         var f = function(n){
36503             if(n == startNode){
36504                 return true;
36505             }
36506             if(af[n.id]){
36507                 return false;
36508             }
36509             var m = fn.call(scope || n, n);
36510             if(!m || rv){
36511                 af[n.id] = n;
36512                 n.ui.hide();
36513                 return false;
36514             }
36515             return true;
36516         };
36517         startNode.cascade(f);
36518         if(this.remove){
36519            for(var id in af){
36520                if(typeof id != "function"){
36521                    var n = af[id];
36522                    if(n && n.parentNode){
36523                        n.parentNode.removeChild(n);
36524                    }
36525                }
36526            }
36527         }
36528     },
36529
36530     /**
36531      * Clears the current filter. Note: with the "remove" option
36532      * set a filter cannot be cleared.
36533      */
36534     clear : function(){
36535         var t = this.tree;
36536         var af = this.filtered;
36537         for(var id in af){
36538             if(typeof id != "function"){
36539                 var n = af[id];
36540                 if(n){
36541                     n.ui.show();
36542                 }
36543             }
36544         }
36545         this.filtered = {};
36546     }
36547 };
36548 /*
36549  * Based on:
36550  * Ext JS Library 1.1.1
36551  * Copyright(c) 2006-2007, Ext JS, LLC.
36552  *
36553  * Originally Released Under LGPL - original licence link has changed is not relivant.
36554  *
36555  * Fork - LGPL
36556  * <script type="text/javascript">
36557  */
36558  
36559
36560 /**
36561  * @class Roo.tree.TreeSorter
36562  * Provides sorting of nodes in a TreePanel
36563  * 
36564  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36565  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36566  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36567  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36568  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36569  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36570  * @constructor
36571  * @param {TreePanel} tree
36572  * @param {Object} config
36573  */
36574 Roo.tree.TreeSorter = function(tree, config){
36575     Roo.apply(this, config);
36576     tree.on("beforechildrenrendered", this.doSort, this);
36577     tree.on("append", this.updateSort, this);
36578     tree.on("insert", this.updateSort, this);
36579     
36580     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36581     var p = this.property || "text";
36582     var sortType = this.sortType;
36583     var fs = this.folderSort;
36584     var cs = this.caseSensitive === true;
36585     var leafAttr = this.leafAttr || 'leaf';
36586
36587     this.sortFn = function(n1, n2){
36588         if(fs){
36589             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36590                 return 1;
36591             }
36592             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36593                 return -1;
36594             }
36595         }
36596         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36597         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36598         if(v1 < v2){
36599                         return dsc ? +1 : -1;
36600                 }else if(v1 > v2){
36601                         return dsc ? -1 : +1;
36602         }else{
36603                 return 0;
36604         }
36605     };
36606 };
36607
36608 Roo.tree.TreeSorter.prototype = {
36609     doSort : function(node){
36610         node.sort(this.sortFn);
36611     },
36612     
36613     compareNodes : function(n1, n2){
36614         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36615     },
36616     
36617     updateSort : function(tree, node){
36618         if(node.childrenRendered){
36619             this.doSort.defer(1, this, [node]);
36620         }
36621     }
36622 };/*
36623  * Based on:
36624  * Ext JS Library 1.1.1
36625  * Copyright(c) 2006-2007, Ext JS, LLC.
36626  *
36627  * Originally Released Under LGPL - original licence link has changed is not relivant.
36628  *
36629  * Fork - LGPL
36630  * <script type="text/javascript">
36631  */
36632
36633 if(Roo.dd.DropZone){
36634     
36635 Roo.tree.TreeDropZone = function(tree, config){
36636     this.allowParentInsert = false;
36637     this.allowContainerDrop = false;
36638     this.appendOnly = false;
36639     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36640     this.tree = tree;
36641     this.lastInsertClass = "x-tree-no-status";
36642     this.dragOverData = {};
36643 };
36644
36645 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36646     ddGroup : "TreeDD",
36647     scroll:  true,
36648     
36649     expandDelay : 1000,
36650     
36651     expandNode : function(node){
36652         if(node.hasChildNodes() && !node.isExpanded()){
36653             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36654         }
36655     },
36656     
36657     queueExpand : function(node){
36658         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36659     },
36660     
36661     cancelExpand : function(){
36662         if(this.expandProcId){
36663             clearTimeout(this.expandProcId);
36664             this.expandProcId = false;
36665         }
36666     },
36667     
36668     isValidDropPoint : function(n, pt, dd, e, data){
36669         if(!n || !data){ return false; }
36670         var targetNode = n.node;
36671         var dropNode = data.node;
36672         // default drop rules
36673         if(!(targetNode && targetNode.isTarget && pt)){
36674             return false;
36675         }
36676         if(pt == "append" && targetNode.allowChildren === false){
36677             return false;
36678         }
36679         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36680             return false;
36681         }
36682         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36683             return false;
36684         }
36685         // reuse the object
36686         var overEvent = this.dragOverData;
36687         overEvent.tree = this.tree;
36688         overEvent.target = targetNode;
36689         overEvent.data = data;
36690         overEvent.point = pt;
36691         overEvent.source = dd;
36692         overEvent.rawEvent = e;
36693         overEvent.dropNode = dropNode;
36694         overEvent.cancel = false;  
36695         var result = this.tree.fireEvent("nodedragover", overEvent);
36696         return overEvent.cancel === false && result !== false;
36697     },
36698     
36699     getDropPoint : function(e, n, dd)
36700     {
36701         var tn = n.node;
36702         if(tn.isRoot){
36703             return tn.allowChildren !== false ? "append" : false; // always append for root
36704         }
36705         var dragEl = n.ddel;
36706         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36707         var y = Roo.lib.Event.getPageY(e);
36708         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36709         
36710         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36711         var noAppend = tn.allowChildren === false;
36712         if(this.appendOnly || tn.parentNode.allowChildren === false){
36713             return noAppend ? false : "append";
36714         }
36715         var noBelow = false;
36716         if(!this.allowParentInsert){
36717             noBelow = tn.hasChildNodes() && tn.isExpanded();
36718         }
36719         var q = (b - t) / (noAppend ? 2 : 3);
36720         if(y >= t && y < (t + q)){
36721             return "above";
36722         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36723             return "below";
36724         }else{
36725             return "append";
36726         }
36727     },
36728     
36729     onNodeEnter : function(n, dd, e, data)
36730     {
36731         this.cancelExpand();
36732     },
36733     
36734     onNodeOver : function(n, dd, e, data)
36735     {
36736        
36737         var pt = this.getDropPoint(e, n, dd);
36738         var node = n.node;
36739         
36740         // auto node expand check
36741         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36742             this.queueExpand(node);
36743         }else if(pt != "append"){
36744             this.cancelExpand();
36745         }
36746         
36747         // set the insert point style on the target node
36748         var returnCls = this.dropNotAllowed;
36749         if(this.isValidDropPoint(n, pt, dd, e, data)){
36750            if(pt){
36751                var el = n.ddel;
36752                var cls;
36753                if(pt == "above"){
36754                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36755                    cls = "x-tree-drag-insert-above";
36756                }else if(pt == "below"){
36757                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36758                    cls = "x-tree-drag-insert-below";
36759                }else{
36760                    returnCls = "x-tree-drop-ok-append";
36761                    cls = "x-tree-drag-append";
36762                }
36763                if(this.lastInsertClass != cls){
36764                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36765                    this.lastInsertClass = cls;
36766                }
36767            }
36768        }
36769        return returnCls;
36770     },
36771     
36772     onNodeOut : function(n, dd, e, data){
36773         
36774         this.cancelExpand();
36775         this.removeDropIndicators(n);
36776     },
36777     
36778     onNodeDrop : function(n, dd, e, data){
36779         var point = this.getDropPoint(e, n, dd);
36780         var targetNode = n.node;
36781         targetNode.ui.startDrop();
36782         if(!this.isValidDropPoint(n, point, dd, e, data)){
36783             targetNode.ui.endDrop();
36784             return false;
36785         }
36786         // first try to find the drop node
36787         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36788         var dropEvent = {
36789             tree : this.tree,
36790             target: targetNode,
36791             data: data,
36792             point: point,
36793             source: dd,
36794             rawEvent: e,
36795             dropNode: dropNode,
36796             cancel: !dropNode   
36797         };
36798         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36799         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36800             targetNode.ui.endDrop();
36801             return false;
36802         }
36803         // allow target changing
36804         targetNode = dropEvent.target;
36805         if(point == "append" && !targetNode.isExpanded()){
36806             targetNode.expand(false, null, function(){
36807                 this.completeDrop(dropEvent);
36808             }.createDelegate(this));
36809         }else{
36810             this.completeDrop(dropEvent);
36811         }
36812         return true;
36813     },
36814     
36815     completeDrop : function(de){
36816         var ns = de.dropNode, p = de.point, t = de.target;
36817         if(!(ns instanceof Array)){
36818             ns = [ns];
36819         }
36820         var n;
36821         for(var i = 0, len = ns.length; i < len; i++){
36822             n = ns[i];
36823             if(p == "above"){
36824                 t.parentNode.insertBefore(n, t);
36825             }else if(p == "below"){
36826                 t.parentNode.insertBefore(n, t.nextSibling);
36827             }else{
36828                 t.appendChild(n);
36829             }
36830         }
36831         n.ui.focus();
36832         if(this.tree.hlDrop){
36833             n.ui.highlight();
36834         }
36835         t.ui.endDrop();
36836         this.tree.fireEvent("nodedrop", de);
36837     },
36838     
36839     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36840         if(this.tree.hlDrop){
36841             dropNode.ui.focus();
36842             dropNode.ui.highlight();
36843         }
36844         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36845     },
36846     
36847     getTree : function(){
36848         return this.tree;
36849     },
36850     
36851     removeDropIndicators : function(n){
36852         if(n && n.ddel){
36853             var el = n.ddel;
36854             Roo.fly(el).removeClass([
36855                     "x-tree-drag-insert-above",
36856                     "x-tree-drag-insert-below",
36857                     "x-tree-drag-append"]);
36858             this.lastInsertClass = "_noclass";
36859         }
36860     },
36861     
36862     beforeDragDrop : function(target, e, id){
36863         this.cancelExpand();
36864         return true;
36865     },
36866     
36867     afterRepair : function(data){
36868         if(data && Roo.enableFx){
36869             data.node.ui.highlight();
36870         }
36871         this.hideProxy();
36872     } 
36873     
36874 });
36875
36876 }
36877 /*
36878  * Based on:
36879  * Ext JS Library 1.1.1
36880  * Copyright(c) 2006-2007, Ext JS, LLC.
36881  *
36882  * Originally Released Under LGPL - original licence link has changed is not relivant.
36883  *
36884  * Fork - LGPL
36885  * <script type="text/javascript">
36886  */
36887  
36888
36889 if(Roo.dd.DragZone){
36890 Roo.tree.TreeDragZone = function(tree, config){
36891     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36892     this.tree = tree;
36893 };
36894
36895 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36896     ddGroup : "TreeDD",
36897    
36898     onBeforeDrag : function(data, e){
36899         var n = data.node;
36900         return n && n.draggable && !n.disabled;
36901     },
36902      
36903     
36904     onInitDrag : function(e){
36905         var data = this.dragData;
36906         this.tree.getSelectionModel().select(data.node);
36907         this.proxy.update("");
36908         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36909         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36910     },
36911     
36912     getRepairXY : function(e, data){
36913         return data.node.ui.getDDRepairXY();
36914     },
36915     
36916     onEndDrag : function(data, e){
36917         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36918         
36919         
36920     },
36921     
36922     onValidDrop : function(dd, e, id){
36923         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36924         this.hideProxy();
36925     },
36926     
36927     beforeInvalidDrop : function(e, id){
36928         // this scrolls the original position back into view
36929         var sm = this.tree.getSelectionModel();
36930         sm.clearSelections();
36931         sm.select(this.dragData.node);
36932     }
36933 });
36934 }/*
36935  * Based on:
36936  * Ext JS Library 1.1.1
36937  * Copyright(c) 2006-2007, Ext JS, LLC.
36938  *
36939  * Originally Released Under LGPL - original licence link has changed is not relivant.
36940  *
36941  * Fork - LGPL
36942  * <script type="text/javascript">
36943  */
36944 /**
36945  * @class Roo.tree.TreeEditor
36946  * @extends Roo.Editor
36947  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36948  * as the editor field.
36949  * @constructor
36950  * @param {Object} config (used to be the tree panel.)
36951  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36952  * 
36953  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36954  * @cfg {Roo.form.TextField|Object} field The field configuration
36955  *
36956  * 
36957  */
36958 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36959     var tree = config;
36960     var field;
36961     if (oldconfig) { // old style..
36962         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36963     } else {
36964         // new style..
36965         tree = config.tree;
36966         config.field = config.field  || {};
36967         config.field.xtype = 'TextField';
36968         field = Roo.factory(config.field, Roo.form);
36969     }
36970     config = config || {};
36971     
36972     
36973     this.addEvents({
36974         /**
36975          * @event beforenodeedit
36976          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36977          * false from the handler of this event.
36978          * @param {Editor} this
36979          * @param {Roo.tree.Node} node 
36980          */
36981         "beforenodeedit" : true
36982     });
36983     
36984     //Roo.log(config);
36985     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36986
36987     this.tree = tree;
36988
36989     tree.on('beforeclick', this.beforeNodeClick, this);
36990     tree.getTreeEl().on('mousedown', this.hide, this);
36991     this.on('complete', this.updateNode, this);
36992     this.on('beforestartedit', this.fitToTree, this);
36993     this.on('startedit', this.bindScroll, this, {delay:10});
36994     this.on('specialkey', this.onSpecialKey, this);
36995 };
36996
36997 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36998     /**
36999      * @cfg {String} alignment
37000      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37001      */
37002     alignment: "l-l",
37003     // inherit
37004     autoSize: false,
37005     /**
37006      * @cfg {Boolean} hideEl
37007      * True to hide the bound element while the editor is displayed (defaults to false)
37008      */
37009     hideEl : false,
37010     /**
37011      * @cfg {String} cls
37012      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37013      */
37014     cls: "x-small-editor x-tree-editor",
37015     /**
37016      * @cfg {Boolean} shim
37017      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37018      */
37019     shim:false,
37020     // inherit
37021     shadow:"frame",
37022     /**
37023      * @cfg {Number} maxWidth
37024      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37025      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37026      * scroll and client offsets into account prior to each edit.
37027      */
37028     maxWidth: 250,
37029
37030     editDelay : 350,
37031
37032     // private
37033     fitToTree : function(ed, el){
37034         var td = this.tree.getTreeEl().dom, nd = el.dom;
37035         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37036             td.scrollLeft = nd.offsetLeft;
37037         }
37038         var w = Math.min(
37039                 this.maxWidth,
37040                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37041         this.setSize(w, '');
37042         
37043         return this.fireEvent('beforenodeedit', this, this.editNode);
37044         
37045     },
37046
37047     // private
37048     triggerEdit : function(node){
37049         this.completeEdit();
37050         this.editNode = node;
37051         this.startEdit(node.ui.textNode, node.text);
37052     },
37053
37054     // private
37055     bindScroll : function(){
37056         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37057     },
37058
37059     // private
37060     beforeNodeClick : function(node, e){
37061         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37062         this.lastClick = new Date();
37063         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37064             e.stopEvent();
37065             this.triggerEdit(node);
37066             return false;
37067         }
37068         return true;
37069     },
37070
37071     // private
37072     updateNode : function(ed, value){
37073         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37074         this.editNode.setText(value);
37075     },
37076
37077     // private
37078     onHide : function(){
37079         Roo.tree.TreeEditor.superclass.onHide.call(this);
37080         if(this.editNode){
37081             this.editNode.ui.focus();
37082         }
37083     },
37084
37085     // private
37086     onSpecialKey : function(field, e){
37087         var k = e.getKey();
37088         if(k == e.ESC){
37089             e.stopEvent();
37090             this.cancelEdit();
37091         }else if(k == e.ENTER && !e.hasModifier()){
37092             e.stopEvent();
37093             this.completeEdit();
37094         }
37095     }
37096 });//<Script type="text/javascript">
37097 /*
37098  * Based on:
37099  * Ext JS Library 1.1.1
37100  * Copyright(c) 2006-2007, Ext JS, LLC.
37101  *
37102  * Originally Released Under LGPL - original licence link has changed is not relivant.
37103  *
37104  * Fork - LGPL
37105  * <script type="text/javascript">
37106  */
37107  
37108 /**
37109  * Not documented??? - probably should be...
37110  */
37111
37112 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37113     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37114     
37115     renderElements : function(n, a, targetNode, bulkRender){
37116         //consel.log("renderElements?");
37117         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37118
37119         var t = n.getOwnerTree();
37120         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37121         
37122         var cols = t.columns;
37123         var bw = t.borderWidth;
37124         var c = cols[0];
37125         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37126          var cb = typeof a.checked == "boolean";
37127         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37128         var colcls = 'x-t-' + tid + '-c0';
37129         var buf = [
37130             '<li class="x-tree-node">',
37131             
37132                 
37133                 '<div class="x-tree-node-el ', a.cls,'">',
37134                     // extran...
37135                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37136                 
37137                 
37138                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37139                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37140                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37141                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37142                            (a.iconCls ? ' '+a.iconCls : ''),
37143                            '" unselectable="on" />',
37144                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37145                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37146                              
37147                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37148                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37149                             '<span unselectable="on" qtip="' + tx + '">',
37150                              tx,
37151                              '</span></a>' ,
37152                     '</div>',
37153                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37154                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37155                  ];
37156         for(var i = 1, len = cols.length; i < len; i++){
37157             c = cols[i];
37158             colcls = 'x-t-' + tid + '-c' +i;
37159             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37160             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37161                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37162                       "</div>");
37163          }
37164          
37165          buf.push(
37166             '</a>',
37167             '<div class="x-clear"></div></div>',
37168             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37169             "</li>");
37170         
37171         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37172             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37173                                 n.nextSibling.ui.getEl(), buf.join(""));
37174         }else{
37175             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37176         }
37177         var el = this.wrap.firstChild;
37178         this.elRow = el;
37179         this.elNode = el.firstChild;
37180         this.ranchor = el.childNodes[1];
37181         this.ctNode = this.wrap.childNodes[1];
37182         var cs = el.firstChild.childNodes;
37183         this.indentNode = cs[0];
37184         this.ecNode = cs[1];
37185         this.iconNode = cs[2];
37186         var index = 3;
37187         if(cb){
37188             this.checkbox = cs[3];
37189             index++;
37190         }
37191         this.anchor = cs[index];
37192         
37193         this.textNode = cs[index].firstChild;
37194         
37195         //el.on("click", this.onClick, this);
37196         //el.on("dblclick", this.onDblClick, this);
37197         
37198         
37199        // console.log(this);
37200     },
37201     initEvents : function(){
37202         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37203         
37204             
37205         var a = this.ranchor;
37206
37207         var el = Roo.get(a);
37208
37209         if(Roo.isOpera){ // opera render bug ignores the CSS
37210             el.setStyle("text-decoration", "none");
37211         }
37212
37213         el.on("click", this.onClick, this);
37214         el.on("dblclick", this.onDblClick, this);
37215         el.on("contextmenu", this.onContextMenu, this);
37216         
37217     },
37218     
37219     /*onSelectedChange : function(state){
37220         if(state){
37221             this.focus();
37222             this.addClass("x-tree-selected");
37223         }else{
37224             //this.blur();
37225             this.removeClass("x-tree-selected");
37226         }
37227     },*/
37228     addClass : function(cls){
37229         if(this.elRow){
37230             Roo.fly(this.elRow).addClass(cls);
37231         }
37232         
37233     },
37234     
37235     
37236     removeClass : function(cls){
37237         if(this.elRow){
37238             Roo.fly(this.elRow).removeClass(cls);
37239         }
37240     }
37241
37242     
37243     
37244 });//<Script type="text/javascript">
37245
37246 /*
37247  * Based on:
37248  * Ext JS Library 1.1.1
37249  * Copyright(c) 2006-2007, Ext JS, LLC.
37250  *
37251  * Originally Released Under LGPL - original licence link has changed is not relivant.
37252  *
37253  * Fork - LGPL
37254  * <script type="text/javascript">
37255  */
37256  
37257
37258 /**
37259  * @class Roo.tree.ColumnTree
37260  * @extends Roo.data.TreePanel
37261  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37262  * @cfg {int} borderWidth  compined right/left border allowance
37263  * @constructor
37264  * @param {String/HTMLElement/Element} el The container element
37265  * @param {Object} config
37266  */
37267 Roo.tree.ColumnTree =  function(el, config)
37268 {
37269    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37270    this.addEvents({
37271         /**
37272         * @event resize
37273         * Fire this event on a container when it resizes
37274         * @param {int} w Width
37275         * @param {int} h Height
37276         */
37277        "resize" : true
37278     });
37279     this.on('resize', this.onResize, this);
37280 };
37281
37282 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37283     //lines:false,
37284     
37285     
37286     borderWidth: Roo.isBorderBox ? 0 : 2, 
37287     headEls : false,
37288     
37289     render : function(){
37290         // add the header.....
37291        
37292         Roo.tree.ColumnTree.superclass.render.apply(this);
37293         
37294         this.el.addClass('x-column-tree');
37295         
37296         this.headers = this.el.createChild(
37297             {cls:'x-tree-headers'},this.innerCt.dom);
37298    
37299         var cols = this.columns, c;
37300         var totalWidth = 0;
37301         this.headEls = [];
37302         var  len = cols.length;
37303         for(var i = 0; i < len; i++){
37304              c = cols[i];
37305              totalWidth += c.width;
37306             this.headEls.push(this.headers.createChild({
37307                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37308                  cn: {
37309                      cls:'x-tree-hd-text',
37310                      html: c.header
37311                  },
37312                  style:'width:'+(c.width-this.borderWidth)+'px;'
37313              }));
37314         }
37315         this.headers.createChild({cls:'x-clear'});
37316         // prevent floats from wrapping when clipped
37317         this.headers.setWidth(totalWidth);
37318         //this.innerCt.setWidth(totalWidth);
37319         this.innerCt.setStyle({ overflow: 'auto' });
37320         this.onResize(this.width, this.height);
37321              
37322         
37323     },
37324     onResize : function(w,h)
37325     {
37326         this.height = h;
37327         this.width = w;
37328         // resize cols..
37329         this.innerCt.setWidth(this.width);
37330         this.innerCt.setHeight(this.height-20);
37331         
37332         // headers...
37333         var cols = this.columns, c;
37334         var totalWidth = 0;
37335         var expEl = false;
37336         var len = cols.length;
37337         for(var i = 0; i < len; i++){
37338             c = cols[i];
37339             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37340                 // it's the expander..
37341                 expEl  = this.headEls[i];
37342                 continue;
37343             }
37344             totalWidth += c.width;
37345             
37346         }
37347         if (expEl) {
37348             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37349         }
37350         this.headers.setWidth(w-20);
37351
37352         
37353         
37354         
37355     }
37356 });
37357 /*
37358  * Based on:
37359  * Ext JS Library 1.1.1
37360  * Copyright(c) 2006-2007, Ext JS, LLC.
37361  *
37362  * Originally Released Under LGPL - original licence link has changed is not relivant.
37363  *
37364  * Fork - LGPL
37365  * <script type="text/javascript">
37366  */
37367  
37368 /**
37369  * @class Roo.menu.Menu
37370  * @extends Roo.util.Observable
37371  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37372  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37373  * @constructor
37374  * Creates a new Menu
37375  * @param {Object} config Configuration options
37376  */
37377 Roo.menu.Menu = function(config){
37378     
37379     Roo.menu.Menu.superclass.constructor.call(this, config);
37380     
37381     this.id = this.id || Roo.id();
37382     this.addEvents({
37383         /**
37384          * @event beforeshow
37385          * Fires before this menu is displayed
37386          * @param {Roo.menu.Menu} this
37387          */
37388         beforeshow : true,
37389         /**
37390          * @event beforehide
37391          * Fires before this menu is hidden
37392          * @param {Roo.menu.Menu} this
37393          */
37394         beforehide : true,
37395         /**
37396          * @event show
37397          * Fires after this menu is displayed
37398          * @param {Roo.menu.Menu} this
37399          */
37400         show : true,
37401         /**
37402          * @event hide
37403          * Fires after this menu is hidden
37404          * @param {Roo.menu.Menu} this
37405          */
37406         hide : true,
37407         /**
37408          * @event click
37409          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37410          * @param {Roo.menu.Menu} this
37411          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37412          * @param {Roo.EventObject} e
37413          */
37414         click : true,
37415         /**
37416          * @event mouseover
37417          * Fires when the mouse is hovering over this menu
37418          * @param {Roo.menu.Menu} this
37419          * @param {Roo.EventObject} e
37420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37421          */
37422         mouseover : true,
37423         /**
37424          * @event mouseout
37425          * Fires when the mouse exits this menu
37426          * @param {Roo.menu.Menu} this
37427          * @param {Roo.EventObject} e
37428          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37429          */
37430         mouseout : true,
37431         /**
37432          * @event itemclick
37433          * Fires when a menu item contained in this menu is clicked
37434          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37435          * @param {Roo.EventObject} e
37436          */
37437         itemclick: true
37438     });
37439     if (this.registerMenu) {
37440         Roo.menu.MenuMgr.register(this);
37441     }
37442     
37443     var mis = this.items;
37444     this.items = new Roo.util.MixedCollection();
37445     if(mis){
37446         this.add.apply(this, mis);
37447     }
37448 };
37449
37450 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37451     /**
37452      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37453      */
37454     minWidth : 120,
37455     /**
37456      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37457      * for bottom-right shadow (defaults to "sides")
37458      */
37459     shadow : "sides",
37460     /**
37461      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37462      * this menu (defaults to "tl-tr?")
37463      */
37464     subMenuAlign : "tl-tr?",
37465     /**
37466      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37467      * relative to its element of origin (defaults to "tl-bl?")
37468      */
37469     defaultAlign : "tl-bl?",
37470     /**
37471      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37472      */
37473     allowOtherMenus : false,
37474     /**
37475      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37476      */
37477     registerMenu : true,
37478
37479     hidden:true,
37480
37481     // private
37482     render : function(){
37483         if(this.el){
37484             return;
37485         }
37486         var el = this.el = new Roo.Layer({
37487             cls: "x-menu",
37488             shadow:this.shadow,
37489             constrain: false,
37490             parentEl: this.parentEl || document.body,
37491             zindex:15000
37492         });
37493
37494         this.keyNav = new Roo.menu.MenuNav(this);
37495
37496         if(this.plain){
37497             el.addClass("x-menu-plain");
37498         }
37499         if(this.cls){
37500             el.addClass(this.cls);
37501         }
37502         // generic focus element
37503         this.focusEl = el.createChild({
37504             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37505         });
37506         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37507         //disabling touch- as it's causing issues ..
37508         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37509         ul.on('click'   , this.onClick, this);
37510         
37511         
37512         ul.on("mouseover", this.onMouseOver, this);
37513         ul.on("mouseout", this.onMouseOut, this);
37514         this.items.each(function(item){
37515             if (item.hidden) {
37516                 return;
37517             }
37518             
37519             var li = document.createElement("li");
37520             li.className = "x-menu-list-item";
37521             ul.dom.appendChild(li);
37522             item.render(li, this);
37523         }, this);
37524         this.ul = ul;
37525         this.autoWidth();
37526     },
37527
37528     // private
37529     autoWidth : function(){
37530         var el = this.el, ul = this.ul;
37531         if(!el){
37532             return;
37533         }
37534         var w = this.width;
37535         if(w){
37536             el.setWidth(w);
37537         }else if(Roo.isIE){
37538             el.setWidth(this.minWidth);
37539             var t = el.dom.offsetWidth; // force recalc
37540             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37541         }
37542     },
37543
37544     // private
37545     delayAutoWidth : function(){
37546         if(this.rendered){
37547             if(!this.awTask){
37548                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37549             }
37550             this.awTask.delay(20);
37551         }
37552     },
37553
37554     // private
37555     findTargetItem : function(e){
37556         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37557         if(t && t.menuItemId){
37558             return this.items.get(t.menuItemId);
37559         }
37560     },
37561
37562     // private
37563     onClick : function(e){
37564         Roo.log("menu.onClick");
37565         var t = this.findTargetItem(e);
37566         if(!t){
37567             return;
37568         }
37569         Roo.log(e);
37570         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37571             if(t == this.activeItem && t.shouldDeactivate(e)){
37572                 this.activeItem.deactivate();
37573                 delete this.activeItem;
37574                 return;
37575             }
37576             if(t.canActivate){
37577                 this.setActiveItem(t, true);
37578             }
37579             return;
37580             
37581             
37582         }
37583         
37584         t.onClick(e);
37585         this.fireEvent("click", this, t, e);
37586     },
37587
37588     // private
37589     setActiveItem : function(item, autoExpand){
37590         if(item != this.activeItem){
37591             if(this.activeItem){
37592                 this.activeItem.deactivate();
37593             }
37594             this.activeItem = item;
37595             item.activate(autoExpand);
37596         }else if(autoExpand){
37597             item.expandMenu();
37598         }
37599     },
37600
37601     // private
37602     tryActivate : function(start, step){
37603         var items = this.items;
37604         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37605             var item = items.get(i);
37606             if(!item.disabled && item.canActivate){
37607                 this.setActiveItem(item, false);
37608                 return item;
37609             }
37610         }
37611         return false;
37612     },
37613
37614     // private
37615     onMouseOver : function(e){
37616         var t;
37617         if(t = this.findTargetItem(e)){
37618             if(t.canActivate && !t.disabled){
37619                 this.setActiveItem(t, true);
37620             }
37621         }
37622         this.fireEvent("mouseover", this, e, t);
37623     },
37624
37625     // private
37626     onMouseOut : function(e){
37627         var t;
37628         if(t = this.findTargetItem(e)){
37629             if(t == this.activeItem && t.shouldDeactivate(e)){
37630                 this.activeItem.deactivate();
37631                 delete this.activeItem;
37632             }
37633         }
37634         this.fireEvent("mouseout", this, e, t);
37635     },
37636
37637     /**
37638      * Read-only.  Returns true if the menu is currently displayed, else false.
37639      * @type Boolean
37640      */
37641     isVisible : function(){
37642         return this.el && !this.hidden;
37643     },
37644
37645     /**
37646      * Displays this menu relative to another element
37647      * @param {String/HTMLElement/Roo.Element} element The element to align to
37648      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37649      * the element (defaults to this.defaultAlign)
37650      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37651      */
37652     show : function(el, pos, parentMenu){
37653         this.parentMenu = parentMenu;
37654         if(!this.el){
37655             this.render();
37656         }
37657         this.fireEvent("beforeshow", this);
37658         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37659     },
37660
37661     /**
37662      * Displays this menu at a specific xy position
37663      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37664      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37665      */
37666     showAt : function(xy, parentMenu, /* private: */_e){
37667         this.parentMenu = parentMenu;
37668         if(!this.el){
37669             this.render();
37670         }
37671         if(_e !== false){
37672             this.fireEvent("beforeshow", this);
37673             xy = this.el.adjustForConstraints(xy);
37674         }
37675         this.el.setXY(xy);
37676         this.el.show();
37677         this.hidden = false;
37678         this.focus();
37679         this.fireEvent("show", this);
37680     },
37681
37682     focus : function(){
37683         if(!this.hidden){
37684             this.doFocus.defer(50, this);
37685         }
37686     },
37687
37688     doFocus : function(){
37689         if(!this.hidden){
37690             this.focusEl.focus();
37691         }
37692     },
37693
37694     /**
37695      * Hides this menu and optionally all parent menus
37696      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37697      */
37698     hide : function(deep){
37699         if(this.el && this.isVisible()){
37700             this.fireEvent("beforehide", this);
37701             if(this.activeItem){
37702                 this.activeItem.deactivate();
37703                 this.activeItem = null;
37704             }
37705             this.el.hide();
37706             this.hidden = true;
37707             this.fireEvent("hide", this);
37708         }
37709         if(deep === true && this.parentMenu){
37710             this.parentMenu.hide(true);
37711         }
37712     },
37713
37714     /**
37715      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37716      * Any of the following are valid:
37717      * <ul>
37718      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37719      * <li>An HTMLElement object which will be converted to a menu item</li>
37720      * <li>A menu item config object that will be created as a new menu item</li>
37721      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37722      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37723      * </ul>
37724      * Usage:
37725      * <pre><code>
37726 // Create the menu
37727 var menu = new Roo.menu.Menu();
37728
37729 // Create a menu item to add by reference
37730 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37731
37732 // Add a bunch of items at once using different methods.
37733 // Only the last item added will be returned.
37734 var item = menu.add(
37735     menuItem,                // add existing item by ref
37736     'Dynamic Item',          // new TextItem
37737     '-',                     // new separator
37738     { text: 'Config Item' }  // new item by config
37739 );
37740 </code></pre>
37741      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37742      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37743      */
37744     add : function(){
37745         var a = arguments, l = a.length, item;
37746         for(var i = 0; i < l; i++){
37747             var el = a[i];
37748             if ((typeof(el) == "object") && el.xtype && el.xns) {
37749                 el = Roo.factory(el, Roo.menu);
37750             }
37751             
37752             if(el.render){ // some kind of Item
37753                 item = this.addItem(el);
37754             }else if(typeof el == "string"){ // string
37755                 if(el == "separator" || el == "-"){
37756                     item = this.addSeparator();
37757                 }else{
37758                     item = this.addText(el);
37759                 }
37760             }else if(el.tagName || el.el){ // element
37761                 item = this.addElement(el);
37762             }else if(typeof el == "object"){ // must be menu item config?
37763                 item = this.addMenuItem(el);
37764             }
37765         }
37766         return item;
37767     },
37768
37769     /**
37770      * Returns this menu's underlying {@link Roo.Element} object
37771      * @return {Roo.Element} The element
37772      */
37773     getEl : function(){
37774         if(!this.el){
37775             this.render();
37776         }
37777         return this.el;
37778     },
37779
37780     /**
37781      * Adds a separator bar to the menu
37782      * @return {Roo.menu.Item} The menu item that was added
37783      */
37784     addSeparator : function(){
37785         return this.addItem(new Roo.menu.Separator());
37786     },
37787
37788     /**
37789      * Adds an {@link Roo.Element} object to the menu
37790      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37791      * @return {Roo.menu.Item} The menu item that was added
37792      */
37793     addElement : function(el){
37794         return this.addItem(new Roo.menu.BaseItem(el));
37795     },
37796
37797     /**
37798      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37799      * @param {Roo.menu.Item} item The menu item to add
37800      * @return {Roo.menu.Item} The menu item that was added
37801      */
37802     addItem : function(item){
37803         this.items.add(item);
37804         if(this.ul){
37805             var li = document.createElement("li");
37806             li.className = "x-menu-list-item";
37807             this.ul.dom.appendChild(li);
37808             item.render(li, this);
37809             this.delayAutoWidth();
37810         }
37811         return item;
37812     },
37813
37814     /**
37815      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37816      * @param {Object} config A MenuItem config object
37817      * @return {Roo.menu.Item} The menu item that was added
37818      */
37819     addMenuItem : function(config){
37820         if(!(config instanceof Roo.menu.Item)){
37821             if(typeof config.checked == "boolean"){ // must be check menu item config?
37822                 config = new Roo.menu.CheckItem(config);
37823             }else{
37824                 config = new Roo.menu.Item(config);
37825             }
37826         }
37827         return this.addItem(config);
37828     },
37829
37830     /**
37831      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37832      * @param {String} text The text to display in the menu item
37833      * @return {Roo.menu.Item} The menu item that was added
37834      */
37835     addText : function(text){
37836         return this.addItem(new Roo.menu.TextItem({ text : text }));
37837     },
37838
37839     /**
37840      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37841      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37842      * @param {Roo.menu.Item} item The menu item to add
37843      * @return {Roo.menu.Item} The menu item that was added
37844      */
37845     insert : function(index, item){
37846         this.items.insert(index, item);
37847         if(this.ul){
37848             var li = document.createElement("li");
37849             li.className = "x-menu-list-item";
37850             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37851             item.render(li, this);
37852             this.delayAutoWidth();
37853         }
37854         return item;
37855     },
37856
37857     /**
37858      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37859      * @param {Roo.menu.Item} item The menu item to remove
37860      */
37861     remove : function(item){
37862         this.items.removeKey(item.id);
37863         item.destroy();
37864     },
37865
37866     /**
37867      * Removes and destroys all items in the menu
37868      */
37869     removeAll : function(){
37870         var f;
37871         while(f = this.items.first()){
37872             this.remove(f);
37873         }
37874     }
37875 });
37876
37877 // MenuNav is a private utility class used internally by the Menu
37878 Roo.menu.MenuNav = function(menu){
37879     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37880     this.scope = this.menu = menu;
37881 };
37882
37883 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37884     doRelay : function(e, h){
37885         var k = e.getKey();
37886         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37887             this.menu.tryActivate(0, 1);
37888             return false;
37889         }
37890         return h.call(this.scope || this, e, this.menu);
37891     },
37892
37893     up : function(e, m){
37894         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37895             m.tryActivate(m.items.length-1, -1);
37896         }
37897     },
37898
37899     down : function(e, m){
37900         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37901             m.tryActivate(0, 1);
37902         }
37903     },
37904
37905     right : function(e, m){
37906         if(m.activeItem){
37907             m.activeItem.expandMenu(true);
37908         }
37909     },
37910
37911     left : function(e, m){
37912         m.hide();
37913         if(m.parentMenu && m.parentMenu.activeItem){
37914             m.parentMenu.activeItem.activate();
37915         }
37916     },
37917
37918     enter : function(e, m){
37919         if(m.activeItem){
37920             e.stopPropagation();
37921             m.activeItem.onClick(e);
37922             m.fireEvent("click", this, m.activeItem);
37923             return true;
37924         }
37925     }
37926 });/*
37927  * Based on:
37928  * Ext JS Library 1.1.1
37929  * Copyright(c) 2006-2007, Ext JS, LLC.
37930  *
37931  * Originally Released Under LGPL - original licence link has changed is not relivant.
37932  *
37933  * Fork - LGPL
37934  * <script type="text/javascript">
37935  */
37936  
37937 /**
37938  * @class Roo.menu.MenuMgr
37939  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37940  * @singleton
37941  */
37942 Roo.menu.MenuMgr = function(){
37943    var menus, active, groups = {}, attached = false, lastShow = new Date();
37944
37945    // private - called when first menu is created
37946    function init(){
37947        menus = {};
37948        active = new Roo.util.MixedCollection();
37949        Roo.get(document).addKeyListener(27, function(){
37950            if(active.length > 0){
37951                hideAll();
37952            }
37953        });
37954    }
37955
37956    // private
37957    function hideAll(){
37958        if(active && active.length > 0){
37959            var c = active.clone();
37960            c.each(function(m){
37961                m.hide();
37962            });
37963        }
37964    }
37965
37966    // private
37967    function onHide(m){
37968        active.remove(m);
37969        if(active.length < 1){
37970            Roo.get(document).un("mousedown", onMouseDown);
37971            attached = false;
37972        }
37973    }
37974
37975    // private
37976    function onShow(m){
37977        var last = active.last();
37978        lastShow = new Date();
37979        active.add(m);
37980        if(!attached){
37981            Roo.get(document).on("mousedown", onMouseDown);
37982            attached = true;
37983        }
37984        if(m.parentMenu){
37985           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37986           m.parentMenu.activeChild = m;
37987        }else if(last && last.isVisible()){
37988           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37989        }
37990    }
37991
37992    // private
37993    function onBeforeHide(m){
37994        if(m.activeChild){
37995            m.activeChild.hide();
37996        }
37997        if(m.autoHideTimer){
37998            clearTimeout(m.autoHideTimer);
37999            delete m.autoHideTimer;
38000        }
38001    }
38002
38003    // private
38004    function onBeforeShow(m){
38005        var pm = m.parentMenu;
38006        if(!pm && !m.allowOtherMenus){
38007            hideAll();
38008        }else if(pm && pm.activeChild && active != m){
38009            pm.activeChild.hide();
38010        }
38011    }
38012
38013    // private
38014    function onMouseDown(e){
38015        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38016            hideAll();
38017        }
38018    }
38019
38020    // private
38021    function onBeforeCheck(mi, state){
38022        if(state){
38023            var g = groups[mi.group];
38024            for(var i = 0, l = g.length; i < l; i++){
38025                if(g[i] != mi){
38026                    g[i].setChecked(false);
38027                }
38028            }
38029        }
38030    }
38031
38032    return {
38033
38034        /**
38035         * Hides all menus that are currently visible
38036         */
38037        hideAll : function(){
38038             hideAll();  
38039        },
38040
38041        // private
38042        register : function(menu){
38043            if(!menus){
38044                init();
38045            }
38046            menus[menu.id] = menu;
38047            menu.on("beforehide", onBeforeHide);
38048            menu.on("hide", onHide);
38049            menu.on("beforeshow", onBeforeShow);
38050            menu.on("show", onShow);
38051            var g = menu.group;
38052            if(g && menu.events["checkchange"]){
38053                if(!groups[g]){
38054                    groups[g] = [];
38055                }
38056                groups[g].push(menu);
38057                menu.on("checkchange", onCheck);
38058            }
38059        },
38060
38061         /**
38062          * Returns a {@link Roo.menu.Menu} object
38063          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38064          * be used to generate and return a new Menu instance.
38065          */
38066        get : function(menu){
38067            if(typeof menu == "string"){ // menu id
38068                return menus[menu];
38069            }else if(menu.events){  // menu instance
38070                return menu;
38071            }else if(typeof menu.length == 'number'){ // array of menu items?
38072                return new Roo.menu.Menu({items:menu});
38073            }else{ // otherwise, must be a config
38074                return new Roo.menu.Menu(menu);
38075            }
38076        },
38077
38078        // private
38079        unregister : function(menu){
38080            delete menus[menu.id];
38081            menu.un("beforehide", onBeforeHide);
38082            menu.un("hide", onHide);
38083            menu.un("beforeshow", onBeforeShow);
38084            menu.un("show", onShow);
38085            var g = menu.group;
38086            if(g && menu.events["checkchange"]){
38087                groups[g].remove(menu);
38088                menu.un("checkchange", onCheck);
38089            }
38090        },
38091
38092        // private
38093        registerCheckable : function(menuItem){
38094            var g = menuItem.group;
38095            if(g){
38096                if(!groups[g]){
38097                    groups[g] = [];
38098                }
38099                groups[g].push(menuItem);
38100                menuItem.on("beforecheckchange", onBeforeCheck);
38101            }
38102        },
38103
38104        // private
38105        unregisterCheckable : function(menuItem){
38106            var g = menuItem.group;
38107            if(g){
38108                groups[g].remove(menuItem);
38109                menuItem.un("beforecheckchange", onBeforeCheck);
38110            }
38111        }
38112    };
38113 }();/*
38114  * Based on:
38115  * Ext JS Library 1.1.1
38116  * Copyright(c) 2006-2007, Ext JS, LLC.
38117  *
38118  * Originally Released Under LGPL - original licence link has changed is not relivant.
38119  *
38120  * Fork - LGPL
38121  * <script type="text/javascript">
38122  */
38123  
38124
38125 /**
38126  * @class Roo.menu.BaseItem
38127  * @extends Roo.Component
38128  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38129  * management and base configuration options shared by all menu components.
38130  * @constructor
38131  * Creates a new BaseItem
38132  * @param {Object} config Configuration options
38133  */
38134 Roo.menu.BaseItem = function(config){
38135     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38136
38137     this.addEvents({
38138         /**
38139          * @event click
38140          * Fires when this item is clicked
38141          * @param {Roo.menu.BaseItem} this
38142          * @param {Roo.EventObject} e
38143          */
38144         click: true,
38145         /**
38146          * @event activate
38147          * Fires when this item is activated
38148          * @param {Roo.menu.BaseItem} this
38149          */
38150         activate : true,
38151         /**
38152          * @event deactivate
38153          * Fires when this item is deactivated
38154          * @param {Roo.menu.BaseItem} this
38155          */
38156         deactivate : true
38157     });
38158
38159     if(this.handler){
38160         this.on("click", this.handler, this.scope, true);
38161     }
38162 };
38163
38164 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38165     /**
38166      * @cfg {Function} handler
38167      * A function that will handle the click event of this menu item (defaults to undefined)
38168      */
38169     /**
38170      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38171      */
38172     canActivate : false,
38173     
38174      /**
38175      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38176      */
38177     hidden: false,
38178     
38179     /**
38180      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38181      */
38182     activeClass : "x-menu-item-active",
38183     /**
38184      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38185      */
38186     hideOnClick : true,
38187     /**
38188      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38189      */
38190     hideDelay : 100,
38191
38192     // private
38193     ctype: "Roo.menu.BaseItem",
38194
38195     // private
38196     actionMode : "container",
38197
38198     // private
38199     render : function(container, parentMenu){
38200         this.parentMenu = parentMenu;
38201         Roo.menu.BaseItem.superclass.render.call(this, container);
38202         this.container.menuItemId = this.id;
38203     },
38204
38205     // private
38206     onRender : function(container, position){
38207         this.el = Roo.get(this.el);
38208         container.dom.appendChild(this.el.dom);
38209     },
38210
38211     // private
38212     onClick : function(e){
38213         if(!this.disabled && this.fireEvent("click", this, e) !== false
38214                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38215             this.handleClick(e);
38216         }else{
38217             e.stopEvent();
38218         }
38219     },
38220
38221     // private
38222     activate : function(){
38223         if(this.disabled){
38224             return false;
38225         }
38226         var li = this.container;
38227         li.addClass(this.activeClass);
38228         this.region = li.getRegion().adjust(2, 2, -2, -2);
38229         this.fireEvent("activate", this);
38230         return true;
38231     },
38232
38233     // private
38234     deactivate : function(){
38235         this.container.removeClass(this.activeClass);
38236         this.fireEvent("deactivate", this);
38237     },
38238
38239     // private
38240     shouldDeactivate : function(e){
38241         return !this.region || !this.region.contains(e.getPoint());
38242     },
38243
38244     // private
38245     handleClick : function(e){
38246         if(this.hideOnClick){
38247             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38248         }
38249     },
38250
38251     // private
38252     expandMenu : function(autoActivate){
38253         // do nothing
38254     },
38255
38256     // private
38257     hideMenu : function(){
38258         // do nothing
38259     }
38260 });/*
38261  * Based on:
38262  * Ext JS Library 1.1.1
38263  * Copyright(c) 2006-2007, Ext JS, LLC.
38264  *
38265  * Originally Released Under LGPL - original licence link has changed is not relivant.
38266  *
38267  * Fork - LGPL
38268  * <script type="text/javascript">
38269  */
38270  
38271 /**
38272  * @class Roo.menu.Adapter
38273  * @extends Roo.menu.BaseItem
38274  * 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.
38275  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38276  * @constructor
38277  * Creates a new Adapter
38278  * @param {Object} config Configuration options
38279  */
38280 Roo.menu.Adapter = function(component, config){
38281     Roo.menu.Adapter.superclass.constructor.call(this, config);
38282     this.component = component;
38283 };
38284 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38285     // private
38286     canActivate : true,
38287
38288     // private
38289     onRender : function(container, position){
38290         this.component.render(container);
38291         this.el = this.component.getEl();
38292     },
38293
38294     // private
38295     activate : function(){
38296         if(this.disabled){
38297             return false;
38298         }
38299         this.component.focus();
38300         this.fireEvent("activate", this);
38301         return true;
38302     },
38303
38304     // private
38305     deactivate : function(){
38306         this.fireEvent("deactivate", this);
38307     },
38308
38309     // private
38310     disable : function(){
38311         this.component.disable();
38312         Roo.menu.Adapter.superclass.disable.call(this);
38313     },
38314
38315     // private
38316     enable : function(){
38317         this.component.enable();
38318         Roo.menu.Adapter.superclass.enable.call(this);
38319     }
38320 });/*
38321  * Based on:
38322  * Ext JS Library 1.1.1
38323  * Copyright(c) 2006-2007, Ext JS, LLC.
38324  *
38325  * Originally Released Under LGPL - original licence link has changed is not relivant.
38326  *
38327  * Fork - LGPL
38328  * <script type="text/javascript">
38329  */
38330
38331 /**
38332  * @class Roo.menu.TextItem
38333  * @extends Roo.menu.BaseItem
38334  * Adds a static text string to a menu, usually used as either a heading or group separator.
38335  * Note: old style constructor with text is still supported.
38336  * 
38337  * @constructor
38338  * Creates a new TextItem
38339  * @param {Object} cfg Configuration
38340  */
38341 Roo.menu.TextItem = function(cfg){
38342     if (typeof(cfg) == 'string') {
38343         this.text = cfg;
38344     } else {
38345         Roo.apply(this,cfg);
38346     }
38347     
38348     Roo.menu.TextItem.superclass.constructor.call(this);
38349 };
38350
38351 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38352     /**
38353      * @cfg {Boolean} text Text to show on item.
38354      */
38355     text : '',
38356     
38357     /**
38358      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38359      */
38360     hideOnClick : false,
38361     /**
38362      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38363      */
38364     itemCls : "x-menu-text",
38365
38366     // private
38367     onRender : function(){
38368         var s = document.createElement("span");
38369         s.className = this.itemCls;
38370         s.innerHTML = this.text;
38371         this.el = s;
38372         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38373     }
38374 });/*
38375  * Based on:
38376  * Ext JS Library 1.1.1
38377  * Copyright(c) 2006-2007, Ext JS, LLC.
38378  *
38379  * Originally Released Under LGPL - original licence link has changed is not relivant.
38380  *
38381  * Fork - LGPL
38382  * <script type="text/javascript">
38383  */
38384
38385 /**
38386  * @class Roo.menu.Separator
38387  * @extends Roo.menu.BaseItem
38388  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38389  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38390  * @constructor
38391  * @param {Object} config Configuration options
38392  */
38393 Roo.menu.Separator = function(config){
38394     Roo.menu.Separator.superclass.constructor.call(this, config);
38395 };
38396
38397 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38398     /**
38399      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38400      */
38401     itemCls : "x-menu-sep",
38402     /**
38403      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38404      */
38405     hideOnClick : false,
38406
38407     // private
38408     onRender : function(li){
38409         var s = document.createElement("span");
38410         s.className = this.itemCls;
38411         s.innerHTML = "&#160;";
38412         this.el = s;
38413         li.addClass("x-menu-sep-li");
38414         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38415     }
38416 });/*
38417  * Based on:
38418  * Ext JS Library 1.1.1
38419  * Copyright(c) 2006-2007, Ext JS, LLC.
38420  *
38421  * Originally Released Under LGPL - original licence link has changed is not relivant.
38422  *
38423  * Fork - LGPL
38424  * <script type="text/javascript">
38425  */
38426 /**
38427  * @class Roo.menu.Item
38428  * @extends Roo.menu.BaseItem
38429  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38430  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38431  * activation and click handling.
38432  * @constructor
38433  * Creates a new Item
38434  * @param {Object} config Configuration options
38435  */
38436 Roo.menu.Item = function(config){
38437     Roo.menu.Item.superclass.constructor.call(this, config);
38438     if(this.menu){
38439         this.menu = Roo.menu.MenuMgr.get(this.menu);
38440     }
38441 };
38442 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38443     
38444     /**
38445      * @cfg {String} text
38446      * The text to show on the menu item.
38447      */
38448     text: '',
38449      /**
38450      * @cfg {String} HTML to render in menu
38451      * The text to show on the menu item (HTML version).
38452      */
38453     html: '',
38454     /**
38455      * @cfg {String} icon
38456      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38457      */
38458     icon: undefined,
38459     /**
38460      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38461      */
38462     itemCls : "x-menu-item",
38463     /**
38464      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38465      */
38466     canActivate : true,
38467     /**
38468      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38469      */
38470     showDelay: 200,
38471     // doc'd in BaseItem
38472     hideDelay: 200,
38473
38474     // private
38475     ctype: "Roo.menu.Item",
38476     
38477     // private
38478     onRender : function(container, position){
38479         var el = document.createElement("a");
38480         el.hideFocus = true;
38481         el.unselectable = "on";
38482         el.href = this.href || "#";
38483         if(this.hrefTarget){
38484             el.target = this.hrefTarget;
38485         }
38486         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38487         
38488         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38489         
38490         el.innerHTML = String.format(
38491                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38492                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38493         this.el = el;
38494         Roo.menu.Item.superclass.onRender.call(this, container, position);
38495     },
38496
38497     /**
38498      * Sets the text to display in this menu item
38499      * @param {String} text The text to display
38500      * @param {Boolean} isHTML true to indicate text is pure html.
38501      */
38502     setText : function(text, isHTML){
38503         if (isHTML) {
38504             this.html = text;
38505         } else {
38506             this.text = text;
38507             this.html = '';
38508         }
38509         if(this.rendered){
38510             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38511      
38512             this.el.update(String.format(
38513                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38514                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38515             this.parentMenu.autoWidth();
38516         }
38517     },
38518
38519     // private
38520     handleClick : function(e){
38521         if(!this.href){ // if no link defined, stop the event automatically
38522             e.stopEvent();
38523         }
38524         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38525     },
38526
38527     // private
38528     activate : function(autoExpand){
38529         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38530             this.focus();
38531             if(autoExpand){
38532                 this.expandMenu();
38533             }
38534         }
38535         return true;
38536     },
38537
38538     // private
38539     shouldDeactivate : function(e){
38540         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38541             if(this.menu && this.menu.isVisible()){
38542                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38543             }
38544             return true;
38545         }
38546         return false;
38547     },
38548
38549     // private
38550     deactivate : function(){
38551         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38552         this.hideMenu();
38553     },
38554
38555     // private
38556     expandMenu : function(autoActivate){
38557         if(!this.disabled && this.menu){
38558             clearTimeout(this.hideTimer);
38559             delete this.hideTimer;
38560             if(!this.menu.isVisible() && !this.showTimer){
38561                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38562             }else if (this.menu.isVisible() && autoActivate){
38563                 this.menu.tryActivate(0, 1);
38564             }
38565         }
38566     },
38567
38568     // private
38569     deferExpand : function(autoActivate){
38570         delete this.showTimer;
38571         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38572         if(autoActivate){
38573             this.menu.tryActivate(0, 1);
38574         }
38575     },
38576
38577     // private
38578     hideMenu : function(){
38579         clearTimeout(this.showTimer);
38580         delete this.showTimer;
38581         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38582             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38583         }
38584     },
38585
38586     // private
38587     deferHide : function(){
38588         delete this.hideTimer;
38589         this.menu.hide();
38590     }
38591 });/*
38592  * Based on:
38593  * Ext JS Library 1.1.1
38594  * Copyright(c) 2006-2007, Ext JS, LLC.
38595  *
38596  * Originally Released Under LGPL - original licence link has changed is not relivant.
38597  *
38598  * Fork - LGPL
38599  * <script type="text/javascript">
38600  */
38601  
38602 /**
38603  * @class Roo.menu.CheckItem
38604  * @extends Roo.menu.Item
38605  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38606  * @constructor
38607  * Creates a new CheckItem
38608  * @param {Object} config Configuration options
38609  */
38610 Roo.menu.CheckItem = function(config){
38611     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38612     this.addEvents({
38613         /**
38614          * @event beforecheckchange
38615          * Fires before the checked value is set, providing an opportunity to cancel if needed
38616          * @param {Roo.menu.CheckItem} this
38617          * @param {Boolean} checked The new checked value that will be set
38618          */
38619         "beforecheckchange" : true,
38620         /**
38621          * @event checkchange
38622          * Fires after the checked value has been set
38623          * @param {Roo.menu.CheckItem} this
38624          * @param {Boolean} checked The checked value that was set
38625          */
38626         "checkchange" : true
38627     });
38628     if(this.checkHandler){
38629         this.on('checkchange', this.checkHandler, this.scope);
38630     }
38631 };
38632 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38633     /**
38634      * @cfg {String} group
38635      * All check items with the same group name will automatically be grouped into a single-select
38636      * radio button group (defaults to '')
38637      */
38638     /**
38639      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38640      */
38641     itemCls : "x-menu-item x-menu-check-item",
38642     /**
38643      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38644      */
38645     groupClass : "x-menu-group-item",
38646
38647     /**
38648      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38649      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38650      * initialized with checked = true will be rendered as checked.
38651      */
38652     checked: false,
38653
38654     // private
38655     ctype: "Roo.menu.CheckItem",
38656
38657     // private
38658     onRender : function(c){
38659         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38660         if(this.group){
38661             this.el.addClass(this.groupClass);
38662         }
38663         Roo.menu.MenuMgr.registerCheckable(this);
38664         if(this.checked){
38665             this.checked = false;
38666             this.setChecked(true, true);
38667         }
38668     },
38669
38670     // private
38671     destroy : function(){
38672         if(this.rendered){
38673             Roo.menu.MenuMgr.unregisterCheckable(this);
38674         }
38675         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38676     },
38677
38678     /**
38679      * Set the checked state of this item
38680      * @param {Boolean} checked The new checked value
38681      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38682      */
38683     setChecked : function(state, suppressEvent){
38684         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38685             if(this.container){
38686                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38687             }
38688             this.checked = state;
38689             if(suppressEvent !== true){
38690                 this.fireEvent("checkchange", this, state);
38691             }
38692         }
38693     },
38694
38695     // private
38696     handleClick : function(e){
38697        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38698            this.setChecked(!this.checked);
38699        }
38700        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38701     }
38702 });/*
38703  * Based on:
38704  * Ext JS Library 1.1.1
38705  * Copyright(c) 2006-2007, Ext JS, LLC.
38706  *
38707  * Originally Released Under LGPL - original licence link has changed is not relivant.
38708  *
38709  * Fork - LGPL
38710  * <script type="text/javascript">
38711  */
38712  
38713 /**
38714  * @class Roo.menu.DateItem
38715  * @extends Roo.menu.Adapter
38716  * A menu item that wraps the {@link Roo.DatPicker} component.
38717  * @constructor
38718  * Creates a new DateItem
38719  * @param {Object} config Configuration options
38720  */
38721 Roo.menu.DateItem = function(config){
38722     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38723     /** The Roo.DatePicker object @type Roo.DatePicker */
38724     this.picker = this.component;
38725     this.addEvents({select: true});
38726     
38727     this.picker.on("render", function(picker){
38728         picker.getEl().swallowEvent("click");
38729         picker.container.addClass("x-menu-date-item");
38730     });
38731
38732     this.picker.on("select", this.onSelect, this);
38733 };
38734
38735 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38736     // private
38737     onSelect : function(picker, date){
38738         this.fireEvent("select", this, date, picker);
38739         Roo.menu.DateItem.superclass.handleClick.call(this);
38740     }
38741 });/*
38742  * Based on:
38743  * Ext JS Library 1.1.1
38744  * Copyright(c) 2006-2007, Ext JS, LLC.
38745  *
38746  * Originally Released Under LGPL - original licence link has changed is not relivant.
38747  *
38748  * Fork - LGPL
38749  * <script type="text/javascript">
38750  */
38751  
38752 /**
38753  * @class Roo.menu.ColorItem
38754  * @extends Roo.menu.Adapter
38755  * A menu item that wraps the {@link Roo.ColorPalette} component.
38756  * @constructor
38757  * Creates a new ColorItem
38758  * @param {Object} config Configuration options
38759  */
38760 Roo.menu.ColorItem = function(config){
38761     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38762     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38763     this.palette = this.component;
38764     this.relayEvents(this.palette, ["select"]);
38765     if(this.selectHandler){
38766         this.on('select', this.selectHandler, this.scope);
38767     }
38768 };
38769 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38770  * Based on:
38771  * Ext JS Library 1.1.1
38772  * Copyright(c) 2006-2007, Ext JS, LLC.
38773  *
38774  * Originally Released Under LGPL - original licence link has changed is not relivant.
38775  *
38776  * Fork - LGPL
38777  * <script type="text/javascript">
38778  */
38779  
38780
38781 /**
38782  * @class Roo.menu.DateMenu
38783  * @extends Roo.menu.Menu
38784  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38785  * @constructor
38786  * Creates a new DateMenu
38787  * @param {Object} config Configuration options
38788  */
38789 Roo.menu.DateMenu = function(config){
38790     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38791     this.plain = true;
38792     var di = new Roo.menu.DateItem(config);
38793     this.add(di);
38794     /**
38795      * The {@link Roo.DatePicker} instance for this DateMenu
38796      * @type DatePicker
38797      */
38798     this.picker = di.picker;
38799     /**
38800      * @event select
38801      * @param {DatePicker} picker
38802      * @param {Date} date
38803      */
38804     this.relayEvents(di, ["select"]);
38805     this.on('beforeshow', function(){
38806         if(this.picker){
38807             this.picker.hideMonthPicker(false);
38808         }
38809     }, this);
38810 };
38811 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38812     cls:'x-date-menu'
38813 });/*
38814  * Based on:
38815  * Ext JS Library 1.1.1
38816  * Copyright(c) 2006-2007, Ext JS, LLC.
38817  *
38818  * Originally Released Under LGPL - original licence link has changed is not relivant.
38819  *
38820  * Fork - LGPL
38821  * <script type="text/javascript">
38822  */
38823  
38824
38825 /**
38826  * @class Roo.menu.ColorMenu
38827  * @extends Roo.menu.Menu
38828  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38829  * @constructor
38830  * Creates a new ColorMenu
38831  * @param {Object} config Configuration options
38832  */
38833 Roo.menu.ColorMenu = function(config){
38834     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38835     this.plain = true;
38836     var ci = new Roo.menu.ColorItem(config);
38837     this.add(ci);
38838     /**
38839      * The {@link Roo.ColorPalette} instance for this ColorMenu
38840      * @type ColorPalette
38841      */
38842     this.palette = ci.palette;
38843     /**
38844      * @event select
38845      * @param {ColorPalette} palette
38846      * @param {String} color
38847      */
38848     this.relayEvents(ci, ["select"]);
38849 };
38850 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38851  * Based on:
38852  * Ext JS Library 1.1.1
38853  * Copyright(c) 2006-2007, Ext JS, LLC.
38854  *
38855  * Originally Released Under LGPL - original licence link has changed is not relivant.
38856  *
38857  * Fork - LGPL
38858  * <script type="text/javascript">
38859  */
38860  
38861 /**
38862  * @class Roo.form.TextItem
38863  * @extends Roo.BoxComponent
38864  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38865  * @constructor
38866  * Creates a new TextItem
38867  * @param {Object} config Configuration options
38868  */
38869 Roo.form.TextItem = function(config){
38870     Roo.form.TextItem.superclass.constructor.call(this, config);
38871 };
38872
38873 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38874     
38875     /**
38876      * @cfg {String} tag the tag for this item (default div)
38877      */
38878     tag : 'div',
38879     /**
38880      * @cfg {String} html the content for this item
38881      */
38882     html : '',
38883     
38884     getAutoCreate : function()
38885     {
38886         var cfg = {
38887             id: this.id,
38888             tag: this.tag,
38889             html: this.html,
38890             cls: 'x-form-item'
38891         };
38892         
38893         return cfg;
38894         
38895     },
38896     
38897     onRender : function(ct, position)
38898     {
38899         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38900         
38901         if(!this.el){
38902             var cfg = this.getAutoCreate();
38903             if(!cfg.name){
38904                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38905             }
38906             if (!cfg.name.length) {
38907                 delete cfg.name;
38908             }
38909             this.el = ct.createChild(cfg, position);
38910         }
38911     },
38912     /*
38913      * setHTML
38914      * @param {String} html update the Contents of the element.
38915      */
38916     setHTML : function(html)
38917     {
38918         this.fieldEl.dom.innerHTML = html;
38919     }
38920     
38921 });/*
38922  * Based on:
38923  * Ext JS Library 1.1.1
38924  * Copyright(c) 2006-2007, Ext JS, LLC.
38925  *
38926  * Originally Released Under LGPL - original licence link has changed is not relivant.
38927  *
38928  * Fork - LGPL
38929  * <script type="text/javascript">
38930  */
38931  
38932 /**
38933  * @class Roo.form.Field
38934  * @extends Roo.BoxComponent
38935  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38936  * @constructor
38937  * Creates a new Field
38938  * @param {Object} config Configuration options
38939  */
38940 Roo.form.Field = function(config){
38941     Roo.form.Field.superclass.constructor.call(this, config);
38942 };
38943
38944 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38945     /**
38946      * @cfg {String} fieldLabel Label to use when rendering a form.
38947      */
38948        /**
38949      * @cfg {String} qtip Mouse over tip
38950      */
38951      
38952     /**
38953      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38954      */
38955     invalidClass : "x-form-invalid",
38956     /**
38957      * @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")
38958      */
38959     invalidText : "The value in this field is invalid",
38960     /**
38961      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38962      */
38963     focusClass : "x-form-focus",
38964     /**
38965      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38966       automatic validation (defaults to "keyup").
38967      */
38968     validationEvent : "keyup",
38969     /**
38970      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38971      */
38972     validateOnBlur : true,
38973     /**
38974      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38975      */
38976     validationDelay : 250,
38977     /**
38978      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38979      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38980      */
38981     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38982     /**
38983      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38984      */
38985     fieldClass : "x-form-field",
38986     /**
38987      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38988      *<pre>
38989 Value         Description
38990 -----------   ----------------------------------------------------------------------
38991 qtip          Display a quick tip when the user hovers over the field
38992 title         Display a default browser title attribute popup
38993 under         Add a block div beneath the field containing the error text
38994 side          Add an error icon to the right of the field with a popup on hover
38995 [element id]  Add the error text directly to the innerHTML of the specified element
38996 </pre>
38997      */
38998     msgTarget : 'qtip',
38999     /**
39000      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39001      */
39002     msgFx : 'normal',
39003
39004     /**
39005      * @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.
39006      */
39007     readOnly : false,
39008
39009     /**
39010      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39011      */
39012     disabled : false,
39013
39014     /**
39015      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39016      */
39017     inputType : undefined,
39018     
39019     /**
39020      * @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).
39021          */
39022         tabIndex : undefined,
39023         
39024     // private
39025     isFormField : true,
39026
39027     // private
39028     hasFocus : false,
39029     /**
39030      * @property {Roo.Element} fieldEl
39031      * Element Containing the rendered Field (with label etc.)
39032      */
39033     /**
39034      * @cfg {Mixed} value A value to initialize this field with.
39035      */
39036     value : undefined,
39037
39038     /**
39039      * @cfg {String} name The field's HTML name attribute.
39040      */
39041     /**
39042      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39043      */
39044     // private
39045     loadedValue : false,
39046      
39047      
39048         // private ??
39049         initComponent : function(){
39050         Roo.form.Field.superclass.initComponent.call(this);
39051         this.addEvents({
39052             /**
39053              * @event focus
39054              * Fires when this field receives input focus.
39055              * @param {Roo.form.Field} this
39056              */
39057             focus : true,
39058             /**
39059              * @event blur
39060              * Fires when this field loses input focus.
39061              * @param {Roo.form.Field} this
39062              */
39063             blur : true,
39064             /**
39065              * @event specialkey
39066              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39067              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39068              * @param {Roo.form.Field} this
39069              * @param {Roo.EventObject} e The event object
39070              */
39071             specialkey : true,
39072             /**
39073              * @event change
39074              * Fires just before the field blurs if the field value has changed.
39075              * @param {Roo.form.Field} this
39076              * @param {Mixed} newValue The new value
39077              * @param {Mixed} oldValue The original value
39078              */
39079             change : true,
39080             /**
39081              * @event invalid
39082              * Fires after the field has been marked as invalid.
39083              * @param {Roo.form.Field} this
39084              * @param {String} msg The validation message
39085              */
39086             invalid : true,
39087             /**
39088              * @event valid
39089              * Fires after the field has been validated with no errors.
39090              * @param {Roo.form.Field} this
39091              */
39092             valid : true,
39093              /**
39094              * @event keyup
39095              * Fires after the key up
39096              * @param {Roo.form.Field} this
39097              * @param {Roo.EventObject}  e The event Object
39098              */
39099             keyup : true
39100         });
39101     },
39102
39103     /**
39104      * Returns the name attribute of the field if available
39105      * @return {String} name The field name
39106      */
39107     getName: function(){
39108          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39109     },
39110
39111     // private
39112     onRender : function(ct, position){
39113         Roo.form.Field.superclass.onRender.call(this, ct, position);
39114         if(!this.el){
39115             var cfg = this.getAutoCreate();
39116             if(!cfg.name){
39117                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39118             }
39119             if (!cfg.name.length) {
39120                 delete cfg.name;
39121             }
39122             if(this.inputType){
39123                 cfg.type = this.inputType;
39124             }
39125             this.el = ct.createChild(cfg, position);
39126         }
39127         var type = this.el.dom.type;
39128         if(type){
39129             if(type == 'password'){
39130                 type = 'text';
39131             }
39132             this.el.addClass('x-form-'+type);
39133         }
39134         if(this.readOnly){
39135             this.el.dom.readOnly = true;
39136         }
39137         if(this.tabIndex !== undefined){
39138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39139         }
39140
39141         this.el.addClass([this.fieldClass, this.cls]);
39142         this.initValue();
39143     },
39144
39145     /**
39146      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39147      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39148      * @return {Roo.form.Field} this
39149      */
39150     applyTo : function(target){
39151         this.allowDomMove = false;
39152         this.el = Roo.get(target);
39153         this.render(this.el.dom.parentNode);
39154         return this;
39155     },
39156
39157     // private
39158     initValue : function(){
39159         if(this.value !== undefined){
39160             this.setValue(this.value);
39161         }else if(this.el.dom.value.length > 0){
39162             this.setValue(this.el.dom.value);
39163         }
39164     },
39165
39166     /**
39167      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39168      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39169      */
39170     isDirty : function() {
39171         if(this.disabled) {
39172             return false;
39173         }
39174         return String(this.getValue()) !== String(this.originalValue);
39175     },
39176
39177     /**
39178      * stores the current value in loadedValue
39179      */
39180     resetHasChanged : function()
39181     {
39182         this.loadedValue = String(this.getValue());
39183     },
39184     /**
39185      * checks the current value against the 'loaded' value.
39186      * Note - will return false if 'resetHasChanged' has not been called first.
39187      */
39188     hasChanged : function()
39189     {
39190         if(this.disabled || this.readOnly) {
39191             return false;
39192         }
39193         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39194     },
39195     
39196     
39197     
39198     // private
39199     afterRender : function(){
39200         Roo.form.Field.superclass.afterRender.call(this);
39201         this.initEvents();
39202     },
39203
39204     // private
39205     fireKey : function(e){
39206         //Roo.log('field ' + e.getKey());
39207         if(e.isNavKeyPress()){
39208             this.fireEvent("specialkey", this, e);
39209         }
39210     },
39211
39212     /**
39213      * Resets the current field value to the originally loaded value and clears any validation messages
39214      */
39215     reset : function(){
39216         this.setValue(this.resetValue);
39217         this.originalValue = this.getValue();
39218         this.clearInvalid();
39219     },
39220
39221     // private
39222     initEvents : function(){
39223         // safari killled keypress - so keydown is now used..
39224         this.el.on("keydown" , this.fireKey,  this);
39225         this.el.on("focus", this.onFocus,  this);
39226         this.el.on("blur", this.onBlur,  this);
39227         this.el.relayEvent('keyup', this);
39228
39229         // reference to original value for reset
39230         this.originalValue = this.getValue();
39231         this.resetValue =  this.getValue();
39232     },
39233
39234     // private
39235     onFocus : function(){
39236         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39237             this.el.addClass(this.focusClass);
39238         }
39239         if(!this.hasFocus){
39240             this.hasFocus = true;
39241             this.startValue = this.getValue();
39242             this.fireEvent("focus", this);
39243         }
39244     },
39245
39246     beforeBlur : Roo.emptyFn,
39247
39248     // private
39249     onBlur : function(){
39250         this.beforeBlur();
39251         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39252             this.el.removeClass(this.focusClass);
39253         }
39254         this.hasFocus = false;
39255         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39256             this.validate();
39257         }
39258         var v = this.getValue();
39259         if(String(v) !== String(this.startValue)){
39260             this.fireEvent('change', this, v, this.startValue);
39261         }
39262         this.fireEvent("blur", this);
39263     },
39264
39265     /**
39266      * Returns whether or not the field value is currently valid
39267      * @param {Boolean} preventMark True to disable marking the field invalid
39268      * @return {Boolean} True if the value is valid, else false
39269      */
39270     isValid : function(preventMark){
39271         if(this.disabled){
39272             return true;
39273         }
39274         var restore = this.preventMark;
39275         this.preventMark = preventMark === true;
39276         var v = this.validateValue(this.processValue(this.getRawValue()));
39277         this.preventMark = restore;
39278         return v;
39279     },
39280
39281     /**
39282      * Validates the field value
39283      * @return {Boolean} True if the value is valid, else false
39284      */
39285     validate : function(){
39286         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39287             this.clearInvalid();
39288             return true;
39289         }
39290         return false;
39291     },
39292
39293     processValue : function(value){
39294         return value;
39295     },
39296
39297     // private
39298     // Subclasses should provide the validation implementation by overriding this
39299     validateValue : function(value){
39300         return true;
39301     },
39302
39303     /**
39304      * Mark this field as invalid
39305      * @param {String} msg The validation message
39306      */
39307     markInvalid : function(msg){
39308         if(!this.rendered || this.preventMark){ // not rendered
39309             return;
39310         }
39311         
39312         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39313         
39314         obj.el.addClass(this.invalidClass);
39315         msg = msg || this.invalidText;
39316         switch(this.msgTarget){
39317             case 'qtip':
39318                 obj.el.dom.qtip = msg;
39319                 obj.el.dom.qclass = 'x-form-invalid-tip';
39320                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39321                     Roo.QuickTips.enable();
39322                 }
39323                 break;
39324             case 'title':
39325                 this.el.dom.title = msg;
39326                 break;
39327             case 'under':
39328                 if(!this.errorEl){
39329                     var elp = this.el.findParent('.x-form-element', 5, true);
39330                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39331                     this.errorEl.setWidth(elp.getWidth(true)-20);
39332                 }
39333                 this.errorEl.update(msg);
39334                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39335                 break;
39336             case 'side':
39337                 if(!this.errorIcon){
39338                     var elp = this.el.findParent('.x-form-element', 5, true);
39339                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39340                 }
39341                 this.alignErrorIcon();
39342                 this.errorIcon.dom.qtip = msg;
39343                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39344                 this.errorIcon.show();
39345                 this.on('resize', this.alignErrorIcon, this);
39346                 break;
39347             default:
39348                 var t = Roo.getDom(this.msgTarget);
39349                 t.innerHTML = msg;
39350                 t.style.display = this.msgDisplay;
39351                 break;
39352         }
39353         this.fireEvent('invalid', this, msg);
39354     },
39355
39356     // private
39357     alignErrorIcon : function(){
39358         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39359     },
39360
39361     /**
39362      * Clear any invalid styles/messages for this field
39363      */
39364     clearInvalid : function(){
39365         if(!this.rendered || this.preventMark){ // not rendered
39366             return;
39367         }
39368         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39369         
39370         obj.el.removeClass(this.invalidClass);
39371         switch(this.msgTarget){
39372             case 'qtip':
39373                 obj.el.dom.qtip = '';
39374                 break;
39375             case 'title':
39376                 this.el.dom.title = '';
39377                 break;
39378             case 'under':
39379                 if(this.errorEl){
39380                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39381                 }
39382                 break;
39383             case 'side':
39384                 if(this.errorIcon){
39385                     this.errorIcon.dom.qtip = '';
39386                     this.errorIcon.hide();
39387                     this.un('resize', this.alignErrorIcon, this);
39388                 }
39389                 break;
39390             default:
39391                 var t = Roo.getDom(this.msgTarget);
39392                 t.innerHTML = '';
39393                 t.style.display = 'none';
39394                 break;
39395         }
39396         this.fireEvent('valid', this);
39397     },
39398
39399     /**
39400      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39401      * @return {Mixed} value The field value
39402      */
39403     getRawValue : function(){
39404         var v = this.el.getValue();
39405         
39406         return v;
39407     },
39408
39409     /**
39410      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39411      * @return {Mixed} value The field value
39412      */
39413     getValue : function(){
39414         var v = this.el.getValue();
39415          
39416         return v;
39417     },
39418
39419     /**
39420      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39421      * @param {Mixed} value The value to set
39422      */
39423     setRawValue : function(v){
39424         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39425     },
39426
39427     /**
39428      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39429      * @param {Mixed} value The value to set
39430      */
39431     setValue : function(v){
39432         this.value = v;
39433         if(this.rendered){
39434             this.el.dom.value = (v === null || v === undefined ? '' : v);
39435              this.validate();
39436         }
39437     },
39438
39439     adjustSize : function(w, h){
39440         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39441         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39442         return s;
39443     },
39444
39445     adjustWidth : function(tag, w){
39446         tag = tag.toLowerCase();
39447         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39448             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39449                 if(tag == 'input'){
39450                     return w + 2;
39451                 }
39452                 if(tag == 'textarea'){
39453                     return w-2;
39454                 }
39455             }else if(Roo.isOpera){
39456                 if(tag == 'input'){
39457                     return w + 2;
39458                 }
39459                 if(tag == 'textarea'){
39460                     return w-2;
39461                 }
39462             }
39463         }
39464         return w;
39465     }
39466 });
39467
39468
39469 // anything other than normal should be considered experimental
39470 Roo.form.Field.msgFx = {
39471     normal : {
39472         show: function(msgEl, f){
39473             msgEl.setDisplayed('block');
39474         },
39475
39476         hide : function(msgEl, f){
39477             msgEl.setDisplayed(false).update('');
39478         }
39479     },
39480
39481     slide : {
39482         show: function(msgEl, f){
39483             msgEl.slideIn('t', {stopFx:true});
39484         },
39485
39486         hide : function(msgEl, f){
39487             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39488         }
39489     },
39490
39491     slideRight : {
39492         show: function(msgEl, f){
39493             msgEl.fixDisplay();
39494             msgEl.alignTo(f.el, 'tl-tr');
39495             msgEl.slideIn('l', {stopFx:true});
39496         },
39497
39498         hide : function(msgEl, f){
39499             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39500         }
39501     }
39502 };/*
39503  * Based on:
39504  * Ext JS Library 1.1.1
39505  * Copyright(c) 2006-2007, Ext JS, LLC.
39506  *
39507  * Originally Released Under LGPL - original licence link has changed is not relivant.
39508  *
39509  * Fork - LGPL
39510  * <script type="text/javascript">
39511  */
39512  
39513
39514 /**
39515  * @class Roo.form.TextField
39516  * @extends Roo.form.Field
39517  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39518  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39519  * @constructor
39520  * Creates a new TextField
39521  * @param {Object} config Configuration options
39522  */
39523 Roo.form.TextField = function(config){
39524     Roo.form.TextField.superclass.constructor.call(this, config);
39525     this.addEvents({
39526         /**
39527          * @event autosize
39528          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39529          * according to the default logic, but this event provides a hook for the developer to apply additional
39530          * logic at runtime to resize the field if needed.
39531              * @param {Roo.form.Field} this This text field
39532              * @param {Number} width The new field width
39533              */
39534         autosize : true
39535     });
39536 };
39537
39538 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39539     /**
39540      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39541      */
39542     grow : false,
39543     /**
39544      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39545      */
39546     growMin : 30,
39547     /**
39548      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39549      */
39550     growMax : 800,
39551     /**
39552      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39553      */
39554     vtype : null,
39555     /**
39556      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39557      */
39558     maskRe : null,
39559     /**
39560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39561      */
39562     disableKeyFilter : false,
39563     /**
39564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39565      */
39566     allowBlank : true,
39567     /**
39568      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39569      */
39570     minLength : 0,
39571     /**
39572      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39573      */
39574     maxLength : Number.MAX_VALUE,
39575     /**
39576      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39577      */
39578     minLengthText : "The minimum length for this field is {0}",
39579     /**
39580      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39581      */
39582     maxLengthText : "The maximum length for this field is {0}",
39583     /**
39584      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39585      */
39586     selectOnFocus : false,
39587     /**
39588      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39589      */    
39590     allowLeadingSpace : false,
39591     /**
39592      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39593      */
39594     blankText : "This field is required",
39595     /**
39596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39597      * If available, this function will be called only after the basic validators all return true, and will be passed the
39598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39599      */
39600     validator : null,
39601     /**
39602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39605      */
39606     regex : null,
39607     /**
39608      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39609      */
39610     regexText : "",
39611     /**
39612      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39613      */
39614     emptyText : null,
39615    
39616
39617     // private
39618     initEvents : function()
39619     {
39620         if (this.emptyText) {
39621             this.el.attr('placeholder', this.emptyText);
39622         }
39623         
39624         Roo.form.TextField.superclass.initEvents.call(this);
39625         if(this.validationEvent == 'keyup'){
39626             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39627             this.el.on('keyup', this.filterValidation, this);
39628         }
39629         else if(this.validationEvent !== false){
39630             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39631         }
39632         
39633         if(this.selectOnFocus){
39634             this.on("focus", this.preFocus, this);
39635         }
39636         if (!this.allowLeadingSpace) {
39637             this.on('blur', this.cleanLeadingSpace, this);
39638         }
39639         
39640         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39641             this.el.on("keypress", this.filterKeys, this);
39642         }
39643         if(this.grow){
39644             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39645             this.el.on("click", this.autoSize,  this);
39646         }
39647         if(this.el.is('input[type=password]') && Roo.isSafari){
39648             this.el.on('keydown', this.SafariOnKeyDown, this);
39649         }
39650     },
39651
39652     processValue : function(value){
39653         if(this.stripCharsRe){
39654             var newValue = value.replace(this.stripCharsRe, '');
39655             if(newValue !== value){
39656                 this.setRawValue(newValue);
39657                 return newValue;
39658             }
39659         }
39660         return value;
39661     },
39662
39663     filterValidation : function(e){
39664         if(!e.isNavKeyPress()){
39665             this.validationTask.delay(this.validationDelay);
39666         }
39667     },
39668
39669     // private
39670     onKeyUp : function(e){
39671         if(!e.isNavKeyPress()){
39672             this.autoSize();
39673         }
39674     },
39675     // private - clean the leading white space
39676     cleanLeadingSpace : function(e)
39677     {
39678         if ( this.inputType == 'file') {
39679             return;
39680         }
39681         
39682         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39683     },
39684     /**
39685      * Resets the current field value to the originally-loaded value and clears any validation messages.
39686      *  
39687      */
39688     reset : function(){
39689         Roo.form.TextField.superclass.reset.call(this);
39690        
39691     }, 
39692     // private
39693     preFocus : function(){
39694         
39695         if(this.selectOnFocus){
39696             this.el.dom.select();
39697         }
39698     },
39699
39700     
39701     // private
39702     filterKeys : function(e){
39703         var k = e.getKey();
39704         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39705             return;
39706         }
39707         var c = e.getCharCode(), cc = String.fromCharCode(c);
39708         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39709             return;
39710         }
39711         if(!this.maskRe.test(cc)){
39712             e.stopEvent();
39713         }
39714     },
39715
39716     setValue : function(v){
39717         
39718         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39719         
39720         this.autoSize();
39721     },
39722
39723     /**
39724      * Validates a value according to the field's validation rules and marks the field as invalid
39725      * if the validation fails
39726      * @param {Mixed} value The value to validate
39727      * @return {Boolean} True if the value is valid, else false
39728      */
39729     validateValue : function(value){
39730         if(value.length < 1)  { // if it's blank
39731              if(this.allowBlank){
39732                 this.clearInvalid();
39733                 return true;
39734              }else{
39735                 this.markInvalid(this.blankText);
39736                 return false;
39737              }
39738         }
39739         if(value.length < this.minLength){
39740             this.markInvalid(String.format(this.minLengthText, this.minLength));
39741             return false;
39742         }
39743         if(value.length > this.maxLength){
39744             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39745             return false;
39746         }
39747         if(this.vtype){
39748             var vt = Roo.form.VTypes;
39749             if(!vt[this.vtype](value, this)){
39750                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39751                 return false;
39752             }
39753         }
39754         if(typeof this.validator == "function"){
39755             var msg = this.validator(value);
39756             if(msg !== true){
39757                 this.markInvalid(msg);
39758                 return false;
39759             }
39760         }
39761         if(this.regex && !this.regex.test(value)){
39762             this.markInvalid(this.regexText);
39763             return false;
39764         }
39765         return true;
39766     },
39767
39768     /**
39769      * Selects text in this field
39770      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39771      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39772      */
39773     selectText : function(start, end){
39774         var v = this.getRawValue();
39775         if(v.length > 0){
39776             start = start === undefined ? 0 : start;
39777             end = end === undefined ? v.length : end;
39778             var d = this.el.dom;
39779             if(d.setSelectionRange){
39780                 d.setSelectionRange(start, end);
39781             }else if(d.createTextRange){
39782                 var range = d.createTextRange();
39783                 range.moveStart("character", start);
39784                 range.moveEnd("character", v.length-end);
39785                 range.select();
39786             }
39787         }
39788     },
39789
39790     /**
39791      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39792      * This only takes effect if grow = true, and fires the autosize event.
39793      */
39794     autoSize : function(){
39795         if(!this.grow || !this.rendered){
39796             return;
39797         }
39798         if(!this.metrics){
39799             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39800         }
39801         var el = this.el;
39802         var v = el.dom.value;
39803         var d = document.createElement('div');
39804         d.appendChild(document.createTextNode(v));
39805         v = d.innerHTML;
39806         d = null;
39807         v += "&#160;";
39808         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39809         this.el.setWidth(w);
39810         this.fireEvent("autosize", this, w);
39811     },
39812     
39813     // private
39814     SafariOnKeyDown : function(event)
39815     {
39816         // this is a workaround for a password hang bug on chrome/ webkit.
39817         
39818         var isSelectAll = false;
39819         
39820         if(this.el.dom.selectionEnd > 0){
39821             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39822         }
39823         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39824             event.preventDefault();
39825             this.setValue('');
39826             return;
39827         }
39828         
39829         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39830             
39831             event.preventDefault();
39832             // this is very hacky as keydown always get's upper case.
39833             
39834             var cc = String.fromCharCode(event.getCharCode());
39835             
39836             
39837             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39838             
39839         }
39840         
39841         
39842     }
39843 });/*
39844  * Based on:
39845  * Ext JS Library 1.1.1
39846  * Copyright(c) 2006-2007, Ext JS, LLC.
39847  *
39848  * Originally Released Under LGPL - original licence link has changed is not relivant.
39849  *
39850  * Fork - LGPL
39851  * <script type="text/javascript">
39852  */
39853  
39854 /**
39855  * @class Roo.form.Hidden
39856  * @extends Roo.form.TextField
39857  * Simple Hidden element used on forms 
39858  * 
39859  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39860  * 
39861  * @constructor
39862  * Creates a new Hidden form element.
39863  * @param {Object} config Configuration options
39864  */
39865
39866
39867
39868 // easy hidden field...
39869 Roo.form.Hidden = function(config){
39870     Roo.form.Hidden.superclass.constructor.call(this, config);
39871 };
39872   
39873 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39874     fieldLabel:      '',
39875     inputType:      'hidden',
39876     width:          50,
39877     allowBlank:     true,
39878     labelSeparator: '',
39879     hidden:         true,
39880     itemCls :       'x-form-item-display-none'
39881
39882
39883 });
39884
39885
39886 /*
39887  * Based on:
39888  * Ext JS Library 1.1.1
39889  * Copyright(c) 2006-2007, Ext JS, LLC.
39890  *
39891  * Originally Released Under LGPL - original licence link has changed is not relivant.
39892  *
39893  * Fork - LGPL
39894  * <script type="text/javascript">
39895  */
39896  
39897 /**
39898  * @class Roo.form.TriggerField
39899  * @extends Roo.form.TextField
39900  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39901  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39902  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39903  * for which you can provide a custom implementation.  For example:
39904  * <pre><code>
39905 var trigger = new Roo.form.TriggerField();
39906 trigger.onTriggerClick = myTriggerFn;
39907 trigger.applyTo('my-field');
39908 </code></pre>
39909  *
39910  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39911  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39912  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39913  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39914  * @constructor
39915  * Create a new TriggerField.
39916  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39917  * to the base TextField)
39918  */
39919 Roo.form.TriggerField = function(config){
39920     this.mimicing = false;
39921     Roo.form.TriggerField.superclass.constructor.call(this, config);
39922 };
39923
39924 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39925     /**
39926      * @cfg {String} triggerClass A CSS class to apply to the trigger
39927      */
39928     /**
39929      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39930      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39931      */
39932     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39933     /**
39934      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39935      */
39936     hideTrigger:false,
39937
39938     /** @cfg {Boolean} grow @hide */
39939     /** @cfg {Number} growMin @hide */
39940     /** @cfg {Number} growMax @hide */
39941
39942     /**
39943      * @hide 
39944      * @method
39945      */
39946     autoSize: Roo.emptyFn,
39947     // private
39948     monitorTab : true,
39949     // private
39950     deferHeight : true,
39951
39952     
39953     actionMode : 'wrap',
39954     // private
39955     onResize : function(w, h){
39956         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39957         if(typeof w == 'number'){
39958             var x = w - this.trigger.getWidth();
39959             this.el.setWidth(this.adjustWidth('input', x));
39960             this.trigger.setStyle('left', x+'px');
39961         }
39962     },
39963
39964     // private
39965     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39966
39967     // private
39968     getResizeEl : function(){
39969         return this.wrap;
39970     },
39971
39972     // private
39973     getPositionEl : function(){
39974         return this.wrap;
39975     },
39976
39977     // private
39978     alignErrorIcon : function(){
39979         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39980     },
39981
39982     // private
39983     onRender : function(ct, position){
39984         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39985         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39986         this.trigger = this.wrap.createChild(this.triggerConfig ||
39987                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39988         if(this.hideTrigger){
39989             this.trigger.setDisplayed(false);
39990         }
39991         this.initTrigger();
39992         if(!this.width){
39993             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39994         }
39995     },
39996
39997     // private
39998     initTrigger : function(){
39999         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40000         this.trigger.addClassOnOver('x-form-trigger-over');
40001         this.trigger.addClassOnClick('x-form-trigger-click');
40002     },
40003
40004     // private
40005     onDestroy : function(){
40006         if(this.trigger){
40007             this.trigger.removeAllListeners();
40008             this.trigger.remove();
40009         }
40010         if(this.wrap){
40011             this.wrap.remove();
40012         }
40013         Roo.form.TriggerField.superclass.onDestroy.call(this);
40014     },
40015
40016     // private
40017     onFocus : function(){
40018         Roo.form.TriggerField.superclass.onFocus.call(this);
40019         if(!this.mimicing){
40020             this.wrap.addClass('x-trigger-wrap-focus');
40021             this.mimicing = true;
40022             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40023             if(this.monitorTab){
40024                 this.el.on("keydown", this.checkTab, this);
40025             }
40026         }
40027     },
40028
40029     // private
40030     checkTab : function(e){
40031         if(e.getKey() == e.TAB){
40032             this.triggerBlur();
40033         }
40034     },
40035
40036     // private
40037     onBlur : function(){
40038         // do nothing
40039     },
40040
40041     // private
40042     mimicBlur : function(e, t){
40043         if(!this.wrap.contains(t) && this.validateBlur()){
40044             this.triggerBlur();
40045         }
40046     },
40047
40048     // private
40049     triggerBlur : function(){
40050         this.mimicing = false;
40051         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40052         if(this.monitorTab){
40053             this.el.un("keydown", this.checkTab, this);
40054         }
40055         this.wrap.removeClass('x-trigger-wrap-focus');
40056         Roo.form.TriggerField.superclass.onBlur.call(this);
40057     },
40058
40059     // private
40060     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40061     validateBlur : function(e, t){
40062         return true;
40063     },
40064
40065     // private
40066     onDisable : function(){
40067         Roo.form.TriggerField.superclass.onDisable.call(this);
40068         if(this.wrap){
40069             this.wrap.addClass('x-item-disabled');
40070         }
40071     },
40072
40073     // private
40074     onEnable : function(){
40075         Roo.form.TriggerField.superclass.onEnable.call(this);
40076         if(this.wrap){
40077             this.wrap.removeClass('x-item-disabled');
40078         }
40079     },
40080
40081     // private
40082     onShow : function(){
40083         var ae = this.getActionEl();
40084         
40085         if(ae){
40086             ae.dom.style.display = '';
40087             ae.dom.style.visibility = 'visible';
40088         }
40089     },
40090
40091     // private
40092     
40093     onHide : function(){
40094         var ae = this.getActionEl();
40095         ae.dom.style.display = 'none';
40096     },
40097
40098     /**
40099      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40100      * by an implementing function.
40101      * @method
40102      * @param {EventObject} e
40103      */
40104     onTriggerClick : Roo.emptyFn
40105 });
40106
40107 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40108 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40109 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40110 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40111     initComponent : function(){
40112         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40113
40114         this.triggerConfig = {
40115             tag:'span', cls:'x-form-twin-triggers', cn:[
40116             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40117             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40118         ]};
40119     },
40120
40121     getTrigger : function(index){
40122         return this.triggers[index];
40123     },
40124
40125     initTrigger : function(){
40126         var ts = this.trigger.select('.x-form-trigger', true);
40127         this.wrap.setStyle('overflow', 'hidden');
40128         var triggerField = this;
40129         ts.each(function(t, all, index){
40130             t.hide = function(){
40131                 var w = triggerField.wrap.getWidth();
40132                 this.dom.style.display = 'none';
40133                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40134             };
40135             t.show = function(){
40136                 var w = triggerField.wrap.getWidth();
40137                 this.dom.style.display = '';
40138                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40139             };
40140             var triggerIndex = 'Trigger'+(index+1);
40141
40142             if(this['hide'+triggerIndex]){
40143                 t.dom.style.display = 'none';
40144             }
40145             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40146             t.addClassOnOver('x-form-trigger-over');
40147             t.addClassOnClick('x-form-trigger-click');
40148         }, this);
40149         this.triggers = ts.elements;
40150     },
40151
40152     onTrigger1Click : Roo.emptyFn,
40153     onTrigger2Click : Roo.emptyFn
40154 });/*
40155  * Based on:
40156  * Ext JS Library 1.1.1
40157  * Copyright(c) 2006-2007, Ext JS, LLC.
40158  *
40159  * Originally Released Under LGPL - original licence link has changed is not relivant.
40160  *
40161  * Fork - LGPL
40162  * <script type="text/javascript">
40163  */
40164  
40165 /**
40166  * @class Roo.form.TextArea
40167  * @extends Roo.form.TextField
40168  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40169  * support for auto-sizing.
40170  * @constructor
40171  * Creates a new TextArea
40172  * @param {Object} config Configuration options
40173  */
40174 Roo.form.TextArea = function(config){
40175     Roo.form.TextArea.superclass.constructor.call(this, config);
40176     // these are provided exchanges for backwards compat
40177     // minHeight/maxHeight were replaced by growMin/growMax to be
40178     // compatible with TextField growing config values
40179     if(this.minHeight !== undefined){
40180         this.growMin = this.minHeight;
40181     }
40182     if(this.maxHeight !== undefined){
40183         this.growMax = this.maxHeight;
40184     }
40185 };
40186
40187 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40188     /**
40189      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40190      */
40191     growMin : 60,
40192     /**
40193      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40194      */
40195     growMax: 1000,
40196     /**
40197      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40198      * in the field (equivalent to setting overflow: hidden, defaults to false)
40199      */
40200     preventScrollbars: false,
40201     /**
40202      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40203      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40204      */
40205
40206     // private
40207     onRender : function(ct, position){
40208         if(!this.el){
40209             this.defaultAutoCreate = {
40210                 tag: "textarea",
40211                 style:"width:300px;height:60px;",
40212                 autocomplete: "new-password"
40213             };
40214         }
40215         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40216         if(this.grow){
40217             this.textSizeEl = Roo.DomHelper.append(document.body, {
40218                 tag: "pre", cls: "x-form-grow-sizer"
40219             });
40220             if(this.preventScrollbars){
40221                 this.el.setStyle("overflow", "hidden");
40222             }
40223             this.el.setHeight(this.growMin);
40224         }
40225     },
40226
40227     onDestroy : function(){
40228         if(this.textSizeEl){
40229             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40230         }
40231         Roo.form.TextArea.superclass.onDestroy.call(this);
40232     },
40233
40234     // private
40235     onKeyUp : function(e){
40236         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40237             this.autoSize();
40238         }
40239     },
40240
40241     /**
40242      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40243      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40244      */
40245     autoSize : function(){
40246         if(!this.grow || !this.textSizeEl){
40247             return;
40248         }
40249         var el = this.el;
40250         var v = el.dom.value;
40251         var ts = this.textSizeEl;
40252
40253         ts.innerHTML = '';
40254         ts.appendChild(document.createTextNode(v));
40255         v = ts.innerHTML;
40256
40257         Roo.fly(ts).setWidth(this.el.getWidth());
40258         if(v.length < 1){
40259             v = "&#160;&#160;";
40260         }else{
40261             if(Roo.isIE){
40262                 v = v.replace(/\n/g, '<p>&#160;</p>');
40263             }
40264             v += "&#160;\n&#160;";
40265         }
40266         ts.innerHTML = v;
40267         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40268         if(h != this.lastHeight){
40269             this.lastHeight = h;
40270             this.el.setHeight(h);
40271             this.fireEvent("autosize", this, h);
40272         }
40273     }
40274 });/*
40275  * Based on:
40276  * Ext JS Library 1.1.1
40277  * Copyright(c) 2006-2007, Ext JS, LLC.
40278  *
40279  * Originally Released Under LGPL - original licence link has changed is not relivant.
40280  *
40281  * Fork - LGPL
40282  * <script type="text/javascript">
40283  */
40284  
40285
40286 /**
40287  * @class Roo.form.NumberField
40288  * @extends Roo.form.TextField
40289  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40290  * @constructor
40291  * Creates a new NumberField
40292  * @param {Object} config Configuration options
40293  */
40294 Roo.form.NumberField = function(config){
40295     Roo.form.NumberField.superclass.constructor.call(this, config);
40296 };
40297
40298 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40299     /**
40300      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40301      */
40302     fieldClass: "x-form-field x-form-num-field",
40303     /**
40304      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40305      */
40306     allowDecimals : true,
40307     /**
40308      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40309      */
40310     decimalSeparator : ".",
40311     /**
40312      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40313      */
40314     decimalPrecision : 2,
40315     /**
40316      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40317      */
40318     allowNegative : true,
40319     /**
40320      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40321      */
40322     minValue : Number.NEGATIVE_INFINITY,
40323     /**
40324      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40325      */
40326     maxValue : Number.MAX_VALUE,
40327     /**
40328      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40329      */
40330     minText : "The minimum value for this field is {0}",
40331     /**
40332      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40333      */
40334     maxText : "The maximum value for this field is {0}",
40335     /**
40336      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40337      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40338      */
40339     nanText : "{0} is not a valid number",
40340
40341     // private
40342     initEvents : function(){
40343         Roo.form.NumberField.superclass.initEvents.call(this);
40344         var allowed = "0123456789";
40345         if(this.allowDecimals){
40346             allowed += this.decimalSeparator;
40347         }
40348         if(this.allowNegative){
40349             allowed += "-";
40350         }
40351         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40352         var keyPress = function(e){
40353             var k = e.getKey();
40354             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40355                 return;
40356             }
40357             var c = e.getCharCode();
40358             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40359                 e.stopEvent();
40360             }
40361         };
40362         this.el.on("keypress", keyPress, this);
40363     },
40364
40365     // private
40366     validateValue : function(value){
40367         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40368             return false;
40369         }
40370         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40371              return true;
40372         }
40373         var num = this.parseValue(value);
40374         if(isNaN(num)){
40375             this.markInvalid(String.format(this.nanText, value));
40376             return false;
40377         }
40378         if(num < this.minValue){
40379             this.markInvalid(String.format(this.minText, this.minValue));
40380             return false;
40381         }
40382         if(num > this.maxValue){
40383             this.markInvalid(String.format(this.maxText, this.maxValue));
40384             return false;
40385         }
40386         return true;
40387     },
40388
40389     getValue : function(){
40390         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40391     },
40392
40393     // private
40394     parseValue : function(value){
40395         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40396         return isNaN(value) ? '' : value;
40397     },
40398
40399     // private
40400     fixPrecision : function(value){
40401         var nan = isNaN(value);
40402         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40403             return nan ? '' : value;
40404         }
40405         return parseFloat(value).toFixed(this.decimalPrecision);
40406     },
40407
40408     setValue : function(v){
40409         v = this.fixPrecision(v);
40410         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40411     },
40412
40413     // private
40414     decimalPrecisionFcn : function(v){
40415         return Math.floor(v);
40416     },
40417
40418     beforeBlur : function(){
40419         var v = this.parseValue(this.getRawValue());
40420         if(v){
40421             this.setValue(v);
40422         }
40423     }
40424 });/*
40425  * Based on:
40426  * Ext JS Library 1.1.1
40427  * Copyright(c) 2006-2007, Ext JS, LLC.
40428  *
40429  * Originally Released Under LGPL - original licence link has changed is not relivant.
40430  *
40431  * Fork - LGPL
40432  * <script type="text/javascript">
40433  */
40434  
40435 /**
40436  * @class Roo.form.DateField
40437  * @extends Roo.form.TriggerField
40438  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40439 * @constructor
40440 * Create a new DateField
40441 * @param {Object} config
40442  */
40443 Roo.form.DateField = function(config)
40444 {
40445     Roo.form.DateField.superclass.constructor.call(this, config);
40446     
40447       this.addEvents({
40448          
40449         /**
40450          * @event select
40451          * Fires when a date is selected
40452              * @param {Roo.form.DateField} combo This combo box
40453              * @param {Date} date The date selected
40454              */
40455         'select' : true
40456          
40457     });
40458     
40459     
40460     if(typeof this.minValue == "string") {
40461         this.minValue = this.parseDate(this.minValue);
40462     }
40463     if(typeof this.maxValue == "string") {
40464         this.maxValue = this.parseDate(this.maxValue);
40465     }
40466     this.ddMatch = null;
40467     if(this.disabledDates){
40468         var dd = this.disabledDates;
40469         var re = "(?:";
40470         for(var i = 0; i < dd.length; i++){
40471             re += dd[i];
40472             if(i != dd.length-1) {
40473                 re += "|";
40474             }
40475         }
40476         this.ddMatch = new RegExp(re + ")");
40477     }
40478 };
40479
40480 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40481     /**
40482      * @cfg {String} format
40483      * The default date format string which can be overriden for localization support.  The format must be
40484      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40485      */
40486     format : "m/d/y",
40487     /**
40488      * @cfg {String} altFormats
40489      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40490      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40491      */
40492     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40493     /**
40494      * @cfg {Array} disabledDays
40495      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40496      */
40497     disabledDays : null,
40498     /**
40499      * @cfg {String} disabledDaysText
40500      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40501      */
40502     disabledDaysText : "Disabled",
40503     /**
40504      * @cfg {Array} disabledDates
40505      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40506      * expression so they are very powerful. Some examples:
40507      * <ul>
40508      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40509      * <li>["03/08", "09/16"] would disable those days for every year</li>
40510      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40511      * <li>["03/../2006"] would disable every day in March 2006</li>
40512      * <li>["^03"] would disable every day in every March</li>
40513      * </ul>
40514      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40515      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40516      */
40517     disabledDates : null,
40518     /**
40519      * @cfg {String} disabledDatesText
40520      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40521      */
40522     disabledDatesText : "Disabled",
40523     /**
40524      * @cfg {Date/String} minValue
40525      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40526      * valid format (defaults to null).
40527      */
40528     minValue : null,
40529     /**
40530      * @cfg {Date/String} maxValue
40531      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40532      * valid format (defaults to null).
40533      */
40534     maxValue : null,
40535     /**
40536      * @cfg {String} minText
40537      * The error text to display when the date in the cell is before minValue (defaults to
40538      * 'The date in this field must be after {minValue}').
40539      */
40540     minText : "The date in this field must be equal to or after {0}",
40541     /**
40542      * @cfg {String} maxText
40543      * The error text to display when the date in the cell is after maxValue (defaults to
40544      * 'The date in this field must be before {maxValue}').
40545      */
40546     maxText : "The date in this field must be equal to or before {0}",
40547     /**
40548      * @cfg {String} invalidText
40549      * The error text to display when the date in the field is invalid (defaults to
40550      * '{value} is not a valid date - it must be in the format {format}').
40551      */
40552     invalidText : "{0} is not a valid date - it must be in the format {1}",
40553     /**
40554      * @cfg {String} triggerClass
40555      * An additional CSS class used to style the trigger button.  The trigger will always get the
40556      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40557      * which displays a calendar icon).
40558      */
40559     triggerClass : 'x-form-date-trigger',
40560     
40561
40562     /**
40563      * @cfg {Boolean} useIso
40564      * if enabled, then the date field will use a hidden field to store the 
40565      * real value as iso formated date. default (false)
40566      */ 
40567     useIso : false,
40568     /**
40569      * @cfg {String/Object} autoCreate
40570      * A DomHelper element spec, or true for a default element spec (defaults to
40571      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40572      */ 
40573     // private
40574     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40575     
40576     // private
40577     hiddenField: false,
40578     
40579     onRender : function(ct, position)
40580     {
40581         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40582         if (this.useIso) {
40583             //this.el.dom.removeAttribute('name'); 
40584             Roo.log("Changing name?");
40585             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40586             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40587                     'before', true);
40588             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40589             // prevent input submission
40590             this.hiddenName = this.name;
40591         }
40592             
40593             
40594     },
40595     
40596     // private
40597     validateValue : function(value)
40598     {
40599         value = this.formatDate(value);
40600         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40601             Roo.log('super failed');
40602             return false;
40603         }
40604         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40605              return true;
40606         }
40607         var svalue = value;
40608         value = this.parseDate(value);
40609         if(!value){
40610             Roo.log('parse date failed' + svalue);
40611             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40612             return false;
40613         }
40614         var time = value.getTime();
40615         if(this.minValue && time < this.minValue.getTime()){
40616             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40617             return false;
40618         }
40619         if(this.maxValue && time > this.maxValue.getTime()){
40620             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40621             return false;
40622         }
40623         if(this.disabledDays){
40624             var day = value.getDay();
40625             for(var i = 0; i < this.disabledDays.length; i++) {
40626                 if(day === this.disabledDays[i]){
40627                     this.markInvalid(this.disabledDaysText);
40628                     return false;
40629                 }
40630             }
40631         }
40632         var fvalue = this.formatDate(value);
40633         if(this.ddMatch && this.ddMatch.test(fvalue)){
40634             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40635             return false;
40636         }
40637         return true;
40638     },
40639
40640     // private
40641     // Provides logic to override the default TriggerField.validateBlur which just returns true
40642     validateBlur : function(){
40643         return !this.menu || !this.menu.isVisible();
40644     },
40645     
40646     getName: function()
40647     {
40648         // returns hidden if it's set..
40649         if (!this.rendered) {return ''};
40650         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40651         
40652     },
40653
40654     /**
40655      * Returns the current date value of the date field.
40656      * @return {Date} The date value
40657      */
40658     getValue : function(){
40659         
40660         return  this.hiddenField ?
40661                 this.hiddenField.value :
40662                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40663     },
40664
40665     /**
40666      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40667      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40668      * (the default format used is "m/d/y").
40669      * <br />Usage:
40670      * <pre><code>
40671 //All of these calls set the same date value (May 4, 2006)
40672
40673 //Pass a date object:
40674 var dt = new Date('5/4/06');
40675 dateField.setValue(dt);
40676
40677 //Pass a date string (default format):
40678 dateField.setValue('5/4/06');
40679
40680 //Pass a date string (custom format):
40681 dateField.format = 'Y-m-d';
40682 dateField.setValue('2006-5-4');
40683 </code></pre>
40684      * @param {String/Date} date The date or valid date string
40685      */
40686     setValue : function(date){
40687         if (this.hiddenField) {
40688             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40689         }
40690         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40691         // make sure the value field is always stored as a date..
40692         this.value = this.parseDate(date);
40693         
40694         
40695     },
40696
40697     // private
40698     parseDate : function(value){
40699         if(!value || value instanceof Date){
40700             return value;
40701         }
40702         var v = Date.parseDate(value, this.format);
40703          if (!v && this.useIso) {
40704             v = Date.parseDate(value, 'Y-m-d');
40705         }
40706         if(!v && this.altFormats){
40707             if(!this.altFormatsArray){
40708                 this.altFormatsArray = this.altFormats.split("|");
40709             }
40710             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40711                 v = Date.parseDate(value, this.altFormatsArray[i]);
40712             }
40713         }
40714         return v;
40715     },
40716
40717     // private
40718     formatDate : function(date, fmt){
40719         return (!date || !(date instanceof Date)) ?
40720                date : date.dateFormat(fmt || this.format);
40721     },
40722
40723     // private
40724     menuListeners : {
40725         select: function(m, d){
40726             
40727             this.setValue(d);
40728             this.fireEvent('select', this, d);
40729         },
40730         show : function(){ // retain focus styling
40731             this.onFocus();
40732         },
40733         hide : function(){
40734             this.focus.defer(10, this);
40735             var ml = this.menuListeners;
40736             this.menu.un("select", ml.select,  this);
40737             this.menu.un("show", ml.show,  this);
40738             this.menu.un("hide", ml.hide,  this);
40739         }
40740     },
40741
40742     // private
40743     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40744     onTriggerClick : function(){
40745         if(this.disabled){
40746             return;
40747         }
40748         if(this.menu == null){
40749             this.menu = new Roo.menu.DateMenu();
40750         }
40751         Roo.apply(this.menu.picker,  {
40752             showClear: this.allowBlank,
40753             minDate : this.minValue,
40754             maxDate : this.maxValue,
40755             disabledDatesRE : this.ddMatch,
40756             disabledDatesText : this.disabledDatesText,
40757             disabledDays : this.disabledDays,
40758             disabledDaysText : this.disabledDaysText,
40759             format : this.useIso ? 'Y-m-d' : this.format,
40760             minText : String.format(this.minText, this.formatDate(this.minValue)),
40761             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40762         });
40763         this.menu.on(Roo.apply({}, this.menuListeners, {
40764             scope:this
40765         }));
40766         this.menu.picker.setValue(this.getValue() || new Date());
40767         this.menu.show(this.el, "tl-bl?");
40768     },
40769
40770     beforeBlur : function(){
40771         var v = this.parseDate(this.getRawValue());
40772         if(v){
40773             this.setValue(v);
40774         }
40775     },
40776
40777     /*@
40778      * overide
40779      * 
40780      */
40781     isDirty : function() {
40782         if(this.disabled) {
40783             return false;
40784         }
40785         
40786         if(typeof(this.startValue) === 'undefined'){
40787             return false;
40788         }
40789         
40790         return String(this.getValue()) !== String(this.startValue);
40791         
40792     },
40793     // @overide
40794     cleanLeadingSpace : function(e)
40795     {
40796        return;
40797     }
40798     
40799 });/*
40800  * Based on:
40801  * Ext JS Library 1.1.1
40802  * Copyright(c) 2006-2007, Ext JS, LLC.
40803  *
40804  * Originally Released Under LGPL - original licence link has changed is not relivant.
40805  *
40806  * Fork - LGPL
40807  * <script type="text/javascript">
40808  */
40809  
40810 /**
40811  * @class Roo.form.MonthField
40812  * @extends Roo.form.TriggerField
40813  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40814 * @constructor
40815 * Create a new MonthField
40816 * @param {Object} config
40817  */
40818 Roo.form.MonthField = function(config){
40819     
40820     Roo.form.MonthField.superclass.constructor.call(this, config);
40821     
40822       this.addEvents({
40823          
40824         /**
40825          * @event select
40826          * Fires when a date is selected
40827              * @param {Roo.form.MonthFieeld} combo This combo box
40828              * @param {Date} date The date selected
40829              */
40830         'select' : true
40831          
40832     });
40833     
40834     
40835     if(typeof this.minValue == "string") {
40836         this.minValue = this.parseDate(this.minValue);
40837     }
40838     if(typeof this.maxValue == "string") {
40839         this.maxValue = this.parseDate(this.maxValue);
40840     }
40841     this.ddMatch = null;
40842     if(this.disabledDates){
40843         var dd = this.disabledDates;
40844         var re = "(?:";
40845         for(var i = 0; i < dd.length; i++){
40846             re += dd[i];
40847             if(i != dd.length-1) {
40848                 re += "|";
40849             }
40850         }
40851         this.ddMatch = new RegExp(re + ")");
40852     }
40853 };
40854
40855 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40856     /**
40857      * @cfg {String} format
40858      * The default date format string which can be overriden for localization support.  The format must be
40859      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40860      */
40861     format : "M Y",
40862     /**
40863      * @cfg {String} altFormats
40864      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40865      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40866      */
40867     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40868     /**
40869      * @cfg {Array} disabledDays
40870      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40871      */
40872     disabledDays : [0,1,2,3,4,5,6],
40873     /**
40874      * @cfg {String} disabledDaysText
40875      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40876      */
40877     disabledDaysText : "Disabled",
40878     /**
40879      * @cfg {Array} disabledDates
40880      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40881      * expression so they are very powerful. Some examples:
40882      * <ul>
40883      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40884      * <li>["03/08", "09/16"] would disable those days for every year</li>
40885      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40886      * <li>["03/../2006"] would disable every day in March 2006</li>
40887      * <li>["^03"] would disable every day in every March</li>
40888      * </ul>
40889      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40890      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40891      */
40892     disabledDates : null,
40893     /**
40894      * @cfg {String} disabledDatesText
40895      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40896      */
40897     disabledDatesText : "Disabled",
40898     /**
40899      * @cfg {Date/String} minValue
40900      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40901      * valid format (defaults to null).
40902      */
40903     minValue : null,
40904     /**
40905      * @cfg {Date/String} maxValue
40906      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40907      * valid format (defaults to null).
40908      */
40909     maxValue : null,
40910     /**
40911      * @cfg {String} minText
40912      * The error text to display when the date in the cell is before minValue (defaults to
40913      * 'The date in this field must be after {minValue}').
40914      */
40915     minText : "The date in this field must be equal to or after {0}",
40916     /**
40917      * @cfg {String} maxTextf
40918      * The error text to display when the date in the cell is after maxValue (defaults to
40919      * 'The date in this field must be before {maxValue}').
40920      */
40921     maxText : "The date in this field must be equal to or before {0}",
40922     /**
40923      * @cfg {String} invalidText
40924      * The error text to display when the date in the field is invalid (defaults to
40925      * '{value} is not a valid date - it must be in the format {format}').
40926      */
40927     invalidText : "{0} is not a valid date - it must be in the format {1}",
40928     /**
40929      * @cfg {String} triggerClass
40930      * An additional CSS class used to style the trigger button.  The trigger will always get the
40931      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40932      * which displays a calendar icon).
40933      */
40934     triggerClass : 'x-form-date-trigger',
40935     
40936
40937     /**
40938      * @cfg {Boolean} useIso
40939      * if enabled, then the date field will use a hidden field to store the 
40940      * real value as iso formated date. default (true)
40941      */ 
40942     useIso : true,
40943     /**
40944      * @cfg {String/Object} autoCreate
40945      * A DomHelper element spec, or true for a default element spec (defaults to
40946      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40947      */ 
40948     // private
40949     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40950     
40951     // private
40952     hiddenField: false,
40953     
40954     hideMonthPicker : false,
40955     
40956     onRender : function(ct, position)
40957     {
40958         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40959         if (this.useIso) {
40960             this.el.dom.removeAttribute('name'); 
40961             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40962                     'before', true);
40963             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40964             // prevent input submission
40965             this.hiddenName = this.name;
40966         }
40967             
40968             
40969     },
40970     
40971     // private
40972     validateValue : function(value)
40973     {
40974         value = this.formatDate(value);
40975         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40976             return false;
40977         }
40978         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40979              return true;
40980         }
40981         var svalue = value;
40982         value = this.parseDate(value);
40983         if(!value){
40984             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40985             return false;
40986         }
40987         var time = value.getTime();
40988         if(this.minValue && time < this.minValue.getTime()){
40989             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40990             return false;
40991         }
40992         if(this.maxValue && time > this.maxValue.getTime()){
40993             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40994             return false;
40995         }
40996         /*if(this.disabledDays){
40997             var day = value.getDay();
40998             for(var i = 0; i < this.disabledDays.length; i++) {
40999                 if(day === this.disabledDays[i]){
41000                     this.markInvalid(this.disabledDaysText);
41001                     return false;
41002                 }
41003             }
41004         }
41005         */
41006         var fvalue = this.formatDate(value);
41007         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41008             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41009             return false;
41010         }
41011         */
41012         return true;
41013     },
41014
41015     // private
41016     // Provides logic to override the default TriggerField.validateBlur which just returns true
41017     validateBlur : function(){
41018         return !this.menu || !this.menu.isVisible();
41019     },
41020
41021     /**
41022      * Returns the current date value of the date field.
41023      * @return {Date} The date value
41024      */
41025     getValue : function(){
41026         
41027         
41028         
41029         return  this.hiddenField ?
41030                 this.hiddenField.value :
41031                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41032     },
41033
41034     /**
41035      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41036      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41037      * (the default format used is "m/d/y").
41038      * <br />Usage:
41039      * <pre><code>
41040 //All of these calls set the same date value (May 4, 2006)
41041
41042 //Pass a date object:
41043 var dt = new Date('5/4/06');
41044 monthField.setValue(dt);
41045
41046 //Pass a date string (default format):
41047 monthField.setValue('5/4/06');
41048
41049 //Pass a date string (custom format):
41050 monthField.format = 'Y-m-d';
41051 monthField.setValue('2006-5-4');
41052 </code></pre>
41053      * @param {String/Date} date The date or valid date string
41054      */
41055     setValue : function(date){
41056         Roo.log('month setValue' + date);
41057         // can only be first of month..
41058         
41059         var val = this.parseDate(date);
41060         
41061         if (this.hiddenField) {
41062             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41063         }
41064         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41065         this.value = this.parseDate(date);
41066     },
41067
41068     // private
41069     parseDate : function(value){
41070         if(!value || value instanceof Date){
41071             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41072             return value;
41073         }
41074         var v = Date.parseDate(value, this.format);
41075         if (!v && this.useIso) {
41076             v = Date.parseDate(value, 'Y-m-d');
41077         }
41078         if (v) {
41079             // 
41080             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41081         }
41082         
41083         
41084         if(!v && this.altFormats){
41085             if(!this.altFormatsArray){
41086                 this.altFormatsArray = this.altFormats.split("|");
41087             }
41088             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41089                 v = Date.parseDate(value, this.altFormatsArray[i]);
41090             }
41091         }
41092         return v;
41093     },
41094
41095     // private
41096     formatDate : function(date, fmt){
41097         return (!date || !(date instanceof Date)) ?
41098                date : date.dateFormat(fmt || this.format);
41099     },
41100
41101     // private
41102     menuListeners : {
41103         select: function(m, d){
41104             this.setValue(d);
41105             this.fireEvent('select', this, d);
41106         },
41107         show : function(){ // retain focus styling
41108             this.onFocus();
41109         },
41110         hide : function(){
41111             this.focus.defer(10, this);
41112             var ml = this.menuListeners;
41113             this.menu.un("select", ml.select,  this);
41114             this.menu.un("show", ml.show,  this);
41115             this.menu.un("hide", ml.hide,  this);
41116         }
41117     },
41118     // private
41119     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41120     onTriggerClick : function(){
41121         if(this.disabled){
41122             return;
41123         }
41124         if(this.menu == null){
41125             this.menu = new Roo.menu.DateMenu();
41126            
41127         }
41128         
41129         Roo.apply(this.menu.picker,  {
41130             
41131             showClear: this.allowBlank,
41132             minDate : this.minValue,
41133             maxDate : this.maxValue,
41134             disabledDatesRE : this.ddMatch,
41135             disabledDatesText : this.disabledDatesText,
41136             
41137             format : this.useIso ? 'Y-m-d' : this.format,
41138             minText : String.format(this.minText, this.formatDate(this.minValue)),
41139             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41140             
41141         });
41142          this.menu.on(Roo.apply({}, this.menuListeners, {
41143             scope:this
41144         }));
41145        
41146         
41147         var m = this.menu;
41148         var p = m.picker;
41149         
41150         // hide month picker get's called when we called by 'before hide';
41151         
41152         var ignorehide = true;
41153         p.hideMonthPicker  = function(disableAnim){
41154             if (ignorehide) {
41155                 return;
41156             }
41157              if(this.monthPicker){
41158                 Roo.log("hideMonthPicker called");
41159                 if(disableAnim === true){
41160                     this.monthPicker.hide();
41161                 }else{
41162                     this.monthPicker.slideOut('t', {duration:.2});
41163                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41164                     p.fireEvent("select", this, this.value);
41165                     m.hide();
41166                 }
41167             }
41168         }
41169         
41170         Roo.log('picker set value');
41171         Roo.log(this.getValue());
41172         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41173         m.show(this.el, 'tl-bl?');
41174         ignorehide  = false;
41175         // this will trigger hideMonthPicker..
41176         
41177         
41178         // hidden the day picker
41179         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41180         
41181         
41182         
41183       
41184         
41185         p.showMonthPicker.defer(100, p);
41186     
41187         
41188        
41189     },
41190
41191     beforeBlur : function(){
41192         var v = this.parseDate(this.getRawValue());
41193         if(v){
41194             this.setValue(v);
41195         }
41196     }
41197
41198     /** @cfg {Boolean} grow @hide */
41199     /** @cfg {Number} growMin @hide */
41200     /** @cfg {Number} growMax @hide */
41201     /**
41202      * @hide
41203      * @method autoSize
41204      */
41205 });/*
41206  * Based on:
41207  * Ext JS Library 1.1.1
41208  * Copyright(c) 2006-2007, Ext JS, LLC.
41209  *
41210  * Originally Released Under LGPL - original licence link has changed is not relivant.
41211  *
41212  * Fork - LGPL
41213  * <script type="text/javascript">
41214  */
41215  
41216
41217 /**
41218  * @class Roo.form.ComboBox
41219  * @extends Roo.form.TriggerField
41220  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41221  * @constructor
41222  * Create a new ComboBox.
41223  * @param {Object} config Configuration options
41224  */
41225 Roo.form.ComboBox = function(config){
41226     Roo.form.ComboBox.superclass.constructor.call(this, config);
41227     this.addEvents({
41228         /**
41229          * @event expand
41230          * Fires when the dropdown list is expanded
41231              * @param {Roo.form.ComboBox} combo This combo box
41232              */
41233         'expand' : true,
41234         /**
41235          * @event collapse
41236          * Fires when the dropdown list is collapsed
41237              * @param {Roo.form.ComboBox} combo This combo box
41238              */
41239         'collapse' : true,
41240         /**
41241          * @event beforeselect
41242          * Fires before a list item is selected. Return false to cancel the selection.
41243              * @param {Roo.form.ComboBox} combo This combo box
41244              * @param {Roo.data.Record} record The data record returned from the underlying store
41245              * @param {Number} index The index of the selected item in the dropdown list
41246              */
41247         'beforeselect' : true,
41248         /**
41249          * @event select
41250          * Fires when a list item is selected
41251              * @param {Roo.form.ComboBox} combo This combo box
41252              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41253              * @param {Number} index The index of the selected item in the dropdown list
41254              */
41255         'select' : true,
41256         /**
41257          * @event beforequery
41258          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41259          * The event object passed has these properties:
41260              * @param {Roo.form.ComboBox} combo This combo box
41261              * @param {String} query The query
41262              * @param {Boolean} forceAll true to force "all" query
41263              * @param {Boolean} cancel true to cancel the query
41264              * @param {Object} e The query event object
41265              */
41266         'beforequery': true,
41267          /**
41268          * @event add
41269          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41270              * @param {Roo.form.ComboBox} combo This combo box
41271              */
41272         'add' : true,
41273         /**
41274          * @event edit
41275          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41276              * @param {Roo.form.ComboBox} combo This combo box
41277              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41278              */
41279         'edit' : true
41280         
41281         
41282     });
41283     if(this.transform){
41284         this.allowDomMove = false;
41285         var s = Roo.getDom(this.transform);
41286         if(!this.hiddenName){
41287             this.hiddenName = s.name;
41288         }
41289         if(!this.store){
41290             this.mode = 'local';
41291             var d = [], opts = s.options;
41292             for(var i = 0, len = opts.length;i < len; i++){
41293                 var o = opts[i];
41294                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41295                 if(o.selected) {
41296                     this.value = value;
41297                 }
41298                 d.push([value, o.text]);
41299             }
41300             this.store = new Roo.data.SimpleStore({
41301                 'id': 0,
41302                 fields: ['value', 'text'],
41303                 data : d
41304             });
41305             this.valueField = 'value';
41306             this.displayField = 'text';
41307         }
41308         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41309         if(!this.lazyRender){
41310             this.target = true;
41311             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41312             s.parentNode.removeChild(s); // remove it
41313             this.render(this.el.parentNode);
41314         }else{
41315             s.parentNode.removeChild(s); // remove it
41316         }
41317
41318     }
41319     if (this.store) {
41320         this.store = Roo.factory(this.store, Roo.data);
41321     }
41322     
41323     this.selectedIndex = -1;
41324     if(this.mode == 'local'){
41325         if(config.queryDelay === undefined){
41326             this.queryDelay = 10;
41327         }
41328         if(config.minChars === undefined){
41329             this.minChars = 0;
41330         }
41331     }
41332 };
41333
41334 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41335     /**
41336      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41337      */
41338     /**
41339      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41340      * rendering into an Roo.Editor, defaults to false)
41341      */
41342     /**
41343      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41344      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41345      */
41346     /**
41347      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41348      */
41349     /**
41350      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41351      * the dropdown list (defaults to undefined, with no header element)
41352      */
41353
41354      /**
41355      * @cfg {String/Roo.Template} tpl The template to use to render the output
41356      */
41357      
41358     // private
41359     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41360     /**
41361      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41362      */
41363     listWidth: undefined,
41364     /**
41365      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41366      * mode = 'remote' or 'text' if mode = 'local')
41367      */
41368     displayField: undefined,
41369     /**
41370      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41371      * mode = 'remote' or 'value' if mode = 'local'). 
41372      * Note: use of a valueField requires the user make a selection
41373      * in order for a value to be mapped.
41374      */
41375     valueField: undefined,
41376     
41377     
41378     /**
41379      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41380      * field's data value (defaults to the underlying DOM element's name)
41381      */
41382     hiddenName: undefined,
41383     /**
41384      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41385      */
41386     listClass: '',
41387     /**
41388      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41389      */
41390     selectedClass: 'x-combo-selected',
41391     /**
41392      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41393      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41394      * which displays a downward arrow icon).
41395      */
41396     triggerClass : 'x-form-arrow-trigger',
41397     /**
41398      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41399      */
41400     shadow:'sides',
41401     /**
41402      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41403      * anchor positions (defaults to 'tl-bl')
41404      */
41405     listAlign: 'tl-bl?',
41406     /**
41407      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41408      */
41409     maxHeight: 300,
41410     /**
41411      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41412      * query specified by the allQuery config option (defaults to 'query')
41413      */
41414     triggerAction: 'query',
41415     /**
41416      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41417      * (defaults to 4, does not apply if editable = false)
41418      */
41419     minChars : 4,
41420     /**
41421      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41422      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41423      */
41424     typeAhead: false,
41425     /**
41426      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41427      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41428      */
41429     queryDelay: 500,
41430     /**
41431      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41432      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41433      */
41434     pageSize: 0,
41435     /**
41436      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41437      * when editable = true (defaults to false)
41438      */
41439     selectOnFocus:false,
41440     /**
41441      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41442      */
41443     queryParam: 'query',
41444     /**
41445      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41446      * when mode = 'remote' (defaults to 'Loading...')
41447      */
41448     loadingText: 'Loading...',
41449     /**
41450      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41451      */
41452     resizable: false,
41453     /**
41454      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41455      */
41456     handleHeight : 8,
41457     /**
41458      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41459      * traditional select (defaults to true)
41460      */
41461     editable: true,
41462     /**
41463      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41464      */
41465     allQuery: '',
41466     /**
41467      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41468      */
41469     mode: 'remote',
41470     /**
41471      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41472      * listWidth has a higher value)
41473      */
41474     minListWidth : 70,
41475     /**
41476      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41477      * allow the user to set arbitrary text into the field (defaults to false)
41478      */
41479     forceSelection:false,
41480     /**
41481      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41482      * if typeAhead = true (defaults to 250)
41483      */
41484     typeAheadDelay : 250,
41485     /**
41486      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41487      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41488      */
41489     valueNotFoundText : undefined,
41490     /**
41491      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41492      */
41493     blockFocus : false,
41494     
41495     /**
41496      * @cfg {Boolean} disableClear Disable showing of clear button.
41497      */
41498     disableClear : false,
41499     /**
41500      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41501      */
41502     alwaysQuery : false,
41503     
41504     //private
41505     addicon : false,
41506     editicon: false,
41507     
41508     // element that contains real text value.. (when hidden is used..)
41509      
41510     // private
41511     onRender : function(ct, position)
41512     {
41513         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41514         
41515         if(this.hiddenName){
41516             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41517                     'before', true);
41518             this.hiddenField.value =
41519                 this.hiddenValue !== undefined ? this.hiddenValue :
41520                 this.value !== undefined ? this.value : '';
41521
41522             // prevent input submission
41523             this.el.dom.removeAttribute('name');
41524              
41525              
41526         }
41527         
41528         if(Roo.isGecko){
41529             this.el.dom.setAttribute('autocomplete', 'off');
41530         }
41531
41532         var cls = 'x-combo-list';
41533
41534         this.list = new Roo.Layer({
41535             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41536         });
41537
41538         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41539         this.list.setWidth(lw);
41540         this.list.swallowEvent('mousewheel');
41541         this.assetHeight = 0;
41542
41543         if(this.title){
41544             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41545             this.assetHeight += this.header.getHeight();
41546         }
41547
41548         this.innerList = this.list.createChild({cls:cls+'-inner'});
41549         this.innerList.on('mouseover', this.onViewOver, this);
41550         this.innerList.on('mousemove', this.onViewMove, this);
41551         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41552         
41553         if(this.allowBlank && !this.pageSize && !this.disableClear){
41554             this.footer = this.list.createChild({cls:cls+'-ft'});
41555             this.pageTb = new Roo.Toolbar(this.footer);
41556            
41557         }
41558         if(this.pageSize){
41559             this.footer = this.list.createChild({cls:cls+'-ft'});
41560             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41561                     {pageSize: this.pageSize});
41562             
41563         }
41564         
41565         if (this.pageTb && this.allowBlank && !this.disableClear) {
41566             var _this = this;
41567             this.pageTb.add(new Roo.Toolbar.Fill(), {
41568                 cls: 'x-btn-icon x-btn-clear',
41569                 text: '&#160;',
41570                 handler: function()
41571                 {
41572                     _this.collapse();
41573                     _this.clearValue();
41574                     _this.onSelect(false, -1);
41575                 }
41576             });
41577         }
41578         if (this.footer) {
41579             this.assetHeight += this.footer.getHeight();
41580         }
41581         
41582
41583         if(!this.tpl){
41584             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41585         }
41586
41587         this.view = new Roo.View(this.innerList, this.tpl, {
41588             singleSelect:true,
41589             store: this.store,
41590             selectedClass: this.selectedClass
41591         });
41592
41593         this.view.on('click', this.onViewClick, this);
41594
41595         this.store.on('beforeload', this.onBeforeLoad, this);
41596         this.store.on('load', this.onLoad, this);
41597         this.store.on('loadexception', this.onLoadException, this);
41598
41599         if(this.resizable){
41600             this.resizer = new Roo.Resizable(this.list,  {
41601                pinned:true, handles:'se'
41602             });
41603             this.resizer.on('resize', function(r, w, h){
41604                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41605                 this.listWidth = w;
41606                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41607                 this.restrictHeight();
41608             }, this);
41609             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41610         }
41611         if(!this.editable){
41612             this.editable = true;
41613             this.setEditable(false);
41614         }  
41615         
41616         
41617         if (typeof(this.events.add.listeners) != 'undefined') {
41618             
41619             this.addicon = this.wrap.createChild(
41620                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41621        
41622             this.addicon.on('click', function(e) {
41623                 this.fireEvent('add', this);
41624             }, this);
41625         }
41626         if (typeof(this.events.edit.listeners) != 'undefined') {
41627             
41628             this.editicon = this.wrap.createChild(
41629                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41630             if (this.addicon) {
41631                 this.editicon.setStyle('margin-left', '40px');
41632             }
41633             this.editicon.on('click', function(e) {
41634                 
41635                 // we fire even  if inothing is selected..
41636                 this.fireEvent('edit', this, this.lastData );
41637                 
41638             }, this);
41639         }
41640         
41641         
41642         
41643     },
41644
41645     // private
41646     initEvents : function(){
41647         Roo.form.ComboBox.superclass.initEvents.call(this);
41648
41649         this.keyNav = new Roo.KeyNav(this.el, {
41650             "up" : function(e){
41651                 this.inKeyMode = true;
41652                 this.selectPrev();
41653             },
41654
41655             "down" : function(e){
41656                 if(!this.isExpanded()){
41657                     this.onTriggerClick();
41658                 }else{
41659                     this.inKeyMode = true;
41660                     this.selectNext();
41661                 }
41662             },
41663
41664             "enter" : function(e){
41665                 this.onViewClick();
41666                 //return true;
41667             },
41668
41669             "esc" : function(e){
41670                 this.collapse();
41671             },
41672
41673             "tab" : function(e){
41674                 this.onViewClick(false);
41675                 this.fireEvent("specialkey", this, e);
41676                 return true;
41677             },
41678
41679             scope : this,
41680
41681             doRelay : function(foo, bar, hname){
41682                 if(hname == 'down' || this.scope.isExpanded()){
41683                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41684                 }
41685                 return true;
41686             },
41687
41688             forceKeyDown: true
41689         });
41690         this.queryDelay = Math.max(this.queryDelay || 10,
41691                 this.mode == 'local' ? 10 : 250);
41692         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41693         if(this.typeAhead){
41694             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41695         }
41696         if(this.editable !== false){
41697             this.el.on("keyup", this.onKeyUp, this);
41698         }
41699         if(this.forceSelection){
41700             this.on('blur', this.doForce, this);
41701         }
41702     },
41703
41704     onDestroy : function(){
41705         if(this.view){
41706             this.view.setStore(null);
41707             this.view.el.removeAllListeners();
41708             this.view.el.remove();
41709             this.view.purgeListeners();
41710         }
41711         if(this.list){
41712             this.list.destroy();
41713         }
41714         if(this.store){
41715             this.store.un('beforeload', this.onBeforeLoad, this);
41716             this.store.un('load', this.onLoad, this);
41717             this.store.un('loadexception', this.onLoadException, this);
41718         }
41719         Roo.form.ComboBox.superclass.onDestroy.call(this);
41720     },
41721
41722     // private
41723     fireKey : function(e){
41724         if(e.isNavKeyPress() && !this.list.isVisible()){
41725             this.fireEvent("specialkey", this, e);
41726         }
41727     },
41728
41729     // private
41730     onResize: function(w, h){
41731         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41732         
41733         if(typeof w != 'number'){
41734             // we do not handle it!?!?
41735             return;
41736         }
41737         var tw = this.trigger.getWidth();
41738         tw += this.addicon ? this.addicon.getWidth() : 0;
41739         tw += this.editicon ? this.editicon.getWidth() : 0;
41740         var x = w - tw;
41741         this.el.setWidth( this.adjustWidth('input', x));
41742             
41743         this.trigger.setStyle('left', x+'px');
41744         
41745         if(this.list && this.listWidth === undefined){
41746             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41747             this.list.setWidth(lw);
41748             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41749         }
41750         
41751     
41752         
41753     },
41754
41755     /**
41756      * Allow or prevent the user from directly editing the field text.  If false is passed,
41757      * the user will only be able to select from the items defined in the dropdown list.  This method
41758      * is the runtime equivalent of setting the 'editable' config option at config time.
41759      * @param {Boolean} value True to allow the user to directly edit the field text
41760      */
41761     setEditable : function(value){
41762         if(value == this.editable){
41763             return;
41764         }
41765         this.editable = value;
41766         if(!value){
41767             this.el.dom.setAttribute('readOnly', true);
41768             this.el.on('mousedown', this.onTriggerClick,  this);
41769             this.el.addClass('x-combo-noedit');
41770         }else{
41771             this.el.dom.setAttribute('readOnly', false);
41772             this.el.un('mousedown', this.onTriggerClick,  this);
41773             this.el.removeClass('x-combo-noedit');
41774         }
41775     },
41776
41777     // private
41778     onBeforeLoad : function(){
41779         if(!this.hasFocus){
41780             return;
41781         }
41782         this.innerList.update(this.loadingText ?
41783                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41784         this.restrictHeight();
41785         this.selectedIndex = -1;
41786     },
41787
41788     // private
41789     onLoad : function(){
41790         if(!this.hasFocus){
41791             return;
41792         }
41793         if(this.store.getCount() > 0){
41794             this.expand();
41795             this.restrictHeight();
41796             if(this.lastQuery == this.allQuery){
41797                 if(this.editable){
41798                     this.el.dom.select();
41799                 }
41800                 if(!this.selectByValue(this.value, true)){
41801                     this.select(0, true);
41802                 }
41803             }else{
41804                 this.selectNext();
41805                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41806                     this.taTask.delay(this.typeAheadDelay);
41807                 }
41808             }
41809         }else{
41810             this.onEmptyResults();
41811         }
41812         //this.el.focus();
41813     },
41814     // private
41815     onLoadException : function()
41816     {
41817         this.collapse();
41818         Roo.log(this.store.reader.jsonData);
41819         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41820             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41821         }
41822         
41823         
41824     },
41825     // private
41826     onTypeAhead : function(){
41827         if(this.store.getCount() > 0){
41828             var r = this.store.getAt(0);
41829             var newValue = r.data[this.displayField];
41830             var len = newValue.length;
41831             var selStart = this.getRawValue().length;
41832             if(selStart != len){
41833                 this.setRawValue(newValue);
41834                 this.selectText(selStart, newValue.length);
41835             }
41836         }
41837     },
41838
41839     // private
41840     onSelect : function(record, index){
41841         if(this.fireEvent('beforeselect', this, record, index) !== false){
41842             this.setFromData(index > -1 ? record.data : false);
41843             this.collapse();
41844             this.fireEvent('select', this, record, index);
41845         }
41846     },
41847
41848     /**
41849      * Returns the currently selected field value or empty string if no value is set.
41850      * @return {String} value The selected value
41851      */
41852     getValue : function(){
41853         if(this.valueField){
41854             return typeof this.value != 'undefined' ? this.value : '';
41855         }
41856         return Roo.form.ComboBox.superclass.getValue.call(this);
41857     },
41858
41859     /**
41860      * Clears any text/value currently set in the field
41861      */
41862     clearValue : function(){
41863         if(this.hiddenField){
41864             this.hiddenField.value = '';
41865         }
41866         this.value = '';
41867         this.setRawValue('');
41868         this.lastSelectionText = '';
41869         
41870     },
41871
41872     /**
41873      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41874      * will be displayed in the field.  If the value does not match the data value of an existing item,
41875      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41876      * Otherwise the field will be blank (although the value will still be set).
41877      * @param {String} value The value to match
41878      */
41879     setValue : function(v){
41880         var text = v;
41881         if(this.valueField){
41882             var r = this.findRecord(this.valueField, v);
41883             if(r){
41884                 text = r.data[this.displayField];
41885             }else if(this.valueNotFoundText !== undefined){
41886                 text = this.valueNotFoundText;
41887             }
41888         }
41889         this.lastSelectionText = text;
41890         if(this.hiddenField){
41891             this.hiddenField.value = v;
41892         }
41893         Roo.form.ComboBox.superclass.setValue.call(this, text);
41894         this.value = v;
41895     },
41896     /**
41897      * @property {Object} the last set data for the element
41898      */
41899     
41900     lastData : false,
41901     /**
41902      * Sets the value of the field based on a object which is related to the record format for the store.
41903      * @param {Object} value the value to set as. or false on reset?
41904      */
41905     setFromData : function(o){
41906         var dv = ''; // display value
41907         var vv = ''; // value value..
41908         this.lastData = o;
41909         if (this.displayField) {
41910             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41911         } else {
41912             // this is an error condition!!!
41913             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41914         }
41915         
41916         if(this.valueField){
41917             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41918         }
41919         if(this.hiddenField){
41920             this.hiddenField.value = vv;
41921             
41922             this.lastSelectionText = dv;
41923             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41924             this.value = vv;
41925             return;
41926         }
41927         // no hidden field.. - we store the value in 'value', but still display
41928         // display field!!!!
41929         this.lastSelectionText = dv;
41930         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41931         this.value = vv;
41932         
41933         
41934     },
41935     // private
41936     reset : function(){
41937         // overridden so that last data is reset..
41938         this.setValue(this.resetValue);
41939         this.originalValue = this.getValue();
41940         this.clearInvalid();
41941         this.lastData = false;
41942         if (this.view) {
41943             this.view.clearSelections();
41944         }
41945     },
41946     // private
41947     findRecord : function(prop, value){
41948         var record;
41949         if(this.store.getCount() > 0){
41950             this.store.each(function(r){
41951                 if(r.data[prop] == value){
41952                     record = r;
41953                     return false;
41954                 }
41955                 return true;
41956             });
41957         }
41958         return record;
41959     },
41960     
41961     getName: function()
41962     {
41963         // returns hidden if it's set..
41964         if (!this.rendered) {return ''};
41965         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41966         
41967     },
41968     // private
41969     onViewMove : function(e, t){
41970         this.inKeyMode = false;
41971     },
41972
41973     // private
41974     onViewOver : function(e, t){
41975         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41976             return;
41977         }
41978         var item = this.view.findItemFromChild(t);
41979         if(item){
41980             var index = this.view.indexOf(item);
41981             this.select(index, false);
41982         }
41983     },
41984
41985     // private
41986     onViewClick : function(doFocus)
41987     {
41988         var index = this.view.getSelectedIndexes()[0];
41989         var r = this.store.getAt(index);
41990         if(r){
41991             this.onSelect(r, index);
41992         }
41993         if(doFocus !== false && !this.blockFocus){
41994             this.el.focus();
41995         }
41996     },
41997
41998     // private
41999     restrictHeight : function(){
42000         this.innerList.dom.style.height = '';
42001         var inner = this.innerList.dom;
42002         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42003         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42004         this.list.beginUpdate();
42005         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42006         this.list.alignTo(this.el, this.listAlign);
42007         this.list.endUpdate();
42008     },
42009
42010     // private
42011     onEmptyResults : function(){
42012         this.collapse();
42013     },
42014
42015     /**
42016      * Returns true if the dropdown list is expanded, else false.
42017      */
42018     isExpanded : function(){
42019         return this.list.isVisible();
42020     },
42021
42022     /**
42023      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42024      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42025      * @param {String} value The data value of the item to select
42026      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42027      * selected item if it is not currently in view (defaults to true)
42028      * @return {Boolean} True if the value matched an item in the list, else false
42029      */
42030     selectByValue : function(v, scrollIntoView){
42031         if(v !== undefined && v !== null){
42032             var r = this.findRecord(this.valueField || this.displayField, v);
42033             if(r){
42034                 this.select(this.store.indexOf(r), scrollIntoView);
42035                 return true;
42036             }
42037         }
42038         return false;
42039     },
42040
42041     /**
42042      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42043      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42044      * @param {Number} index The zero-based index of the list item to select
42045      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42046      * selected item if it is not currently in view (defaults to true)
42047      */
42048     select : function(index, scrollIntoView){
42049         this.selectedIndex = index;
42050         this.view.select(index);
42051         if(scrollIntoView !== false){
42052             var el = this.view.getNode(index);
42053             if(el){
42054                 this.innerList.scrollChildIntoView(el, false);
42055             }
42056         }
42057     },
42058
42059     // private
42060     selectNext : function(){
42061         var ct = this.store.getCount();
42062         if(ct > 0){
42063             if(this.selectedIndex == -1){
42064                 this.select(0);
42065             }else if(this.selectedIndex < ct-1){
42066                 this.select(this.selectedIndex+1);
42067             }
42068         }
42069     },
42070
42071     // private
42072     selectPrev : function(){
42073         var ct = this.store.getCount();
42074         if(ct > 0){
42075             if(this.selectedIndex == -1){
42076                 this.select(0);
42077             }else if(this.selectedIndex != 0){
42078                 this.select(this.selectedIndex-1);
42079             }
42080         }
42081     },
42082
42083     // private
42084     onKeyUp : function(e){
42085         if(this.editable !== false && !e.isSpecialKey()){
42086             this.lastKey = e.getKey();
42087             this.dqTask.delay(this.queryDelay);
42088         }
42089     },
42090
42091     // private
42092     validateBlur : function(){
42093         return !this.list || !this.list.isVisible();   
42094     },
42095
42096     // private
42097     initQuery : function(){
42098         this.doQuery(this.getRawValue());
42099     },
42100
42101     // private
42102     doForce : function(){
42103         if(this.el.dom.value.length > 0){
42104             this.el.dom.value =
42105                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42106              
42107         }
42108     },
42109
42110     /**
42111      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42112      * query allowing the query action to be canceled if needed.
42113      * @param {String} query The SQL query to execute
42114      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42115      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42116      * saved in the current store (defaults to false)
42117      */
42118     doQuery : function(q, forceAll){
42119         if(q === undefined || q === null){
42120             q = '';
42121         }
42122         var qe = {
42123             query: q,
42124             forceAll: forceAll,
42125             combo: this,
42126             cancel:false
42127         };
42128         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42129             return false;
42130         }
42131         q = qe.query;
42132         forceAll = qe.forceAll;
42133         if(forceAll === true || (q.length >= this.minChars)){
42134             if(this.lastQuery != q || this.alwaysQuery){
42135                 this.lastQuery = q;
42136                 if(this.mode == 'local'){
42137                     this.selectedIndex = -1;
42138                     if(forceAll){
42139                         this.store.clearFilter();
42140                     }else{
42141                         this.store.filter(this.displayField, q);
42142                     }
42143                     this.onLoad();
42144                 }else{
42145                     this.store.baseParams[this.queryParam] = q;
42146                     this.store.load({
42147                         params: this.getParams(q)
42148                     });
42149                     this.expand();
42150                 }
42151             }else{
42152                 this.selectedIndex = -1;
42153                 this.onLoad();   
42154             }
42155         }
42156     },
42157
42158     // private
42159     getParams : function(q){
42160         var p = {};
42161         //p[this.queryParam] = q;
42162         if(this.pageSize){
42163             p.start = 0;
42164             p.limit = this.pageSize;
42165         }
42166         return p;
42167     },
42168
42169     /**
42170      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42171      */
42172     collapse : function(){
42173         if(!this.isExpanded()){
42174             return;
42175         }
42176         this.list.hide();
42177         Roo.get(document).un('mousedown', this.collapseIf, this);
42178         Roo.get(document).un('mousewheel', this.collapseIf, this);
42179         if (!this.editable) {
42180             Roo.get(document).un('keydown', this.listKeyPress, this);
42181         }
42182         this.fireEvent('collapse', this);
42183     },
42184
42185     // private
42186     collapseIf : function(e){
42187         if(!e.within(this.wrap) && !e.within(this.list)){
42188             this.collapse();
42189         }
42190     },
42191
42192     /**
42193      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42194      */
42195     expand : function(){
42196         if(this.isExpanded() || !this.hasFocus){
42197             return;
42198         }
42199         this.list.alignTo(this.el, this.listAlign);
42200         this.list.show();
42201         Roo.get(document).on('mousedown', this.collapseIf, this);
42202         Roo.get(document).on('mousewheel', this.collapseIf, this);
42203         if (!this.editable) {
42204             Roo.get(document).on('keydown', this.listKeyPress, this);
42205         }
42206         
42207         this.fireEvent('expand', this);
42208     },
42209
42210     // private
42211     // Implements the default empty TriggerField.onTriggerClick function
42212     onTriggerClick : function(){
42213         if(this.disabled){
42214             return;
42215         }
42216         if(this.isExpanded()){
42217             this.collapse();
42218             if (!this.blockFocus) {
42219                 this.el.focus();
42220             }
42221             
42222         }else {
42223             this.hasFocus = true;
42224             if(this.triggerAction == 'all') {
42225                 this.doQuery(this.allQuery, true);
42226             } else {
42227                 this.doQuery(this.getRawValue());
42228             }
42229             if (!this.blockFocus) {
42230                 this.el.focus();
42231             }
42232         }
42233     },
42234     listKeyPress : function(e)
42235     {
42236         //Roo.log('listkeypress');
42237         // scroll to first matching element based on key pres..
42238         if (e.isSpecialKey()) {
42239             return false;
42240         }
42241         var k = String.fromCharCode(e.getKey()).toUpperCase();
42242         //Roo.log(k);
42243         var match  = false;
42244         var csel = this.view.getSelectedNodes();
42245         var cselitem = false;
42246         if (csel.length) {
42247             var ix = this.view.indexOf(csel[0]);
42248             cselitem  = this.store.getAt(ix);
42249             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42250                 cselitem = false;
42251             }
42252             
42253         }
42254         
42255         this.store.each(function(v) { 
42256             if (cselitem) {
42257                 // start at existing selection.
42258                 if (cselitem.id == v.id) {
42259                     cselitem = false;
42260                 }
42261                 return;
42262             }
42263                 
42264             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42265                 match = this.store.indexOf(v);
42266                 return false;
42267             }
42268         }, this);
42269         
42270         if (match === false) {
42271             return true; // no more action?
42272         }
42273         // scroll to?
42274         this.view.select(match);
42275         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42276         sn.scrollIntoView(sn.dom.parentNode, false);
42277     } 
42278
42279     /** 
42280     * @cfg {Boolean} grow 
42281     * @hide 
42282     */
42283     /** 
42284     * @cfg {Number} growMin 
42285     * @hide 
42286     */
42287     /** 
42288     * @cfg {Number} growMax 
42289     * @hide 
42290     */
42291     /**
42292      * @hide
42293      * @method autoSize
42294      */
42295 });/*
42296  * Copyright(c) 2010-2012, Roo J Solutions Limited
42297  *
42298  * Licence LGPL
42299  *
42300  */
42301
42302 /**
42303  * @class Roo.form.ComboBoxArray
42304  * @extends Roo.form.TextField
42305  * A facebook style adder... for lists of email / people / countries  etc...
42306  * pick multiple items from a combo box, and shows each one.
42307  *
42308  *  Fred [x]  Brian [x]  [Pick another |v]
42309  *
42310  *
42311  *  For this to work: it needs various extra information
42312  *    - normal combo problay has
42313  *      name, hiddenName
42314  *    + displayField, valueField
42315  *
42316  *    For our purpose...
42317  *
42318  *
42319  *   If we change from 'extends' to wrapping...
42320  *   
42321  *  
42322  *
42323  
42324  
42325  * @constructor
42326  * Create a new ComboBoxArray.
42327  * @param {Object} config Configuration options
42328  */
42329  
42330
42331 Roo.form.ComboBoxArray = function(config)
42332 {
42333     this.addEvents({
42334         /**
42335          * @event beforeremove
42336          * Fires before remove the value from the list
42337              * @param {Roo.form.ComboBoxArray} _self This combo box array
42338              * @param {Roo.form.ComboBoxArray.Item} item removed item
42339              */
42340         'beforeremove' : true,
42341         /**
42342          * @event remove
42343          * Fires when remove the value from the list
42344              * @param {Roo.form.ComboBoxArray} _self This combo box array
42345              * @param {Roo.form.ComboBoxArray.Item} item removed item
42346              */
42347         'remove' : true
42348         
42349         
42350     });
42351     
42352     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42353     
42354     this.items = new Roo.util.MixedCollection(false);
42355     
42356     // construct the child combo...
42357     
42358     
42359     
42360     
42361    
42362     
42363 }
42364
42365  
42366 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42367
42368     /**
42369      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42370      */
42371     
42372     lastData : false,
42373     
42374     // behavies liek a hiddne field
42375     inputType:      'hidden',
42376     /**
42377      * @cfg {Number} width The width of the box that displays the selected element
42378      */ 
42379     width:          300,
42380
42381     
42382     
42383     /**
42384      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42385      */
42386     name : false,
42387     /**
42388      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42389      */
42390     hiddenName : false,
42391       /**
42392      * @cfg {String} seperator    The value seperator normally ',' 
42393      */
42394     seperator : ',',
42395     
42396     // private the array of items that are displayed..
42397     items  : false,
42398     // private - the hidden field el.
42399     hiddenEl : false,
42400     // private - the filed el..
42401     el : false,
42402     
42403     //validateValue : function() { return true; }, // all values are ok!
42404     //onAddClick: function() { },
42405     
42406     onRender : function(ct, position) 
42407     {
42408         
42409         // create the standard hidden element
42410         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42411         
42412         
42413         // give fake names to child combo;
42414         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42415         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42416         
42417         this.combo = Roo.factory(this.combo, Roo.form);
42418         this.combo.onRender(ct, position);
42419         if (typeof(this.combo.width) != 'undefined') {
42420             this.combo.onResize(this.combo.width,0);
42421         }
42422         
42423         this.combo.initEvents();
42424         
42425         // assigned so form know we need to do this..
42426         this.store          = this.combo.store;
42427         this.valueField     = this.combo.valueField;
42428         this.displayField   = this.combo.displayField ;
42429         
42430         
42431         this.combo.wrap.addClass('x-cbarray-grp');
42432         
42433         var cbwrap = this.combo.wrap.createChild(
42434             {tag: 'div', cls: 'x-cbarray-cb'},
42435             this.combo.el.dom
42436         );
42437         
42438              
42439         this.hiddenEl = this.combo.wrap.createChild({
42440             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42441         });
42442         this.el = this.combo.wrap.createChild({
42443             tag: 'input',  type:'hidden' , name: this.name, value : ''
42444         });
42445          //   this.el.dom.removeAttribute("name");
42446         
42447         
42448         this.outerWrap = this.combo.wrap;
42449         this.wrap = cbwrap;
42450         
42451         this.outerWrap.setWidth(this.width);
42452         this.outerWrap.dom.removeChild(this.el.dom);
42453         
42454         this.wrap.dom.appendChild(this.el.dom);
42455         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42456         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42457         
42458         this.combo.trigger.setStyle('position','relative');
42459         this.combo.trigger.setStyle('left', '0px');
42460         this.combo.trigger.setStyle('top', '2px');
42461         
42462         this.combo.el.setStyle('vertical-align', 'text-bottom');
42463         
42464         //this.trigger.setStyle('vertical-align', 'top');
42465         
42466         // this should use the code from combo really... on('add' ....)
42467         if (this.adder) {
42468             
42469         
42470             this.adder = this.outerWrap.createChild(
42471                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42472             var _t = this;
42473             this.adder.on('click', function(e) {
42474                 _t.fireEvent('adderclick', this, e);
42475             }, _t);
42476         }
42477         //var _t = this;
42478         //this.adder.on('click', this.onAddClick, _t);
42479         
42480         
42481         this.combo.on('select', function(cb, rec, ix) {
42482             this.addItem(rec.data);
42483             
42484             cb.setValue('');
42485             cb.el.dom.value = '';
42486             //cb.lastData = rec.data;
42487             // add to list
42488             
42489         }, this);
42490         
42491         
42492     },
42493     
42494     
42495     getName: function()
42496     {
42497         // returns hidden if it's set..
42498         if (!this.rendered) {return ''};
42499         return  this.hiddenName ? this.hiddenName : this.name;
42500         
42501     },
42502     
42503     
42504     onResize: function(w, h){
42505         
42506         return;
42507         // not sure if this is needed..
42508         //this.combo.onResize(w,h);
42509         
42510         if(typeof w != 'number'){
42511             // we do not handle it!?!?
42512             return;
42513         }
42514         var tw = this.combo.trigger.getWidth();
42515         tw += this.addicon ? this.addicon.getWidth() : 0;
42516         tw += this.editicon ? this.editicon.getWidth() : 0;
42517         var x = w - tw;
42518         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42519             
42520         this.combo.trigger.setStyle('left', '0px');
42521         
42522         if(this.list && this.listWidth === undefined){
42523             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42524             this.list.setWidth(lw);
42525             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42526         }
42527         
42528     
42529         
42530     },
42531     
42532     addItem: function(rec)
42533     {
42534         var valueField = this.combo.valueField;
42535         var displayField = this.combo.displayField;
42536         
42537         if (this.items.indexOfKey(rec[valueField]) > -1) {
42538             //console.log("GOT " + rec.data.id);
42539             return;
42540         }
42541         
42542         var x = new Roo.form.ComboBoxArray.Item({
42543             //id : rec[this.idField],
42544             data : rec,
42545             displayField : displayField ,
42546             tipField : displayField ,
42547             cb : this
42548         });
42549         // use the 
42550         this.items.add(rec[valueField],x);
42551         // add it before the element..
42552         this.updateHiddenEl();
42553         x.render(this.outerWrap, this.wrap.dom);
42554         // add the image handler..
42555     },
42556     
42557     updateHiddenEl : function()
42558     {
42559         this.validate();
42560         if (!this.hiddenEl) {
42561             return;
42562         }
42563         var ar = [];
42564         var idField = this.combo.valueField;
42565         
42566         this.items.each(function(f) {
42567             ar.push(f.data[idField]);
42568         });
42569         this.hiddenEl.dom.value = ar.join(this.seperator);
42570         this.validate();
42571     },
42572     
42573     reset : function()
42574     {
42575         this.items.clear();
42576         
42577         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42578            el.remove();
42579         });
42580         
42581         this.el.dom.value = '';
42582         if (this.hiddenEl) {
42583             this.hiddenEl.dom.value = '';
42584         }
42585         
42586     },
42587     getValue: function()
42588     {
42589         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42590     },
42591     setValue: function(v) // not a valid action - must use addItems..
42592     {
42593         
42594         this.reset();
42595          
42596         if (this.store.isLocal && (typeof(v) == 'string')) {
42597             // then we can use the store to find the values..
42598             // comma seperated at present.. this needs to allow JSON based encoding..
42599             this.hiddenEl.value  = v;
42600             var v_ar = [];
42601             Roo.each(v.split(this.seperator), function(k) {
42602                 Roo.log("CHECK " + this.valueField + ',' + k);
42603                 var li = this.store.query(this.valueField, k);
42604                 if (!li.length) {
42605                     return;
42606                 }
42607                 var add = {};
42608                 add[this.valueField] = k;
42609                 add[this.displayField] = li.item(0).data[this.displayField];
42610                 
42611                 this.addItem(add);
42612             }, this) 
42613              
42614         }
42615         if (typeof(v) == 'object' ) {
42616             // then let's assume it's an array of objects..
42617             Roo.each(v, function(l) {
42618                 var add = l;
42619                 if (typeof(l) == 'string') {
42620                     add = {};
42621                     add[this.valueField] = l;
42622                     add[this.displayField] = l
42623                 }
42624                 this.addItem(add);
42625             }, this);
42626              
42627         }
42628         
42629         
42630     },
42631     setFromData: function(v)
42632     {
42633         // this recieves an object, if setValues is called.
42634         this.reset();
42635         this.el.dom.value = v[this.displayField];
42636         this.hiddenEl.dom.value = v[this.valueField];
42637         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42638             return;
42639         }
42640         var kv = v[this.valueField];
42641         var dv = v[this.displayField];
42642         kv = typeof(kv) != 'string' ? '' : kv;
42643         dv = typeof(dv) != 'string' ? '' : dv;
42644         
42645         
42646         var keys = kv.split(this.seperator);
42647         var display = dv.split(this.seperator);
42648         for (var i = 0 ; i < keys.length; i++) {
42649             add = {};
42650             add[this.valueField] = keys[i];
42651             add[this.displayField] = display[i];
42652             this.addItem(add);
42653         }
42654       
42655         
42656     },
42657     
42658     /**
42659      * Validates the combox array value
42660      * @return {Boolean} True if the value is valid, else false
42661      */
42662     validate : function(){
42663         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42664             this.clearInvalid();
42665             return true;
42666         }
42667         return false;
42668     },
42669     
42670     validateValue : function(value){
42671         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42672         
42673     },
42674     
42675     /*@
42676      * overide
42677      * 
42678      */
42679     isDirty : function() {
42680         if(this.disabled) {
42681             return false;
42682         }
42683         
42684         try {
42685             var d = Roo.decode(String(this.originalValue));
42686         } catch (e) {
42687             return String(this.getValue()) !== String(this.originalValue);
42688         }
42689         
42690         var originalValue = [];
42691         
42692         for (var i = 0; i < d.length; i++){
42693             originalValue.push(d[i][this.valueField]);
42694         }
42695         
42696         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42697         
42698     }
42699     
42700 });
42701
42702
42703
42704 /**
42705  * @class Roo.form.ComboBoxArray.Item
42706  * @extends Roo.BoxComponent
42707  * A selected item in the list
42708  *  Fred [x]  Brian [x]  [Pick another |v]
42709  * 
42710  * @constructor
42711  * Create a new item.
42712  * @param {Object} config Configuration options
42713  */
42714  
42715 Roo.form.ComboBoxArray.Item = function(config) {
42716     config.id = Roo.id();
42717     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42718 }
42719
42720 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42721     data : {},
42722     cb: false,
42723     displayField : false,
42724     tipField : false,
42725     
42726     
42727     defaultAutoCreate : {
42728         tag: 'div',
42729         cls: 'x-cbarray-item',
42730         cn : [ 
42731             { tag: 'div' },
42732             {
42733                 tag: 'img',
42734                 width:16,
42735                 height : 16,
42736                 src : Roo.BLANK_IMAGE_URL ,
42737                 align: 'center'
42738             }
42739         ]
42740         
42741     },
42742     
42743  
42744     onRender : function(ct, position)
42745     {
42746         Roo.form.Field.superclass.onRender.call(this, ct, position);
42747         
42748         if(!this.el){
42749             var cfg = this.getAutoCreate();
42750             this.el = ct.createChild(cfg, position);
42751         }
42752         
42753         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42754         
42755         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42756             this.cb.renderer(this.data) :
42757             String.format('{0}',this.data[this.displayField]);
42758         
42759             
42760         this.el.child('div').dom.setAttribute('qtip',
42761                         String.format('{0}',this.data[this.tipField])
42762         );
42763         
42764         this.el.child('img').on('click', this.remove, this);
42765         
42766     },
42767    
42768     remove : function()
42769     {
42770         if(this.cb.disabled){
42771             return;
42772         }
42773         
42774         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42775             this.cb.items.remove(this);
42776             this.el.child('img').un('click', this.remove, this);
42777             this.el.remove();
42778             this.cb.updateHiddenEl();
42779
42780             this.cb.fireEvent('remove', this.cb, this);
42781         }
42782         
42783     }
42784 });/*
42785  * RooJS Library 1.1.1
42786  * Copyright(c) 2008-2011  Alan Knowles
42787  *
42788  * License - LGPL
42789  */
42790  
42791
42792 /**
42793  * @class Roo.form.ComboNested
42794  * @extends Roo.form.ComboBox
42795  * A combobox for that allows selection of nested items in a list,
42796  * eg.
42797  *
42798  *  Book
42799  *    -> red
42800  *    -> green
42801  *  Table
42802  *    -> square
42803  *      ->red
42804  *      ->green
42805  *    -> rectangle
42806  *      ->green
42807  *      
42808  * 
42809  * @constructor
42810  * Create a new ComboNested
42811  * @param {Object} config Configuration options
42812  */
42813 Roo.form.ComboNested = function(config){
42814     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42815     // should verify some data...
42816     // like
42817     // hiddenName = required..
42818     // displayField = required
42819     // valudField == required
42820     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42821     var _t = this;
42822     Roo.each(req, function(e) {
42823         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42824             throw "Roo.form.ComboNested : missing value for: " + e;
42825         }
42826     });
42827      
42828     
42829 };
42830
42831 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42832    
42833     /*
42834      * @config {Number} max Number of columns to show
42835      */
42836     
42837     maxColumns : 3,
42838    
42839     list : null, // the outermost div..
42840     innerLists : null, // the
42841     views : null,
42842     stores : null,
42843     // private
42844     loadingChildren : false,
42845     
42846     onRender : function(ct, position)
42847     {
42848         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42849         
42850         if(this.hiddenName){
42851             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42852                     'before', true);
42853             this.hiddenField.value =
42854                 this.hiddenValue !== undefined ? this.hiddenValue :
42855                 this.value !== undefined ? this.value : '';
42856
42857             // prevent input submission
42858             this.el.dom.removeAttribute('name');
42859              
42860              
42861         }
42862         
42863         if(Roo.isGecko){
42864             this.el.dom.setAttribute('autocomplete', 'off');
42865         }
42866
42867         var cls = 'x-combo-list';
42868
42869         this.list = new Roo.Layer({
42870             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42871         });
42872
42873         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42874         this.list.setWidth(lw);
42875         this.list.swallowEvent('mousewheel');
42876         this.assetHeight = 0;
42877
42878         if(this.title){
42879             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42880             this.assetHeight += this.header.getHeight();
42881         }
42882         this.innerLists = [];
42883         this.views = [];
42884         this.stores = [];
42885         for (var i =0 ; i < this.maxColumns; i++) {
42886             this.onRenderList( cls, i);
42887         }
42888         
42889         // always needs footer, as we are going to have an 'OK' button.
42890         this.footer = this.list.createChild({cls:cls+'-ft'});
42891         this.pageTb = new Roo.Toolbar(this.footer);  
42892         var _this = this;
42893         this.pageTb.add(  {
42894             
42895             text: 'Done',
42896             handler: function()
42897             {
42898                 _this.collapse();
42899             }
42900         });
42901         
42902         if ( this.allowBlank && !this.disableClear) {
42903             
42904             this.pageTb.add(new Roo.Toolbar.Fill(), {
42905                 cls: 'x-btn-icon x-btn-clear',
42906                 text: '&#160;',
42907                 handler: function()
42908                 {
42909                     _this.collapse();
42910                     _this.clearValue();
42911                     _this.onSelect(false, -1);
42912                 }
42913             });
42914         }
42915         if (this.footer) {
42916             this.assetHeight += this.footer.getHeight();
42917         }
42918         
42919     },
42920     onRenderList : function (  cls, i)
42921     {
42922         
42923         var lw = Math.floor(
42924                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42925         );
42926         
42927         this.list.setWidth(lw); // default to '1'
42928
42929         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42930         //il.on('mouseover', this.onViewOver, this, { list:  i });
42931         //il.on('mousemove', this.onViewMove, this, { list:  i });
42932         il.setWidth(lw);
42933         il.setStyle({ 'overflow-x' : 'hidden'});
42934
42935         if(!this.tpl){
42936             this.tpl = new Roo.Template({
42937                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42938                 isEmpty: function (value, allValues) {
42939                     //Roo.log(value);
42940                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42941                     return dl ? 'has-children' : 'no-children'
42942                 }
42943             });
42944         }
42945         
42946         var store  = this.store;
42947         if (i > 0) {
42948             store  = new Roo.data.SimpleStore({
42949                 //fields : this.store.reader.meta.fields,
42950                 reader : this.store.reader,
42951                 data : [ ]
42952             });
42953         }
42954         this.stores[i]  = store;
42955                   
42956         var view = this.views[i] = new Roo.View(
42957             il,
42958             this.tpl,
42959             {
42960                 singleSelect:true,
42961                 store: store,
42962                 selectedClass: this.selectedClass
42963             }
42964         );
42965         view.getEl().setWidth(lw);
42966         view.getEl().setStyle({
42967             position: i < 1 ? 'relative' : 'absolute',
42968             top: 0,
42969             left: (i * lw ) + 'px',
42970             display : i > 0 ? 'none' : 'block'
42971         });
42972         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42973         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42974         //view.on('click', this.onViewClick, this, { list : i });
42975
42976         store.on('beforeload', this.onBeforeLoad, this);
42977         store.on('load',  this.onLoad, this, { list  : i});
42978         store.on('loadexception', this.onLoadException, this);
42979
42980         // hide the other vies..
42981         
42982         
42983         
42984     },
42985       
42986     restrictHeight : function()
42987     {
42988         var mh = 0;
42989         Roo.each(this.innerLists, function(il,i) {
42990             var el = this.views[i].getEl();
42991             el.dom.style.height = '';
42992             var inner = el.dom;
42993             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42994             // only adjust heights on other ones..
42995             mh = Math.max(h, mh);
42996             if (i < 1) {
42997                 
42998                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42999                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43000                
43001             }
43002             
43003             
43004         }, this);
43005         
43006         this.list.beginUpdate();
43007         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43008         this.list.alignTo(this.el, this.listAlign);
43009         this.list.endUpdate();
43010         
43011     },
43012      
43013     
43014     // -- store handlers..
43015     // private
43016     onBeforeLoad : function()
43017     {
43018         if(!this.hasFocus){
43019             return;
43020         }
43021         this.innerLists[0].update(this.loadingText ?
43022                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43023         this.restrictHeight();
43024         this.selectedIndex = -1;
43025     },
43026     // private
43027     onLoad : function(a,b,c,d)
43028     {
43029         if (!this.loadingChildren) {
43030             // then we are loading the top level. - hide the children
43031             for (var i = 1;i < this.views.length; i++) {
43032                 this.views[i].getEl().setStyle({ display : 'none' });
43033             }
43034             var lw = Math.floor(
43035                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43036             );
43037         
43038              this.list.setWidth(lw); // default to '1'
43039
43040             
43041         }
43042         if(!this.hasFocus){
43043             return;
43044         }
43045         
43046         if(this.store.getCount() > 0) {
43047             this.expand();
43048             this.restrictHeight();   
43049         } else {
43050             this.onEmptyResults();
43051         }
43052         
43053         if (!this.loadingChildren) {
43054             this.selectActive();
43055         }
43056         /*
43057         this.stores[1].loadData([]);
43058         this.stores[2].loadData([]);
43059         this.views
43060         */    
43061     
43062         //this.el.focus();
43063     },
43064     
43065     
43066     // private
43067     onLoadException : function()
43068     {
43069         this.collapse();
43070         Roo.log(this.store.reader.jsonData);
43071         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43072             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43073         }
43074         
43075         
43076     },
43077     // no cleaning of leading spaces on blur here.
43078     cleanLeadingSpace : function(e) { },
43079     
43080
43081     onSelectChange : function (view, sels, opts )
43082     {
43083         var ix = view.getSelectedIndexes();
43084          
43085         if (opts.list > this.maxColumns - 2) {
43086             if (view.store.getCount()<  1) {
43087                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43088
43089             } else  {
43090                 if (ix.length) {
43091                     // used to clear ?? but if we are loading unselected 
43092                     this.setFromData(view.store.getAt(ix[0]).data);
43093                 }
43094                 
43095             }
43096             
43097             return;
43098         }
43099         
43100         if (!ix.length) {
43101             // this get's fired when trigger opens..
43102            // this.setFromData({});
43103             var str = this.stores[opts.list+1];
43104             str.data.clear(); // removeall wihtout the fire events..
43105             return;
43106         }
43107         
43108         var rec = view.store.getAt(ix[0]);
43109          
43110         this.setFromData(rec.data);
43111         this.fireEvent('select', this, rec, ix[0]);
43112         
43113         var lw = Math.floor(
43114              (
43115                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43116              ) / this.maxColumns
43117         );
43118         this.loadingChildren = true;
43119         this.stores[opts.list+1].loadDataFromChildren( rec );
43120         this.loadingChildren = false;
43121         var dl = this.stores[opts.list+1]. getTotalCount();
43122         
43123         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43124         
43125         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43126         for (var i = opts.list+2; i < this.views.length;i++) {
43127             this.views[i].getEl().setStyle({ display : 'none' });
43128         }
43129         
43130         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43131         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43132         
43133         if (this.isLoading) {
43134            // this.selectActive(opts.list);
43135         }
43136          
43137     },
43138     
43139     
43140     
43141     
43142     onDoubleClick : function()
43143     {
43144         this.collapse(); //??
43145     },
43146     
43147      
43148     
43149     
43150     
43151     // private
43152     recordToStack : function(store, prop, value, stack)
43153     {
43154         var cstore = new Roo.data.SimpleStore({
43155             //fields : this.store.reader.meta.fields, // we need array reader.. for
43156             reader : this.store.reader,
43157             data : [ ]
43158         });
43159         var _this = this;
43160         var record  = false;
43161         var srec = false;
43162         if(store.getCount() < 1){
43163             return false;
43164         }
43165         store.each(function(r){
43166             if(r.data[prop] == value){
43167                 record = r;
43168             srec = r;
43169                 return false;
43170             }
43171             if (r.data.cn && r.data.cn.length) {
43172                 cstore.loadDataFromChildren( r);
43173                 var cret = _this.recordToStack(cstore, prop, value, stack);
43174                 if (cret !== false) {
43175                     record = cret;
43176                     srec = r;
43177                     return false;
43178                 }
43179             }
43180              
43181             return true;
43182         });
43183         if (record == false) {
43184             return false
43185         }
43186         stack.unshift(srec);
43187         return record;
43188     },
43189     
43190     /*
43191      * find the stack of stores that match our value.
43192      *
43193      * 
43194      */
43195     
43196     selectActive : function ()
43197     {
43198         // if store is not loaded, then we will need to wait for that to happen first.
43199         var stack = [];
43200         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43201         for (var i = 0; i < stack.length; i++ ) {
43202             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43203         }
43204         
43205     }
43206         
43207          
43208     
43209     
43210     
43211     
43212 });/*
43213  * Based on:
43214  * Ext JS Library 1.1.1
43215  * Copyright(c) 2006-2007, Ext JS, LLC.
43216  *
43217  * Originally Released Under LGPL - original licence link has changed is not relivant.
43218  *
43219  * Fork - LGPL
43220  * <script type="text/javascript">
43221  */
43222 /**
43223  * @class Roo.form.Checkbox
43224  * @extends Roo.form.Field
43225  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43226  * @constructor
43227  * Creates a new Checkbox
43228  * @param {Object} config Configuration options
43229  */
43230 Roo.form.Checkbox = function(config){
43231     Roo.form.Checkbox.superclass.constructor.call(this, config);
43232     this.addEvents({
43233         /**
43234          * @event check
43235          * Fires when the checkbox is checked or unchecked.
43236              * @param {Roo.form.Checkbox} this This checkbox
43237              * @param {Boolean} checked The new checked value
43238              */
43239         check : true
43240     });
43241 };
43242
43243 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43244     /**
43245      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43246      */
43247     focusClass : undefined,
43248     /**
43249      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43250      */
43251     fieldClass: "x-form-field",
43252     /**
43253      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43254      */
43255     checked: false,
43256     /**
43257      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43258      * {tag: "input", type: "checkbox", autocomplete: "off"})
43259      */
43260     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43261     /**
43262      * @cfg {String} boxLabel The text that appears beside the checkbox
43263      */
43264     boxLabel : "",
43265     /**
43266      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43267      */  
43268     inputValue : '1',
43269     /**
43270      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43271      */
43272      valueOff: '0', // value when not checked..
43273
43274     actionMode : 'viewEl', 
43275     //
43276     // private
43277     itemCls : 'x-menu-check-item x-form-item',
43278     groupClass : 'x-menu-group-item',
43279     inputType : 'hidden',
43280     
43281     
43282     inSetChecked: false, // check that we are not calling self...
43283     
43284     inputElement: false, // real input element?
43285     basedOn: false, // ????
43286     
43287     isFormField: true, // not sure where this is needed!!!!
43288
43289     onResize : function(){
43290         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43291         if(!this.boxLabel){
43292             this.el.alignTo(this.wrap, 'c-c');
43293         }
43294     },
43295
43296     initEvents : function(){
43297         Roo.form.Checkbox.superclass.initEvents.call(this);
43298         this.el.on("click", this.onClick,  this);
43299         this.el.on("change", this.onClick,  this);
43300     },
43301
43302
43303     getResizeEl : function(){
43304         return this.wrap;
43305     },
43306
43307     getPositionEl : function(){
43308         return this.wrap;
43309     },
43310
43311     // private
43312     onRender : function(ct, position){
43313         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43314         /*
43315         if(this.inputValue !== undefined){
43316             this.el.dom.value = this.inputValue;
43317         }
43318         */
43319         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43320         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43321         var viewEl = this.wrap.createChild({ 
43322             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43323         this.viewEl = viewEl;   
43324         this.wrap.on('click', this.onClick,  this); 
43325         
43326         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43327         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43328         
43329         
43330         
43331         if(this.boxLabel){
43332             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43333         //    viewEl.on('click', this.onClick,  this); 
43334         }
43335         //if(this.checked){
43336             this.setChecked(this.checked);
43337         //}else{
43338             //this.checked = this.el.dom;
43339         //}
43340
43341     },
43342
43343     // private
43344     initValue : Roo.emptyFn,
43345
43346     /**
43347      * Returns the checked state of the checkbox.
43348      * @return {Boolean} True if checked, else false
43349      */
43350     getValue : function(){
43351         if(this.el){
43352             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43353         }
43354         return this.valueOff;
43355         
43356     },
43357
43358         // private
43359     onClick : function(){ 
43360         if (this.disabled) {
43361             return;
43362         }
43363         this.setChecked(!this.checked);
43364
43365         //if(this.el.dom.checked != this.checked){
43366         //    this.setValue(this.el.dom.checked);
43367        // }
43368     },
43369
43370     /**
43371      * Sets the checked state of the checkbox.
43372      * On is always based on a string comparison between inputValue and the param.
43373      * @param {Boolean/String} value - the value to set 
43374      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43375      */
43376     setValue : function(v,suppressEvent){
43377         
43378         
43379         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43380         //if(this.el && this.el.dom){
43381         //    this.el.dom.checked = this.checked;
43382         //    this.el.dom.defaultChecked = this.checked;
43383         //}
43384         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43385         //this.fireEvent("check", this, this.checked);
43386     },
43387     // private..
43388     setChecked : function(state,suppressEvent)
43389     {
43390         if (this.inSetChecked) {
43391             this.checked = state;
43392             return;
43393         }
43394         
43395     
43396         if(this.wrap){
43397             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43398         }
43399         this.checked = state;
43400         if(suppressEvent !== true){
43401             this.fireEvent('check', this, state);
43402         }
43403         this.inSetChecked = true;
43404         this.el.dom.value = state ? this.inputValue : this.valueOff;
43405         this.inSetChecked = false;
43406         
43407     },
43408     // handle setting of hidden value by some other method!!?!?
43409     setFromHidden: function()
43410     {
43411         if(!this.el){
43412             return;
43413         }
43414         //console.log("SET FROM HIDDEN");
43415         //alert('setFrom hidden');
43416         this.setValue(this.el.dom.value);
43417     },
43418     
43419     onDestroy : function()
43420     {
43421         if(this.viewEl){
43422             Roo.get(this.viewEl).remove();
43423         }
43424          
43425         Roo.form.Checkbox.superclass.onDestroy.call(this);
43426     },
43427     
43428     setBoxLabel : function(str)
43429     {
43430         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43431     }
43432
43433 });/*
43434  * Based on:
43435  * Ext JS Library 1.1.1
43436  * Copyright(c) 2006-2007, Ext JS, LLC.
43437  *
43438  * Originally Released Under LGPL - original licence link has changed is not relivant.
43439  *
43440  * Fork - LGPL
43441  * <script type="text/javascript">
43442  */
43443  
43444 /**
43445  * @class Roo.form.Radio
43446  * @extends Roo.form.Checkbox
43447  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43448  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43449  * @constructor
43450  * Creates a new Radio
43451  * @param {Object} config Configuration options
43452  */
43453 Roo.form.Radio = function(){
43454     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43455 };
43456 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43457     inputType: 'radio',
43458
43459     /**
43460      * If this radio is part of a group, it will return the selected value
43461      * @return {String}
43462      */
43463     getGroupValue : function(){
43464         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43465     },
43466     
43467     
43468     onRender : function(ct, position){
43469         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43470         
43471         if(this.inputValue !== undefined){
43472             this.el.dom.value = this.inputValue;
43473         }
43474          
43475         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43476         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43477         //var viewEl = this.wrap.createChild({ 
43478         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43479         //this.viewEl = viewEl;   
43480         //this.wrap.on('click', this.onClick,  this); 
43481         
43482         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43483         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43484         
43485         
43486         
43487         if(this.boxLabel){
43488             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43489         //    viewEl.on('click', this.onClick,  this); 
43490         }
43491          if(this.checked){
43492             this.el.dom.checked =   'checked' ;
43493         }
43494          
43495     } 
43496     
43497     
43498 });//<script type="text/javascript">
43499
43500 /*
43501  * Based  Ext JS Library 1.1.1
43502  * Copyright(c) 2006-2007, Ext JS, LLC.
43503  * LGPL
43504  *
43505  */
43506  
43507 /**
43508  * @class Roo.HtmlEditorCore
43509  * @extends Roo.Component
43510  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43511  *
43512  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43513  */
43514
43515 Roo.HtmlEditorCore = function(config){
43516     
43517     
43518     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43519     
43520     
43521     this.addEvents({
43522         /**
43523          * @event initialize
43524          * Fires when the editor is fully initialized (including the iframe)
43525          * @param {Roo.HtmlEditorCore} this
43526          */
43527         initialize: true,
43528         /**
43529          * @event activate
43530          * Fires when the editor is first receives the focus. Any insertion must wait
43531          * until after this event.
43532          * @param {Roo.HtmlEditorCore} this
43533          */
43534         activate: true,
43535          /**
43536          * @event beforesync
43537          * Fires before the textarea is updated with content from the editor iframe. Return false
43538          * to cancel the sync.
43539          * @param {Roo.HtmlEditorCore} this
43540          * @param {String} html
43541          */
43542         beforesync: true,
43543          /**
43544          * @event beforepush
43545          * Fires before the iframe editor is updated with content from the textarea. Return false
43546          * to cancel the push.
43547          * @param {Roo.HtmlEditorCore} this
43548          * @param {String} html
43549          */
43550         beforepush: true,
43551          /**
43552          * @event sync
43553          * Fires when the textarea is updated with content from the editor iframe.
43554          * @param {Roo.HtmlEditorCore} this
43555          * @param {String} html
43556          */
43557         sync: true,
43558          /**
43559          * @event push
43560          * Fires when the iframe editor is updated with content from the textarea.
43561          * @param {Roo.HtmlEditorCore} this
43562          * @param {String} html
43563          */
43564         push: true,
43565         
43566         /**
43567          * @event editorevent
43568          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43569          * @param {Roo.HtmlEditorCore} this
43570          */
43571         editorevent: true
43572         
43573     });
43574     
43575     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43576     
43577     // defaults : white / black...
43578     this.applyBlacklists();
43579     
43580     
43581     
43582 };
43583
43584
43585 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43586
43587
43588      /**
43589      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43590      */
43591     
43592     owner : false,
43593     
43594      /**
43595      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43596      *                        Roo.resizable.
43597      */
43598     resizable : false,
43599      /**
43600      * @cfg {Number} height (in pixels)
43601      */   
43602     height: 300,
43603    /**
43604      * @cfg {Number} width (in pixels)
43605      */   
43606     width: 500,
43607     
43608     /**
43609      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43610      * 
43611      */
43612     stylesheets: false,
43613     
43614     // id of frame..
43615     frameId: false,
43616     
43617     // private properties
43618     validationEvent : false,
43619     deferHeight: true,
43620     initialized : false,
43621     activated : false,
43622     sourceEditMode : false,
43623     onFocus : Roo.emptyFn,
43624     iframePad:3,
43625     hideMode:'offsets',
43626     
43627     clearUp: true,
43628     
43629     // blacklist + whitelisted elements..
43630     black: false,
43631     white: false,
43632      
43633     bodyCls : '',
43634
43635     /**
43636      * Protected method that will not generally be called directly. It
43637      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43638      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43639      */
43640     getDocMarkup : function(){
43641         // body styles..
43642         var st = '';
43643         
43644         // inherit styels from page...?? 
43645         if (this.stylesheets === false) {
43646             
43647             Roo.get(document.head).select('style').each(function(node) {
43648                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43649             });
43650             
43651             Roo.get(document.head).select('link').each(function(node) { 
43652                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43653             });
43654             
43655         } else if (!this.stylesheets.length) {
43656                 // simple..
43657                 st = '<style type="text/css">' +
43658                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43659                    '</style>';
43660         } else {
43661             for (var i in this.stylesheets) { 
43662                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43663             }
43664             
43665         }
43666         
43667         st +=  '<style type="text/css">' +
43668             'IMG { cursor: pointer } ' +
43669         '</style>';
43670
43671         var cls = 'roo-htmleditor-body';
43672         
43673         if(this.bodyCls.length){
43674             cls += ' ' + this.bodyCls;
43675         }
43676         
43677         return '<html><head>' + st  +
43678             //<style type="text/css">' +
43679             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43680             //'</style>' +
43681             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43682     },
43683
43684     // private
43685     onRender : function(ct, position)
43686     {
43687         var _t = this;
43688         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43689         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43690         
43691         
43692         this.el.dom.style.border = '0 none';
43693         this.el.dom.setAttribute('tabIndex', -1);
43694         this.el.addClass('x-hidden hide');
43695         
43696         
43697         
43698         if(Roo.isIE){ // fix IE 1px bogus margin
43699             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43700         }
43701        
43702         
43703         this.frameId = Roo.id();
43704         
43705          
43706         
43707         var iframe = this.owner.wrap.createChild({
43708             tag: 'iframe',
43709             cls: 'form-control', // bootstrap..
43710             id: this.frameId,
43711             name: this.frameId,
43712             frameBorder : 'no',
43713             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43714         }, this.el
43715         );
43716         
43717         
43718         this.iframe = iframe.dom;
43719
43720          this.assignDocWin();
43721         
43722         this.doc.designMode = 'on';
43723        
43724         this.doc.open();
43725         this.doc.write(this.getDocMarkup());
43726         this.doc.close();
43727
43728         
43729         var task = { // must defer to wait for browser to be ready
43730             run : function(){
43731                 //console.log("run task?" + this.doc.readyState);
43732                 this.assignDocWin();
43733                 if(this.doc.body || this.doc.readyState == 'complete'){
43734                     try {
43735                         this.doc.designMode="on";
43736                     } catch (e) {
43737                         return;
43738                     }
43739                     Roo.TaskMgr.stop(task);
43740                     this.initEditor.defer(10, this);
43741                 }
43742             },
43743             interval : 10,
43744             duration: 10000,
43745             scope: this
43746         };
43747         Roo.TaskMgr.start(task);
43748
43749     },
43750
43751     // private
43752     onResize : function(w, h)
43753     {
43754          Roo.log('resize: ' +w + ',' + h );
43755         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43756         if(!this.iframe){
43757             return;
43758         }
43759         if(typeof w == 'number'){
43760             
43761             this.iframe.style.width = w + 'px';
43762         }
43763         if(typeof h == 'number'){
43764             
43765             this.iframe.style.height = h + 'px';
43766             if(this.doc){
43767                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43768             }
43769         }
43770         
43771     },
43772
43773     /**
43774      * Toggles the editor between standard and source edit mode.
43775      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43776      */
43777     toggleSourceEdit : function(sourceEditMode){
43778         
43779         this.sourceEditMode = sourceEditMode === true;
43780         
43781         if(this.sourceEditMode){
43782  
43783             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43784             
43785         }else{
43786             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43787             //this.iframe.className = '';
43788             this.deferFocus();
43789         }
43790         //this.setSize(this.owner.wrap.getSize());
43791         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43792     },
43793
43794     
43795   
43796
43797     /**
43798      * Protected method that will not generally be called directly. If you need/want
43799      * custom HTML cleanup, this is the method you should override.
43800      * @param {String} html The HTML to be cleaned
43801      * return {String} The cleaned HTML
43802      */
43803     cleanHtml : function(html){
43804         html = String(html);
43805         if(html.length > 5){
43806             if(Roo.isSafari){ // strip safari nonsense
43807                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43808             }
43809         }
43810         if(html == '&nbsp;'){
43811             html = '';
43812         }
43813         return html;
43814     },
43815
43816     /**
43817      * HTML Editor -> Textarea
43818      * Protected method that will not generally be called directly. Syncs the contents
43819      * of the editor iframe with the textarea.
43820      */
43821     syncValue : function(){
43822         if(this.initialized){
43823             var bd = (this.doc.body || this.doc.documentElement);
43824             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43825             var html = bd.innerHTML;
43826             if(Roo.isSafari){
43827                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43828                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43829                 if(m && m[1]){
43830                     html = '<div style="'+m[0]+'">' + html + '</div>';
43831                 }
43832             }
43833             html = this.cleanHtml(html);
43834             // fix up the special chars.. normaly like back quotes in word...
43835             // however we do not want to do this with chinese..
43836             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43837                 
43838                 var cc = match.charCodeAt();
43839
43840                 // Get the character value, handling surrogate pairs
43841                 if (match.length == 2) {
43842                     // It's a surrogate pair, calculate the Unicode code point
43843                     var high = match.charCodeAt(0) - 0xD800;
43844                     var low  = match.charCodeAt(1) - 0xDC00;
43845                     cc = (high * 0x400) + low + 0x10000;
43846                 }  else if (
43847                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43848                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43849                     (cc >= 0xf900 && cc < 0xfb00 )
43850                 ) {
43851                         return match;
43852                 }  
43853          
43854                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43855                 return "&#" + cc + ";";
43856                 
43857                 
43858             });
43859             
43860             
43861              
43862             if(this.owner.fireEvent('beforesync', this, html) !== false){
43863                 this.el.dom.value = html;
43864                 this.owner.fireEvent('sync', this, html);
43865             }
43866         }
43867     },
43868
43869     /**
43870      * Protected method that will not generally be called directly. Pushes the value of the textarea
43871      * into the iframe editor.
43872      */
43873     pushValue : function(){
43874         if(this.initialized){
43875             var v = this.el.dom.value.trim();
43876             
43877 //            if(v.length < 1){
43878 //                v = '&#160;';
43879 //            }
43880             
43881             if(this.owner.fireEvent('beforepush', this, v) !== false){
43882                 var d = (this.doc.body || this.doc.documentElement);
43883                 d.innerHTML = v;
43884                 this.cleanUpPaste();
43885                 this.el.dom.value = d.innerHTML;
43886                 this.owner.fireEvent('push', this, v);
43887             }
43888         }
43889     },
43890
43891     // private
43892     deferFocus : function(){
43893         this.focus.defer(10, this);
43894     },
43895
43896     // doc'ed in Field
43897     focus : function(){
43898         if(this.win && !this.sourceEditMode){
43899             this.win.focus();
43900         }else{
43901             this.el.focus();
43902         }
43903     },
43904     
43905     assignDocWin: function()
43906     {
43907         var iframe = this.iframe;
43908         
43909          if(Roo.isIE){
43910             this.doc = iframe.contentWindow.document;
43911             this.win = iframe.contentWindow;
43912         } else {
43913 //            if (!Roo.get(this.frameId)) {
43914 //                return;
43915 //            }
43916 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43917 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43918             
43919             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43920                 return;
43921             }
43922             
43923             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43924             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43925         }
43926     },
43927     
43928     // private
43929     initEditor : function(){
43930         //console.log("INIT EDITOR");
43931         this.assignDocWin();
43932         
43933         
43934         
43935         this.doc.designMode="on";
43936         this.doc.open();
43937         this.doc.write(this.getDocMarkup());
43938         this.doc.close();
43939         
43940         var dbody = (this.doc.body || this.doc.documentElement);
43941         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43942         // this copies styles from the containing element into thsi one..
43943         // not sure why we need all of this..
43944         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43945         
43946         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43947         //ss['background-attachment'] = 'fixed'; // w3c
43948         dbody.bgProperties = 'fixed'; // ie
43949         //Roo.DomHelper.applyStyles(dbody, ss);
43950         Roo.EventManager.on(this.doc, {
43951             //'mousedown': this.onEditorEvent,
43952             'mouseup': this.onEditorEvent,
43953             'dblclick': this.onEditorEvent,
43954             'click': this.onEditorEvent,
43955             'keyup': this.onEditorEvent,
43956             buffer:100,
43957             scope: this
43958         });
43959         if(Roo.isGecko){
43960             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43961         }
43962         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43963             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43964         }
43965         this.initialized = true;
43966
43967         this.owner.fireEvent('initialize', this);
43968         this.pushValue();
43969     },
43970
43971     // private
43972     onDestroy : function(){
43973         
43974         
43975         
43976         if(this.rendered){
43977             
43978             //for (var i =0; i < this.toolbars.length;i++) {
43979             //    // fixme - ask toolbars for heights?
43980             //    this.toolbars[i].onDestroy();
43981            // }
43982             
43983             //this.wrap.dom.innerHTML = '';
43984             //this.wrap.remove();
43985         }
43986     },
43987
43988     // private
43989     onFirstFocus : function(){
43990         
43991         this.assignDocWin();
43992         
43993         
43994         this.activated = true;
43995          
43996     
43997         if(Roo.isGecko){ // prevent silly gecko errors
43998             this.win.focus();
43999             var s = this.win.getSelection();
44000             if(!s.focusNode || s.focusNode.nodeType != 3){
44001                 var r = s.getRangeAt(0);
44002                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44003                 r.collapse(true);
44004                 this.deferFocus();
44005             }
44006             try{
44007                 this.execCmd('useCSS', true);
44008                 this.execCmd('styleWithCSS', false);
44009             }catch(e){}
44010         }
44011         this.owner.fireEvent('activate', this);
44012     },
44013
44014     // private
44015     adjustFont: function(btn){
44016         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44017         //if(Roo.isSafari){ // safari
44018         //    adjust *= 2;
44019        // }
44020         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44021         if(Roo.isSafari){ // safari
44022             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44023             v =  (v < 10) ? 10 : v;
44024             v =  (v > 48) ? 48 : v;
44025             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44026             
44027         }
44028         
44029         
44030         v = Math.max(1, v+adjust);
44031         
44032         this.execCmd('FontSize', v  );
44033     },
44034
44035     onEditorEvent : function(e)
44036     {
44037         this.owner.fireEvent('editorevent', this, e);
44038       //  this.updateToolbar();
44039         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44040     },
44041
44042     insertTag : function(tg)
44043     {
44044         // could be a bit smarter... -> wrap the current selected tRoo..
44045         if (tg.toLowerCase() == 'span' ||
44046             tg.toLowerCase() == 'code' ||
44047             tg.toLowerCase() == 'sup' ||
44048             tg.toLowerCase() == 'sub' 
44049             ) {
44050             
44051             range = this.createRange(this.getSelection());
44052             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44053             wrappingNode.appendChild(range.extractContents());
44054             range.insertNode(wrappingNode);
44055
44056             return;
44057             
44058             
44059             
44060         }
44061         this.execCmd("formatblock",   tg);
44062         
44063     },
44064     
44065     insertText : function(txt)
44066     {
44067         
44068         
44069         var range = this.createRange();
44070         range.deleteContents();
44071                //alert(Sender.getAttribute('label'));
44072                
44073         range.insertNode(this.doc.createTextNode(txt));
44074     } ,
44075     
44076      
44077
44078     /**
44079      * Executes a Midas editor command on the editor document and performs necessary focus and
44080      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44081      * @param {String} cmd The Midas command
44082      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44083      */
44084     relayCmd : function(cmd, value){
44085         this.win.focus();
44086         this.execCmd(cmd, value);
44087         this.owner.fireEvent('editorevent', this);
44088         //this.updateToolbar();
44089         this.owner.deferFocus();
44090     },
44091
44092     /**
44093      * Executes a Midas editor command directly on the editor document.
44094      * For visual commands, you should use {@link #relayCmd} instead.
44095      * <b>This should only be called after the editor is initialized.</b>
44096      * @param {String} cmd The Midas command
44097      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44098      */
44099     execCmd : function(cmd, value){
44100         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44101         this.syncValue();
44102     },
44103  
44104  
44105    
44106     /**
44107      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44108      * to insert tRoo.
44109      * @param {String} text | dom node.. 
44110      */
44111     insertAtCursor : function(text)
44112     {
44113         
44114         if(!this.activated){
44115             return;
44116         }
44117         /*
44118         if(Roo.isIE){
44119             this.win.focus();
44120             var r = this.doc.selection.createRange();
44121             if(r){
44122                 r.collapse(true);
44123                 r.pasteHTML(text);
44124                 this.syncValue();
44125                 this.deferFocus();
44126             
44127             }
44128             return;
44129         }
44130         */
44131         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44132             this.win.focus();
44133             
44134             
44135             // from jquery ui (MIT licenced)
44136             var range, node;
44137             var win = this.win;
44138             
44139             if (win.getSelection && win.getSelection().getRangeAt) {
44140                 range = win.getSelection().getRangeAt(0);
44141                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44142                 range.insertNode(node);
44143             } else if (win.document.selection && win.document.selection.createRange) {
44144                 // no firefox support
44145                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44146                 win.document.selection.createRange().pasteHTML(txt);
44147             } else {
44148                 // no firefox support
44149                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44150                 this.execCmd('InsertHTML', txt);
44151             } 
44152             
44153             this.syncValue();
44154             
44155             this.deferFocus();
44156         }
44157     },
44158  // private
44159     mozKeyPress : function(e){
44160         if(e.ctrlKey){
44161             var c = e.getCharCode(), cmd;
44162           
44163             if(c > 0){
44164                 c = String.fromCharCode(c).toLowerCase();
44165                 switch(c){
44166                     case 'b':
44167                         cmd = 'bold';
44168                         break;
44169                     case 'i':
44170                         cmd = 'italic';
44171                         break;
44172                     
44173                     case 'u':
44174                         cmd = 'underline';
44175                         break;
44176                     
44177                     case 'v':
44178                         this.cleanUpPaste.defer(100, this);
44179                         return;
44180                         
44181                 }
44182                 if(cmd){
44183                     this.win.focus();
44184                     this.execCmd(cmd);
44185                     this.deferFocus();
44186                     e.preventDefault();
44187                 }
44188                 
44189             }
44190         }
44191     },
44192
44193     // private
44194     fixKeys : function(){ // load time branching for fastest keydown performance
44195         if(Roo.isIE){
44196             return function(e){
44197                 var k = e.getKey(), r;
44198                 if(k == e.TAB){
44199                     e.stopEvent();
44200                     r = this.doc.selection.createRange();
44201                     if(r){
44202                         r.collapse(true);
44203                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44204                         this.deferFocus();
44205                     }
44206                     return;
44207                 }
44208                 
44209                 if(k == e.ENTER){
44210                     r = this.doc.selection.createRange();
44211                     if(r){
44212                         var target = r.parentElement();
44213                         if(!target || target.tagName.toLowerCase() != 'li'){
44214                             e.stopEvent();
44215                             r.pasteHTML('<br />');
44216                             r.collapse(false);
44217                             r.select();
44218                         }
44219                     }
44220                 }
44221                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44222                     this.cleanUpPaste.defer(100, this);
44223                     return;
44224                 }
44225                 
44226                 
44227             };
44228         }else if(Roo.isOpera){
44229             return function(e){
44230                 var k = e.getKey();
44231                 if(k == e.TAB){
44232                     e.stopEvent();
44233                     this.win.focus();
44234                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44235                     this.deferFocus();
44236                 }
44237                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44238                     this.cleanUpPaste.defer(100, this);
44239                     return;
44240                 }
44241                 
44242             };
44243         }else if(Roo.isSafari){
44244             return function(e){
44245                 var k = e.getKey();
44246                 
44247                 if(k == e.TAB){
44248                     e.stopEvent();
44249                     this.execCmd('InsertText','\t');
44250                     this.deferFocus();
44251                     return;
44252                 }
44253                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44254                     this.cleanUpPaste.defer(100, this);
44255                     return;
44256                 }
44257                 
44258              };
44259         }
44260     }(),
44261     
44262     getAllAncestors: function()
44263     {
44264         var p = this.getSelectedNode();
44265         var a = [];
44266         if (!p) {
44267             a.push(p); // push blank onto stack..
44268             p = this.getParentElement();
44269         }
44270         
44271         
44272         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44273             a.push(p);
44274             p = p.parentNode;
44275         }
44276         a.push(this.doc.body);
44277         return a;
44278     },
44279     lastSel : false,
44280     lastSelNode : false,
44281     
44282     
44283     getSelection : function() 
44284     {
44285         this.assignDocWin();
44286         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44287     },
44288     
44289     getSelectedNode: function() 
44290     {
44291         // this may only work on Gecko!!!
44292         
44293         // should we cache this!!!!
44294         
44295         
44296         
44297          
44298         var range = this.createRange(this.getSelection()).cloneRange();
44299         
44300         if (Roo.isIE) {
44301             var parent = range.parentElement();
44302             while (true) {
44303                 var testRange = range.duplicate();
44304                 testRange.moveToElementText(parent);
44305                 if (testRange.inRange(range)) {
44306                     break;
44307                 }
44308                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44309                     break;
44310                 }
44311                 parent = parent.parentElement;
44312             }
44313             return parent;
44314         }
44315         
44316         // is ancestor a text element.
44317         var ac =  range.commonAncestorContainer;
44318         if (ac.nodeType == 3) {
44319             ac = ac.parentNode;
44320         }
44321         
44322         var ar = ac.childNodes;
44323          
44324         var nodes = [];
44325         var other_nodes = [];
44326         var has_other_nodes = false;
44327         for (var i=0;i<ar.length;i++) {
44328             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44329                 continue;
44330             }
44331             // fullly contained node.
44332             
44333             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44334                 nodes.push(ar[i]);
44335                 continue;
44336             }
44337             
44338             // probably selected..
44339             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44340                 other_nodes.push(ar[i]);
44341                 continue;
44342             }
44343             // outer..
44344             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44345                 continue;
44346             }
44347             
44348             
44349             has_other_nodes = true;
44350         }
44351         if (!nodes.length && other_nodes.length) {
44352             nodes= other_nodes;
44353         }
44354         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44355             return false;
44356         }
44357         
44358         return nodes[0];
44359     },
44360     createRange: function(sel)
44361     {
44362         // this has strange effects when using with 
44363         // top toolbar - not sure if it's a great idea.
44364         //this.editor.contentWindow.focus();
44365         if (typeof sel != "undefined") {
44366             try {
44367                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44368             } catch(e) {
44369                 return this.doc.createRange();
44370             }
44371         } else {
44372             return this.doc.createRange();
44373         }
44374     },
44375     getParentElement: function()
44376     {
44377         
44378         this.assignDocWin();
44379         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44380         
44381         var range = this.createRange(sel);
44382          
44383         try {
44384             var p = range.commonAncestorContainer;
44385             while (p.nodeType == 3) { // text node
44386                 p = p.parentNode;
44387             }
44388             return p;
44389         } catch (e) {
44390             return null;
44391         }
44392     
44393     },
44394     /***
44395      *
44396      * Range intersection.. the hard stuff...
44397      *  '-1' = before
44398      *  '0' = hits..
44399      *  '1' = after.
44400      *         [ -- selected range --- ]
44401      *   [fail]                        [fail]
44402      *
44403      *    basically..
44404      *      if end is before start or  hits it. fail.
44405      *      if start is after end or hits it fail.
44406      *
44407      *   if either hits (but other is outside. - then it's not 
44408      *   
44409      *    
44410      **/
44411     
44412     
44413     // @see http://www.thismuchiknow.co.uk/?p=64.
44414     rangeIntersectsNode : function(range, node)
44415     {
44416         var nodeRange = node.ownerDocument.createRange();
44417         try {
44418             nodeRange.selectNode(node);
44419         } catch (e) {
44420             nodeRange.selectNodeContents(node);
44421         }
44422     
44423         var rangeStartRange = range.cloneRange();
44424         rangeStartRange.collapse(true);
44425     
44426         var rangeEndRange = range.cloneRange();
44427         rangeEndRange.collapse(false);
44428     
44429         var nodeStartRange = nodeRange.cloneRange();
44430         nodeStartRange.collapse(true);
44431     
44432         var nodeEndRange = nodeRange.cloneRange();
44433         nodeEndRange.collapse(false);
44434     
44435         return rangeStartRange.compareBoundaryPoints(
44436                  Range.START_TO_START, nodeEndRange) == -1 &&
44437                rangeEndRange.compareBoundaryPoints(
44438                  Range.START_TO_START, nodeStartRange) == 1;
44439         
44440          
44441     },
44442     rangeCompareNode : function(range, node)
44443     {
44444         var nodeRange = node.ownerDocument.createRange();
44445         try {
44446             nodeRange.selectNode(node);
44447         } catch (e) {
44448             nodeRange.selectNodeContents(node);
44449         }
44450         
44451         
44452         range.collapse(true);
44453     
44454         nodeRange.collapse(true);
44455      
44456         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44457         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44458          
44459         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44460         
44461         var nodeIsBefore   =  ss == 1;
44462         var nodeIsAfter    = ee == -1;
44463         
44464         if (nodeIsBefore && nodeIsAfter) {
44465             return 0; // outer
44466         }
44467         if (!nodeIsBefore && nodeIsAfter) {
44468             return 1; //right trailed.
44469         }
44470         
44471         if (nodeIsBefore && !nodeIsAfter) {
44472             return 2;  // left trailed.
44473         }
44474         // fully contined.
44475         return 3;
44476     },
44477
44478     // private? - in a new class?
44479     cleanUpPaste :  function()
44480     {
44481         // cleans up the whole document..
44482         Roo.log('cleanuppaste');
44483         
44484         this.cleanUpChildren(this.doc.body);
44485         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44486         if (clean != this.doc.body.innerHTML) {
44487             this.doc.body.innerHTML = clean;
44488         }
44489         
44490     },
44491     
44492     cleanWordChars : function(input) {// change the chars to hex code
44493         var he = Roo.HtmlEditorCore;
44494         
44495         var output = input;
44496         Roo.each(he.swapCodes, function(sw) { 
44497             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44498             
44499             output = output.replace(swapper, sw[1]);
44500         });
44501         
44502         return output;
44503     },
44504     
44505     
44506     cleanUpChildren : function (n)
44507     {
44508         if (!n.childNodes.length) {
44509             return;
44510         }
44511         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44512            this.cleanUpChild(n.childNodes[i]);
44513         }
44514     },
44515     
44516     
44517         
44518     
44519     cleanUpChild : function (node)
44520     {
44521         var ed = this;
44522         //console.log(node);
44523         if (node.nodeName == "#text") {
44524             // clean up silly Windows -- stuff?
44525             return; 
44526         }
44527         if (node.nodeName == "#comment") {
44528             node.parentNode.removeChild(node);
44529             // clean up silly Windows -- stuff?
44530             return; 
44531         }
44532         var lcname = node.tagName.toLowerCase();
44533         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44534         // whitelist of tags..
44535         
44536         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44537             // remove node.
44538             node.parentNode.removeChild(node);
44539             return;
44540             
44541         }
44542         
44543         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44544         
44545         // spans with no attributes - just remove them..
44546         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44547             remove_keep_children = true;
44548         }
44549         
44550         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44551         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44552         
44553         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44554         //    remove_keep_children = true;
44555         //}
44556         
44557         if (remove_keep_children) {
44558             this.cleanUpChildren(node);
44559             // inserts everything just before this node...
44560             while (node.childNodes.length) {
44561                 var cn = node.childNodes[0];
44562                 node.removeChild(cn);
44563                 node.parentNode.insertBefore(cn, node);
44564             }
44565             node.parentNode.removeChild(node);
44566             return;
44567         }
44568         
44569         if (!node.attributes || !node.attributes.length) {
44570             
44571           
44572             
44573             
44574             this.cleanUpChildren(node);
44575             return;
44576         }
44577         
44578         function cleanAttr(n,v)
44579         {
44580             
44581             if (v.match(/^\./) || v.match(/^\//)) {
44582                 return;
44583             }
44584             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44585                 return;
44586             }
44587             if (v.match(/^#/)) {
44588                 return;
44589             }
44590             if (v.match(/^\{/)) { // allow template editing.
44591                 return;
44592             }
44593 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44594             node.removeAttribute(n);
44595             
44596         }
44597         
44598         var cwhite = this.cwhite;
44599         var cblack = this.cblack;
44600             
44601         function cleanStyle(n,v)
44602         {
44603             if (v.match(/expression/)) { //XSS?? should we even bother..
44604                 node.removeAttribute(n);
44605                 return;
44606             }
44607             
44608             var parts = v.split(/;/);
44609             var clean = [];
44610             
44611             Roo.each(parts, function(p) {
44612                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44613                 if (!p.length) {
44614                     return true;
44615                 }
44616                 var l = p.split(':').shift().replace(/\s+/g,'');
44617                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44618                 
44619                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44620 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44621                     //node.removeAttribute(n);
44622                     return true;
44623                 }
44624                 //Roo.log()
44625                 // only allow 'c whitelisted system attributes'
44626                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44627 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44628                     //node.removeAttribute(n);
44629                     return true;
44630                 }
44631                 
44632                 
44633                  
44634                 
44635                 clean.push(p);
44636                 return true;
44637             });
44638             if (clean.length) { 
44639                 node.setAttribute(n, clean.join(';'));
44640             } else {
44641                 node.removeAttribute(n);
44642             }
44643             
44644         }
44645         
44646         
44647         for (var i = node.attributes.length-1; i > -1 ; i--) {
44648             var a = node.attributes[i];
44649             //console.log(a);
44650             
44651             if (a.name.toLowerCase().substr(0,2)=='on')  {
44652                 node.removeAttribute(a.name);
44653                 continue;
44654             }
44655             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44656                 node.removeAttribute(a.name);
44657                 continue;
44658             }
44659             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44660                 cleanAttr(a.name,a.value); // fixme..
44661                 continue;
44662             }
44663             if (a.name == 'style') {
44664                 cleanStyle(a.name,a.value);
44665                 continue;
44666             }
44667             /// clean up MS crap..
44668             // tecnically this should be a list of valid class'es..
44669             
44670             
44671             if (a.name == 'class') {
44672                 if (a.value.match(/^Mso/)) {
44673                     node.removeAttribute('class');
44674                 }
44675                 
44676                 if (a.value.match(/^body$/)) {
44677                     node.removeAttribute('class');
44678                 }
44679                 continue;
44680             }
44681             
44682             // style cleanup!?
44683             // class cleanup?
44684             
44685         }
44686         
44687         
44688         this.cleanUpChildren(node);
44689         
44690         
44691     },
44692     
44693     /**
44694      * Clean up MS wordisms...
44695      */
44696     cleanWord : function(node)
44697     {
44698         if (!node) {
44699             this.cleanWord(this.doc.body);
44700             return;
44701         }
44702         
44703         if(
44704                 node.nodeName == 'SPAN' &&
44705                 !node.hasAttributes() &&
44706                 node.childNodes.length == 1 &&
44707                 node.firstChild.nodeName == "#text"  
44708         ) {
44709             var textNode = node.firstChild;
44710             node.removeChild(textNode);
44711             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44712                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44713             }
44714             node.parentNode.insertBefore(textNode, node);
44715             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44716                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44717             }
44718             node.parentNode.removeChild(node);
44719         }
44720         
44721         if (node.nodeName == "#text") {
44722             // clean up silly Windows -- stuff?
44723             return; 
44724         }
44725         if (node.nodeName == "#comment") {
44726             node.parentNode.removeChild(node);
44727             // clean up silly Windows -- stuff?
44728             return; 
44729         }
44730         
44731         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44732             node.parentNode.removeChild(node);
44733             return;
44734         }
44735         //Roo.log(node.tagName);
44736         // remove - but keep children..
44737         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44738             //Roo.log('-- removed');
44739             while (node.childNodes.length) {
44740                 var cn = node.childNodes[0];
44741                 node.removeChild(cn);
44742                 node.parentNode.insertBefore(cn, node);
44743                 // move node to parent - and clean it..
44744                 this.cleanWord(cn);
44745             }
44746             node.parentNode.removeChild(node);
44747             /// no need to iterate chidlren = it's got none..
44748             //this.iterateChildren(node, this.cleanWord);
44749             return;
44750         }
44751         // clean styles
44752         if (node.className.length) {
44753             
44754             var cn = node.className.split(/\W+/);
44755             var cna = [];
44756             Roo.each(cn, function(cls) {
44757                 if (cls.match(/Mso[a-zA-Z]+/)) {
44758                     return;
44759                 }
44760                 cna.push(cls);
44761             });
44762             node.className = cna.length ? cna.join(' ') : '';
44763             if (!cna.length) {
44764                 node.removeAttribute("class");
44765             }
44766         }
44767         
44768         if (node.hasAttribute("lang")) {
44769             node.removeAttribute("lang");
44770         }
44771         
44772         if (node.hasAttribute("style")) {
44773             
44774             var styles = node.getAttribute("style").split(";");
44775             var nstyle = [];
44776             Roo.each(styles, function(s) {
44777                 if (!s.match(/:/)) {
44778                     return;
44779                 }
44780                 var kv = s.split(":");
44781                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44782                     return;
44783                 }
44784                 // what ever is left... we allow.
44785                 nstyle.push(s);
44786             });
44787             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44788             if (!nstyle.length) {
44789                 node.removeAttribute('style');
44790             }
44791         }
44792         this.iterateChildren(node, this.cleanWord);
44793         
44794         
44795         
44796     },
44797     /**
44798      * iterateChildren of a Node, calling fn each time, using this as the scole..
44799      * @param {DomNode} node node to iterate children of.
44800      * @param {Function} fn method of this class to call on each item.
44801      */
44802     iterateChildren : function(node, fn)
44803     {
44804         if (!node.childNodes.length) {
44805                 return;
44806         }
44807         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44808            fn.call(this, node.childNodes[i])
44809         }
44810     },
44811     
44812     
44813     /**
44814      * cleanTableWidths.
44815      *
44816      * Quite often pasting from word etc.. results in tables with column and widths.
44817      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44818      *
44819      */
44820     cleanTableWidths : function(node)
44821     {
44822          
44823          
44824         if (!node) {
44825             this.cleanTableWidths(this.doc.body);
44826             return;
44827         }
44828         
44829         // ignore list...
44830         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44831             return; 
44832         }
44833         Roo.log(node.tagName);
44834         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44835             this.iterateChildren(node, this.cleanTableWidths);
44836             return;
44837         }
44838         if (node.hasAttribute('width')) {
44839             node.removeAttribute('width');
44840         }
44841         
44842          
44843         if (node.hasAttribute("style")) {
44844             // pretty basic...
44845             
44846             var styles = node.getAttribute("style").split(";");
44847             var nstyle = [];
44848             Roo.each(styles, function(s) {
44849                 if (!s.match(/:/)) {
44850                     return;
44851                 }
44852                 var kv = s.split(":");
44853                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44854                     return;
44855                 }
44856                 // what ever is left... we allow.
44857                 nstyle.push(s);
44858             });
44859             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44860             if (!nstyle.length) {
44861                 node.removeAttribute('style');
44862             }
44863         }
44864         
44865         this.iterateChildren(node, this.cleanTableWidths);
44866         
44867         
44868     },
44869     
44870     
44871     
44872     
44873     domToHTML : function(currentElement, depth, nopadtext) {
44874         
44875         depth = depth || 0;
44876         nopadtext = nopadtext || false;
44877     
44878         if (!currentElement) {
44879             return this.domToHTML(this.doc.body);
44880         }
44881         
44882         //Roo.log(currentElement);
44883         var j;
44884         var allText = false;
44885         var nodeName = currentElement.nodeName;
44886         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44887         
44888         if  (nodeName == '#text') {
44889             
44890             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44891         }
44892         
44893         
44894         var ret = '';
44895         if (nodeName != 'BODY') {
44896              
44897             var i = 0;
44898             // Prints the node tagName, such as <A>, <IMG>, etc
44899             if (tagName) {
44900                 var attr = [];
44901                 for(i = 0; i < currentElement.attributes.length;i++) {
44902                     // quoting?
44903                     var aname = currentElement.attributes.item(i).name;
44904                     if (!currentElement.attributes.item(i).value.length) {
44905                         continue;
44906                     }
44907                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44908                 }
44909                 
44910                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44911             } 
44912             else {
44913                 
44914                 // eack
44915             }
44916         } else {
44917             tagName = false;
44918         }
44919         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44920             return ret;
44921         }
44922         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44923             nopadtext = true;
44924         }
44925         
44926         
44927         // Traverse the tree
44928         i = 0;
44929         var currentElementChild = currentElement.childNodes.item(i);
44930         var allText = true;
44931         var innerHTML  = '';
44932         lastnode = '';
44933         while (currentElementChild) {
44934             // Formatting code (indent the tree so it looks nice on the screen)
44935             var nopad = nopadtext;
44936             if (lastnode == 'SPAN') {
44937                 nopad  = true;
44938             }
44939             // text
44940             if  (currentElementChild.nodeName == '#text') {
44941                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44942                 toadd = nopadtext ? toadd : toadd.trim();
44943                 if (!nopad && toadd.length > 80) {
44944                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44945                 }
44946                 innerHTML  += toadd;
44947                 
44948                 i++;
44949                 currentElementChild = currentElement.childNodes.item(i);
44950                 lastNode = '';
44951                 continue;
44952             }
44953             allText = false;
44954             
44955             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44956                 
44957             // Recursively traverse the tree structure of the child node
44958             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44959             lastnode = currentElementChild.nodeName;
44960             i++;
44961             currentElementChild=currentElement.childNodes.item(i);
44962         }
44963         
44964         ret += innerHTML;
44965         
44966         if (!allText) {
44967                 // The remaining code is mostly for formatting the tree
44968             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44969         }
44970         
44971         
44972         if (tagName) {
44973             ret+= "</"+tagName+">";
44974         }
44975         return ret;
44976         
44977     },
44978         
44979     applyBlacklists : function()
44980     {
44981         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44982         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44983         
44984         this.white = [];
44985         this.black = [];
44986         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44987             if (b.indexOf(tag) > -1) {
44988                 return;
44989             }
44990             this.white.push(tag);
44991             
44992         }, this);
44993         
44994         Roo.each(w, function(tag) {
44995             if (b.indexOf(tag) > -1) {
44996                 return;
44997             }
44998             if (this.white.indexOf(tag) > -1) {
44999                 return;
45000             }
45001             this.white.push(tag);
45002             
45003         }, this);
45004         
45005         
45006         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45007             if (w.indexOf(tag) > -1) {
45008                 return;
45009             }
45010             this.black.push(tag);
45011             
45012         }, this);
45013         
45014         Roo.each(b, function(tag) {
45015             if (w.indexOf(tag) > -1) {
45016                 return;
45017             }
45018             if (this.black.indexOf(tag) > -1) {
45019                 return;
45020             }
45021             this.black.push(tag);
45022             
45023         }, this);
45024         
45025         
45026         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45027         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45028         
45029         this.cwhite = [];
45030         this.cblack = [];
45031         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45032             if (b.indexOf(tag) > -1) {
45033                 return;
45034             }
45035             this.cwhite.push(tag);
45036             
45037         }, this);
45038         
45039         Roo.each(w, function(tag) {
45040             if (b.indexOf(tag) > -1) {
45041                 return;
45042             }
45043             if (this.cwhite.indexOf(tag) > -1) {
45044                 return;
45045             }
45046             this.cwhite.push(tag);
45047             
45048         }, this);
45049         
45050         
45051         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45052             if (w.indexOf(tag) > -1) {
45053                 return;
45054             }
45055             this.cblack.push(tag);
45056             
45057         }, this);
45058         
45059         Roo.each(b, function(tag) {
45060             if (w.indexOf(tag) > -1) {
45061                 return;
45062             }
45063             if (this.cblack.indexOf(tag) > -1) {
45064                 return;
45065             }
45066             this.cblack.push(tag);
45067             
45068         }, this);
45069     },
45070     
45071     setStylesheets : function(stylesheets)
45072     {
45073         if(typeof(stylesheets) == 'string'){
45074             Roo.get(this.iframe.contentDocument.head).createChild({
45075                 tag : 'link',
45076                 rel : 'stylesheet',
45077                 type : 'text/css',
45078                 href : stylesheets
45079             });
45080             
45081             return;
45082         }
45083         var _this = this;
45084      
45085         Roo.each(stylesheets, function(s) {
45086             if(!s.length){
45087                 return;
45088             }
45089             
45090             Roo.get(_this.iframe.contentDocument.head).createChild({
45091                 tag : 'link',
45092                 rel : 'stylesheet',
45093                 type : 'text/css',
45094                 href : s
45095             });
45096         });
45097
45098         
45099     },
45100     
45101     removeStylesheets : function()
45102     {
45103         var _this = this;
45104         
45105         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45106             s.remove();
45107         });
45108     },
45109     
45110     setStyle : function(style)
45111     {
45112         Roo.get(this.iframe.contentDocument.head).createChild({
45113             tag : 'style',
45114             type : 'text/css',
45115             html : style
45116         });
45117
45118         return;
45119     }
45120     
45121     // hide stuff that is not compatible
45122     /**
45123      * @event blur
45124      * @hide
45125      */
45126     /**
45127      * @event change
45128      * @hide
45129      */
45130     /**
45131      * @event focus
45132      * @hide
45133      */
45134     /**
45135      * @event specialkey
45136      * @hide
45137      */
45138     /**
45139      * @cfg {String} fieldClass @hide
45140      */
45141     /**
45142      * @cfg {String} focusClass @hide
45143      */
45144     /**
45145      * @cfg {String} autoCreate @hide
45146      */
45147     /**
45148      * @cfg {String} inputType @hide
45149      */
45150     /**
45151      * @cfg {String} invalidClass @hide
45152      */
45153     /**
45154      * @cfg {String} invalidText @hide
45155      */
45156     /**
45157      * @cfg {String} msgFx @hide
45158      */
45159     /**
45160      * @cfg {String} validateOnBlur @hide
45161      */
45162 });
45163
45164 Roo.HtmlEditorCore.white = [
45165         'area', 'br', 'img', 'input', 'hr', 'wbr',
45166         
45167        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45168        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45169        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45170        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45171        'table',   'ul',         'xmp', 
45172        
45173        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45174       'thead',   'tr', 
45175      
45176       'dir', 'menu', 'ol', 'ul', 'dl',
45177        
45178       'embed',  'object'
45179 ];
45180
45181
45182 Roo.HtmlEditorCore.black = [
45183     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45184         'applet', // 
45185         'base',   'basefont', 'bgsound', 'blink',  'body', 
45186         'frame',  'frameset', 'head',    'html',   'ilayer', 
45187         'iframe', 'layer',  'link',     'meta',    'object',   
45188         'script', 'style' ,'title',  'xml' // clean later..
45189 ];
45190 Roo.HtmlEditorCore.clean = [
45191     'script', 'style', 'title', 'xml'
45192 ];
45193 Roo.HtmlEditorCore.remove = [
45194     'font'
45195 ];
45196 // attributes..
45197
45198 Roo.HtmlEditorCore.ablack = [
45199     'on'
45200 ];
45201     
45202 Roo.HtmlEditorCore.aclean = [ 
45203     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45204 ];
45205
45206 // protocols..
45207 Roo.HtmlEditorCore.pwhite= [
45208         'http',  'https',  'mailto'
45209 ];
45210
45211 // white listed style attributes.
45212 Roo.HtmlEditorCore.cwhite= [
45213       //  'text-align', /// default is to allow most things..
45214       
45215          
45216 //        'font-size'//??
45217 ];
45218
45219 // black listed style attributes.
45220 Roo.HtmlEditorCore.cblack= [
45221       //  'font-size' -- this can be set by the project 
45222 ];
45223
45224
45225 Roo.HtmlEditorCore.swapCodes   =[ 
45226     [    8211, "--" ], 
45227     [    8212, "--" ], 
45228     [    8216,  "'" ],  
45229     [    8217, "'" ],  
45230     [    8220, '"' ],  
45231     [    8221, '"' ],  
45232     [    8226, "*" ],  
45233     [    8230, "..." ]
45234 ]; 
45235
45236     //<script type="text/javascript">
45237
45238 /*
45239  * Ext JS Library 1.1.1
45240  * Copyright(c) 2006-2007, Ext JS, LLC.
45241  * Licence LGPL
45242  * 
45243  */
45244  
45245  
45246 Roo.form.HtmlEditor = function(config){
45247     
45248     
45249     
45250     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45251     
45252     if (!this.toolbars) {
45253         this.toolbars = [];
45254     }
45255     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45256     
45257     
45258 };
45259
45260 /**
45261  * @class Roo.form.HtmlEditor
45262  * @extends Roo.form.Field
45263  * Provides a lightweight HTML Editor component.
45264  *
45265  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45266  * 
45267  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45268  * supported by this editor.</b><br/><br/>
45269  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45270  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45271  */
45272 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45273     /**
45274      * @cfg {Boolean} clearUp
45275      */
45276     clearUp : true,
45277       /**
45278      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45279      */
45280     toolbars : false,
45281    
45282      /**
45283      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45284      *                        Roo.resizable.
45285      */
45286     resizable : false,
45287      /**
45288      * @cfg {Number} height (in pixels)
45289      */   
45290     height: 300,
45291    /**
45292      * @cfg {Number} width (in pixels)
45293      */   
45294     width: 500,
45295     
45296     /**
45297      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45298      * 
45299      */
45300     stylesheets: false,
45301     
45302     
45303      /**
45304      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45305      * 
45306      */
45307     cblack: false,
45308     /**
45309      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45310      * 
45311      */
45312     cwhite: false,
45313     
45314      /**
45315      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45316      * 
45317      */
45318     black: false,
45319     /**
45320      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45321      * 
45322      */
45323     white: false,
45324     
45325     // id of frame..
45326     frameId: false,
45327     
45328     // private properties
45329     validationEvent : false,
45330     deferHeight: true,
45331     initialized : false,
45332     activated : false,
45333     
45334     onFocus : Roo.emptyFn,
45335     iframePad:3,
45336     hideMode:'offsets',
45337     
45338     actionMode : 'container', // defaults to hiding it...
45339     
45340     defaultAutoCreate : { // modified by initCompnoent..
45341         tag: "textarea",
45342         style:"width:500px;height:300px;",
45343         autocomplete: "new-password"
45344     },
45345
45346     // private
45347     initComponent : function(){
45348         this.addEvents({
45349             /**
45350              * @event initialize
45351              * Fires when the editor is fully initialized (including the iframe)
45352              * @param {HtmlEditor} this
45353              */
45354             initialize: true,
45355             /**
45356              * @event activate
45357              * Fires when the editor is first receives the focus. Any insertion must wait
45358              * until after this event.
45359              * @param {HtmlEditor} this
45360              */
45361             activate: true,
45362              /**
45363              * @event beforesync
45364              * Fires before the textarea is updated with content from the editor iframe. Return false
45365              * to cancel the sync.
45366              * @param {HtmlEditor} this
45367              * @param {String} html
45368              */
45369             beforesync: true,
45370              /**
45371              * @event beforepush
45372              * Fires before the iframe editor is updated with content from the textarea. Return false
45373              * to cancel the push.
45374              * @param {HtmlEditor} this
45375              * @param {String} html
45376              */
45377             beforepush: true,
45378              /**
45379              * @event sync
45380              * Fires when the textarea is updated with content from the editor iframe.
45381              * @param {HtmlEditor} this
45382              * @param {String} html
45383              */
45384             sync: true,
45385              /**
45386              * @event push
45387              * Fires when the iframe editor is updated with content from the textarea.
45388              * @param {HtmlEditor} this
45389              * @param {String} html
45390              */
45391             push: true,
45392              /**
45393              * @event editmodechange
45394              * Fires when the editor switches edit modes
45395              * @param {HtmlEditor} this
45396              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45397              */
45398             editmodechange: true,
45399             /**
45400              * @event editorevent
45401              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45402              * @param {HtmlEditor} this
45403              */
45404             editorevent: true,
45405             /**
45406              * @event firstfocus
45407              * Fires when on first focus - needed by toolbars..
45408              * @param {HtmlEditor} this
45409              */
45410             firstfocus: true,
45411             /**
45412              * @event autosave
45413              * Auto save the htmlEditor value as a file into Events
45414              * @param {HtmlEditor} this
45415              */
45416             autosave: true,
45417             /**
45418              * @event savedpreview
45419              * preview the saved version of htmlEditor
45420              * @param {HtmlEditor} this
45421              */
45422             savedpreview: true,
45423             
45424             /**
45425             * @event stylesheetsclick
45426             * Fires when press the Sytlesheets button
45427             * @param {Roo.HtmlEditorCore} this
45428             */
45429             stylesheetsclick: true
45430         });
45431         this.defaultAutoCreate =  {
45432             tag: "textarea",
45433             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45434             autocomplete: "new-password"
45435         };
45436     },
45437
45438     /**
45439      * Protected method that will not generally be called directly. It
45440      * is called when the editor creates its toolbar. Override this method if you need to
45441      * add custom toolbar buttons.
45442      * @param {HtmlEditor} editor
45443      */
45444     createToolbar : function(editor){
45445         Roo.log("create toolbars");
45446         if (!editor.toolbars || !editor.toolbars.length) {
45447             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45448         }
45449         
45450         for (var i =0 ; i < editor.toolbars.length;i++) {
45451             editor.toolbars[i] = Roo.factory(
45452                     typeof(editor.toolbars[i]) == 'string' ?
45453                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45454                 Roo.form.HtmlEditor);
45455             editor.toolbars[i].init(editor);
45456         }
45457          
45458         
45459     },
45460
45461      
45462     // private
45463     onRender : function(ct, position)
45464     {
45465         var _t = this;
45466         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45467         
45468         this.wrap = this.el.wrap({
45469             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45470         });
45471         
45472         this.editorcore.onRender(ct, position);
45473          
45474         if (this.resizable) {
45475             this.resizeEl = new Roo.Resizable(this.wrap, {
45476                 pinned : true,
45477                 wrap: true,
45478                 dynamic : true,
45479                 minHeight : this.height,
45480                 height: this.height,
45481                 handles : this.resizable,
45482                 width: this.width,
45483                 listeners : {
45484                     resize : function(r, w, h) {
45485                         _t.onResize(w,h); // -something
45486                     }
45487                 }
45488             });
45489             
45490         }
45491         this.createToolbar(this);
45492        
45493         
45494         if(!this.width){
45495             this.setSize(this.wrap.getSize());
45496         }
45497         if (this.resizeEl) {
45498             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45499             // should trigger onReize..
45500         }
45501         
45502         this.keyNav = new Roo.KeyNav(this.el, {
45503             
45504             "tab" : function(e){
45505                 e.preventDefault();
45506                 
45507                 var value = this.getValue();
45508                 
45509                 var start = this.el.dom.selectionStart;
45510                 var end = this.el.dom.selectionEnd;
45511                 
45512                 if(!e.shiftKey){
45513                     
45514                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45515                     this.el.dom.setSelectionRange(end + 1, end + 1);
45516                     return;
45517                 }
45518                 
45519                 var f = value.substring(0, start).split("\t");
45520                 
45521                 if(f.pop().length != 0){
45522                     return;
45523                 }
45524                 
45525                 this.setValue(f.join("\t") + value.substring(end));
45526                 this.el.dom.setSelectionRange(start - 1, start - 1);
45527                 
45528             },
45529             
45530             "home" : function(e){
45531                 e.preventDefault();
45532                 
45533                 var curr = this.el.dom.selectionStart;
45534                 var lines = this.getValue().split("\n");
45535                 
45536                 if(!lines.length){
45537                     return;
45538                 }
45539                 
45540                 if(e.ctrlKey){
45541                     this.el.dom.setSelectionRange(0, 0);
45542                     return;
45543                 }
45544                 
45545                 var pos = 0;
45546                 
45547                 for (var i = 0; i < lines.length;i++) {
45548                     pos += lines[i].length;
45549                     
45550                     if(i != 0){
45551                         pos += 1;
45552                     }
45553                     
45554                     if(pos < curr){
45555                         continue;
45556                     }
45557                     
45558                     pos -= lines[i].length;
45559                     
45560                     break;
45561                 }
45562                 
45563                 if(!e.shiftKey){
45564                     this.el.dom.setSelectionRange(pos, pos);
45565                     return;
45566                 }
45567                 
45568                 this.el.dom.selectionStart = pos;
45569                 this.el.dom.selectionEnd = curr;
45570             },
45571             
45572             "end" : function(e){
45573                 e.preventDefault();
45574                 
45575                 var curr = this.el.dom.selectionStart;
45576                 var lines = this.getValue().split("\n");
45577                 
45578                 if(!lines.length){
45579                     return;
45580                 }
45581                 
45582                 if(e.ctrlKey){
45583                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45584                     return;
45585                 }
45586                 
45587                 var pos = 0;
45588                 
45589                 for (var i = 0; i < lines.length;i++) {
45590                     
45591                     pos += lines[i].length;
45592                     
45593                     if(i != 0){
45594                         pos += 1;
45595                     }
45596                     
45597                     if(pos < curr){
45598                         continue;
45599                     }
45600                     
45601                     break;
45602                 }
45603                 
45604                 if(!e.shiftKey){
45605                     this.el.dom.setSelectionRange(pos, pos);
45606                     return;
45607                 }
45608                 
45609                 this.el.dom.selectionStart = curr;
45610                 this.el.dom.selectionEnd = pos;
45611             },
45612
45613             scope : this,
45614
45615             doRelay : function(foo, bar, hname){
45616                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45617             },
45618
45619             forceKeyDown: true
45620         });
45621         
45622 //        if(this.autosave && this.w){
45623 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45624 //        }
45625     },
45626
45627     // private
45628     onResize : function(w, h)
45629     {
45630         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45631         var ew = false;
45632         var eh = false;
45633         
45634         if(this.el ){
45635             if(typeof w == 'number'){
45636                 var aw = w - this.wrap.getFrameWidth('lr');
45637                 this.el.setWidth(this.adjustWidth('textarea', aw));
45638                 ew = aw;
45639             }
45640             if(typeof h == 'number'){
45641                 var tbh = 0;
45642                 for (var i =0; i < this.toolbars.length;i++) {
45643                     // fixme - ask toolbars for heights?
45644                     tbh += this.toolbars[i].tb.el.getHeight();
45645                     if (this.toolbars[i].footer) {
45646                         tbh += this.toolbars[i].footer.el.getHeight();
45647                     }
45648                 }
45649                 
45650                 
45651                 
45652                 
45653                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45654                 ah -= 5; // knock a few pixes off for look..
45655 //                Roo.log(ah);
45656                 this.el.setHeight(this.adjustWidth('textarea', ah));
45657                 var eh = ah;
45658             }
45659         }
45660         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45661         this.editorcore.onResize(ew,eh);
45662         
45663     },
45664
45665     /**
45666      * Toggles the editor between standard and source edit mode.
45667      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45668      */
45669     toggleSourceEdit : function(sourceEditMode)
45670     {
45671         this.editorcore.toggleSourceEdit(sourceEditMode);
45672         
45673         if(this.editorcore.sourceEditMode){
45674             Roo.log('editor - showing textarea');
45675             
45676 //            Roo.log('in');
45677 //            Roo.log(this.syncValue());
45678             this.editorcore.syncValue();
45679             this.el.removeClass('x-hidden');
45680             this.el.dom.removeAttribute('tabIndex');
45681             this.el.focus();
45682             
45683             for (var i = 0; i < this.toolbars.length; i++) {
45684                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45685                     this.toolbars[i].tb.hide();
45686                     this.toolbars[i].footer.hide();
45687                 }
45688             }
45689             
45690         }else{
45691             Roo.log('editor - hiding textarea');
45692 //            Roo.log('out')
45693 //            Roo.log(this.pushValue()); 
45694             this.editorcore.pushValue();
45695             
45696             this.el.addClass('x-hidden');
45697             this.el.dom.setAttribute('tabIndex', -1);
45698             
45699             for (var i = 0; i < this.toolbars.length; i++) {
45700                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45701                     this.toolbars[i].tb.show();
45702                     this.toolbars[i].footer.show();
45703                 }
45704             }
45705             
45706             //this.deferFocus();
45707         }
45708         
45709         this.setSize(this.wrap.getSize());
45710         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45711         
45712         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45713     },
45714  
45715     // private (for BoxComponent)
45716     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45717
45718     // private (for BoxComponent)
45719     getResizeEl : function(){
45720         return this.wrap;
45721     },
45722
45723     // private (for BoxComponent)
45724     getPositionEl : function(){
45725         return this.wrap;
45726     },
45727
45728     // private
45729     initEvents : function(){
45730         this.originalValue = this.getValue();
45731     },
45732
45733     /**
45734      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45735      * @method
45736      */
45737     markInvalid : Roo.emptyFn,
45738     /**
45739      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45740      * @method
45741      */
45742     clearInvalid : Roo.emptyFn,
45743
45744     setValue : function(v){
45745         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45746         this.editorcore.pushValue();
45747     },
45748
45749      
45750     // private
45751     deferFocus : function(){
45752         this.focus.defer(10, this);
45753     },
45754
45755     // doc'ed in Field
45756     focus : function(){
45757         this.editorcore.focus();
45758         
45759     },
45760       
45761
45762     // private
45763     onDestroy : function(){
45764         
45765         
45766         
45767         if(this.rendered){
45768             
45769             for (var i =0; i < this.toolbars.length;i++) {
45770                 // fixme - ask toolbars for heights?
45771                 this.toolbars[i].onDestroy();
45772             }
45773             
45774             this.wrap.dom.innerHTML = '';
45775             this.wrap.remove();
45776         }
45777     },
45778
45779     // private
45780     onFirstFocus : function(){
45781         //Roo.log("onFirstFocus");
45782         this.editorcore.onFirstFocus();
45783          for (var i =0; i < this.toolbars.length;i++) {
45784             this.toolbars[i].onFirstFocus();
45785         }
45786         
45787     },
45788     
45789     // private
45790     syncValue : function()
45791     {
45792         this.editorcore.syncValue();
45793     },
45794     
45795     pushValue : function()
45796     {
45797         this.editorcore.pushValue();
45798     },
45799     
45800     setStylesheets : function(stylesheets)
45801     {
45802         this.editorcore.setStylesheets(stylesheets);
45803     },
45804     
45805     removeStylesheets : function()
45806     {
45807         this.editorcore.removeStylesheets();
45808     }
45809      
45810     
45811     // hide stuff that is not compatible
45812     /**
45813      * @event blur
45814      * @hide
45815      */
45816     /**
45817      * @event change
45818      * @hide
45819      */
45820     /**
45821      * @event focus
45822      * @hide
45823      */
45824     /**
45825      * @event specialkey
45826      * @hide
45827      */
45828     /**
45829      * @cfg {String} fieldClass @hide
45830      */
45831     /**
45832      * @cfg {String} focusClass @hide
45833      */
45834     /**
45835      * @cfg {String} autoCreate @hide
45836      */
45837     /**
45838      * @cfg {String} inputType @hide
45839      */
45840     /**
45841      * @cfg {String} invalidClass @hide
45842      */
45843     /**
45844      * @cfg {String} invalidText @hide
45845      */
45846     /**
45847      * @cfg {String} msgFx @hide
45848      */
45849     /**
45850      * @cfg {String} validateOnBlur @hide
45851      */
45852 });
45853  
45854     // <script type="text/javascript">
45855 /*
45856  * Based on
45857  * Ext JS Library 1.1.1
45858  * Copyright(c) 2006-2007, Ext JS, LLC.
45859  *  
45860  
45861  */
45862
45863 /**
45864  * @class Roo.form.HtmlEditorToolbar1
45865  * Basic Toolbar
45866  * 
45867  * Usage:
45868  *
45869  new Roo.form.HtmlEditor({
45870     ....
45871     toolbars : [
45872         new Roo.form.HtmlEditorToolbar1({
45873             disable : { fonts: 1 , format: 1, ..., ... , ...],
45874             btns : [ .... ]
45875         })
45876     }
45877      
45878  * 
45879  * @cfg {Object} disable List of elements to disable..
45880  * @cfg {Array} btns List of additional buttons.
45881  * 
45882  * 
45883  * NEEDS Extra CSS? 
45884  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45885  */
45886  
45887 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45888 {
45889     
45890     Roo.apply(this, config);
45891     
45892     // default disabled, based on 'good practice'..
45893     this.disable = this.disable || {};
45894     Roo.applyIf(this.disable, {
45895         fontSize : true,
45896         colors : true,
45897         specialElements : true
45898     });
45899     
45900     
45901     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45902     // dont call parent... till later.
45903 }
45904
45905 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45906     
45907     tb: false,
45908     
45909     rendered: false,
45910     
45911     editor : false,
45912     editorcore : false,
45913     /**
45914      * @cfg {Object} disable  List of toolbar elements to disable
45915          
45916      */
45917     disable : false,
45918     
45919     
45920      /**
45921      * @cfg {String} createLinkText The default text for the create link prompt
45922      */
45923     createLinkText : 'Please enter the URL for the link:',
45924     /**
45925      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45926      */
45927     defaultLinkValue : 'http:/'+'/',
45928    
45929     
45930       /**
45931      * @cfg {Array} fontFamilies An array of available font families
45932      */
45933     fontFamilies : [
45934         'Arial',
45935         'Courier New',
45936         'Tahoma',
45937         'Times New Roman',
45938         'Verdana'
45939     ],
45940     
45941     specialChars : [
45942            "&#169;",
45943           "&#174;",     
45944           "&#8482;",    
45945           "&#163;" ,    
45946          // "&#8212;",    
45947           "&#8230;",    
45948           "&#247;" ,    
45949         //  "&#225;" ,     ?? a acute?
45950            "&#8364;"    , //Euro
45951        //   "&#8220;"    ,
45952         //  "&#8221;"    ,
45953         //  "&#8226;"    ,
45954           "&#176;"  //   , // degrees
45955
45956          // "&#233;"     , // e ecute
45957          // "&#250;"     , // u ecute?
45958     ],
45959     
45960     specialElements : [
45961         {
45962             text: "Insert Table",
45963             xtype: 'MenuItem',
45964             xns : Roo.Menu,
45965             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45966                 
45967         },
45968         {    
45969             text: "Insert Image",
45970             xtype: 'MenuItem',
45971             xns : Roo.Menu,
45972             ihtml : '<img src="about:blank"/>'
45973             
45974         }
45975         
45976          
45977     ],
45978     
45979     
45980     inputElements : [ 
45981             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45982             "input:submit", "input:button", "select", "textarea", "label" ],
45983     formats : [
45984         ["p"] ,  
45985         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45986         ["pre"],[ "code"], 
45987         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45988         ['div'],['span'],
45989         ['sup'],['sub']
45990     ],
45991     
45992     cleanStyles : [
45993         "font-size"
45994     ],
45995      /**
45996      * @cfg {String} defaultFont default font to use.
45997      */
45998     defaultFont: 'tahoma',
45999    
46000     fontSelect : false,
46001     
46002     
46003     formatCombo : false,
46004     
46005     init : function(editor)
46006     {
46007         this.editor = editor;
46008         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46009         var editorcore = this.editorcore;
46010         
46011         var _t = this;
46012         
46013         var fid = editorcore.frameId;
46014         var etb = this;
46015         function btn(id, toggle, handler){
46016             var xid = fid + '-'+ id ;
46017             return {
46018                 id : xid,
46019                 cmd : id,
46020                 cls : 'x-btn-icon x-edit-'+id,
46021                 enableToggle:toggle !== false,
46022                 scope: _t, // was editor...
46023                 handler:handler||_t.relayBtnCmd,
46024                 clickEvent:'mousedown',
46025                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46026                 tabIndex:-1
46027             };
46028         }
46029         
46030         
46031         
46032         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46033         this.tb = tb;
46034          // stop form submits
46035         tb.el.on('click', function(e){
46036             e.preventDefault(); // what does this do?
46037         });
46038
46039         if(!this.disable.font) { // && !Roo.isSafari){
46040             /* why no safari for fonts 
46041             editor.fontSelect = tb.el.createChild({
46042                 tag:'select',
46043                 tabIndex: -1,
46044                 cls:'x-font-select',
46045                 html: this.createFontOptions()
46046             });
46047             
46048             editor.fontSelect.on('change', function(){
46049                 var font = editor.fontSelect.dom.value;
46050                 editor.relayCmd('fontname', font);
46051                 editor.deferFocus();
46052             }, editor);
46053             
46054             tb.add(
46055                 editor.fontSelect.dom,
46056                 '-'
46057             );
46058             */
46059             
46060         };
46061         if(!this.disable.formats){
46062             this.formatCombo = new Roo.form.ComboBox({
46063                 store: new Roo.data.SimpleStore({
46064                     id : 'tag',
46065                     fields: ['tag'],
46066                     data : this.formats // from states.js
46067                 }),
46068                 blockFocus : true,
46069                 name : '',
46070                 //autoCreate : {tag: "div",  size: "20"},
46071                 displayField:'tag',
46072                 typeAhead: false,
46073                 mode: 'local',
46074                 editable : false,
46075                 triggerAction: 'all',
46076                 emptyText:'Add tag',
46077                 selectOnFocus:true,
46078                 width:135,
46079                 listeners : {
46080                     'select': function(c, r, i) {
46081                         editorcore.insertTag(r.get('tag'));
46082                         editor.focus();
46083                     }
46084                 }
46085
46086             });
46087             tb.addField(this.formatCombo);
46088             
46089         }
46090         
46091         if(!this.disable.format){
46092             tb.add(
46093                 btn('bold'),
46094                 btn('italic'),
46095                 btn('underline'),
46096                 btn('strikethrough')
46097             );
46098         };
46099         if(!this.disable.fontSize){
46100             tb.add(
46101                 '-',
46102                 
46103                 
46104                 btn('increasefontsize', false, editorcore.adjustFont),
46105                 btn('decreasefontsize', false, editorcore.adjustFont)
46106             );
46107         };
46108         
46109         
46110         if(!this.disable.colors){
46111             tb.add(
46112                 '-', {
46113                     id:editorcore.frameId +'-forecolor',
46114                     cls:'x-btn-icon x-edit-forecolor',
46115                     clickEvent:'mousedown',
46116                     tooltip: this.buttonTips['forecolor'] || undefined,
46117                     tabIndex:-1,
46118                     menu : new Roo.menu.ColorMenu({
46119                         allowReselect: true,
46120                         focus: Roo.emptyFn,
46121                         value:'000000',
46122                         plain:true,
46123                         selectHandler: function(cp, color){
46124                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46125                             editor.deferFocus();
46126                         },
46127                         scope: editorcore,
46128                         clickEvent:'mousedown'
46129                     })
46130                 }, {
46131                     id:editorcore.frameId +'backcolor',
46132                     cls:'x-btn-icon x-edit-backcolor',
46133                     clickEvent:'mousedown',
46134                     tooltip: this.buttonTips['backcolor'] || undefined,
46135                     tabIndex:-1,
46136                     menu : new Roo.menu.ColorMenu({
46137                         focus: Roo.emptyFn,
46138                         value:'FFFFFF',
46139                         plain:true,
46140                         allowReselect: true,
46141                         selectHandler: function(cp, color){
46142                             if(Roo.isGecko){
46143                                 editorcore.execCmd('useCSS', false);
46144                                 editorcore.execCmd('hilitecolor', color);
46145                                 editorcore.execCmd('useCSS', true);
46146                                 editor.deferFocus();
46147                             }else{
46148                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46149                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46150                                 editor.deferFocus();
46151                             }
46152                         },
46153                         scope:editorcore,
46154                         clickEvent:'mousedown'
46155                     })
46156                 }
46157             );
46158         };
46159         // now add all the items...
46160         
46161
46162         if(!this.disable.alignments){
46163             tb.add(
46164                 '-',
46165                 btn('justifyleft'),
46166                 btn('justifycenter'),
46167                 btn('justifyright')
46168             );
46169         };
46170
46171         //if(!Roo.isSafari){
46172             if(!this.disable.links){
46173                 tb.add(
46174                     '-',
46175                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46176                 );
46177             };
46178
46179             if(!this.disable.lists){
46180                 tb.add(
46181                     '-',
46182                     btn('insertorderedlist'),
46183                     btn('insertunorderedlist')
46184                 );
46185             }
46186             if(!this.disable.sourceEdit){
46187                 tb.add(
46188                     '-',
46189                     btn('sourceedit', true, function(btn){
46190                         this.toggleSourceEdit(btn.pressed);
46191                     })
46192                 );
46193             }
46194         //}
46195         
46196         var smenu = { };
46197         // special menu.. - needs to be tidied up..
46198         if (!this.disable.special) {
46199             smenu = {
46200                 text: "&#169;",
46201                 cls: 'x-edit-none',
46202                 
46203                 menu : {
46204                     items : []
46205                 }
46206             };
46207             for (var i =0; i < this.specialChars.length; i++) {
46208                 smenu.menu.items.push({
46209                     
46210                     html: this.specialChars[i],
46211                     handler: function(a,b) {
46212                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46213                         //editor.insertAtCursor(a.html);
46214                         
46215                     },
46216                     tabIndex:-1
46217                 });
46218             }
46219             
46220             
46221             tb.add(smenu);
46222             
46223             
46224         }
46225         
46226         var cmenu = { };
46227         if (!this.disable.cleanStyles) {
46228             cmenu = {
46229                 cls: 'x-btn-icon x-btn-clear',
46230                 
46231                 menu : {
46232                     items : []
46233                 }
46234             };
46235             for (var i =0; i < this.cleanStyles.length; i++) {
46236                 cmenu.menu.items.push({
46237                     actiontype : this.cleanStyles[i],
46238                     html: 'Remove ' + this.cleanStyles[i],
46239                     handler: function(a,b) {
46240 //                        Roo.log(a);
46241 //                        Roo.log(b);
46242                         var c = Roo.get(editorcore.doc.body);
46243                         c.select('[style]').each(function(s) {
46244                             s.dom.style.removeProperty(a.actiontype);
46245                         });
46246                         editorcore.syncValue();
46247                     },
46248                     tabIndex:-1
46249                 });
46250             }
46251              cmenu.menu.items.push({
46252                 actiontype : 'tablewidths',
46253                 html: 'Remove Table Widths',
46254                 handler: function(a,b) {
46255                     editorcore.cleanTableWidths();
46256                     editorcore.syncValue();
46257                 },
46258                 tabIndex:-1
46259             });
46260             cmenu.menu.items.push({
46261                 actiontype : 'word',
46262                 html: 'Remove MS Word Formating',
46263                 handler: function(a,b) {
46264                     editorcore.cleanWord();
46265                     editorcore.syncValue();
46266                 },
46267                 tabIndex:-1
46268             });
46269             
46270             cmenu.menu.items.push({
46271                 actiontype : 'all',
46272                 html: 'Remove All Styles',
46273                 handler: function(a,b) {
46274                     
46275                     var c = Roo.get(editorcore.doc.body);
46276                     c.select('[style]').each(function(s) {
46277                         s.dom.removeAttribute('style');
46278                     });
46279                     editorcore.syncValue();
46280                 },
46281                 tabIndex:-1
46282             });
46283             
46284             cmenu.menu.items.push({
46285                 actiontype : 'all',
46286                 html: 'Remove All CSS Classes',
46287                 handler: function(a,b) {
46288                     
46289                     var c = Roo.get(editorcore.doc.body);
46290                     c.select('[class]').each(function(s) {
46291                         s.dom.removeAttribute('class');
46292                     });
46293                     editorcore.cleanWord();
46294                     editorcore.syncValue();
46295                 },
46296                 tabIndex:-1
46297             });
46298             
46299              cmenu.menu.items.push({
46300                 actiontype : 'tidy',
46301                 html: 'Tidy HTML Source',
46302                 handler: function(a,b) {
46303                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46304                     editorcore.syncValue();
46305                 },
46306                 tabIndex:-1
46307             });
46308             
46309             
46310             tb.add(cmenu);
46311         }
46312          
46313         if (!this.disable.specialElements) {
46314             var semenu = {
46315                 text: "Other;",
46316                 cls: 'x-edit-none',
46317                 menu : {
46318                     items : []
46319                 }
46320             };
46321             for (var i =0; i < this.specialElements.length; i++) {
46322                 semenu.menu.items.push(
46323                     Roo.apply({ 
46324                         handler: function(a,b) {
46325                             editor.insertAtCursor(this.ihtml);
46326                         }
46327                     }, this.specialElements[i])
46328                 );
46329                     
46330             }
46331             
46332             tb.add(semenu);
46333             
46334             
46335         }
46336          
46337         
46338         if (this.btns) {
46339             for(var i =0; i< this.btns.length;i++) {
46340                 var b = Roo.factory(this.btns[i],Roo.form);
46341                 b.cls =  'x-edit-none';
46342                 
46343                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46344                     b.cls += ' x-init-enable';
46345                 }
46346                 
46347                 b.scope = editorcore;
46348                 tb.add(b);
46349             }
46350         
46351         }
46352         
46353         
46354         
46355         // disable everything...
46356         
46357         this.tb.items.each(function(item){
46358             
46359            if(
46360                 item.id != editorcore.frameId+ '-sourceedit' && 
46361                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46362             ){
46363                 
46364                 item.disable();
46365             }
46366         });
46367         this.rendered = true;
46368         
46369         // the all the btns;
46370         editor.on('editorevent', this.updateToolbar, this);
46371         // other toolbars need to implement this..
46372         //editor.on('editmodechange', this.updateToolbar, this);
46373     },
46374     
46375     
46376     relayBtnCmd : function(btn) {
46377         this.editorcore.relayCmd(btn.cmd);
46378     },
46379     // private used internally
46380     createLink : function(){
46381         Roo.log("create link?");
46382         var url = prompt(this.createLinkText, this.defaultLinkValue);
46383         if(url && url != 'http:/'+'/'){
46384             this.editorcore.relayCmd('createlink', url);
46385         }
46386     },
46387
46388     
46389     /**
46390      * Protected method that will not generally be called directly. It triggers
46391      * a toolbar update by reading the markup state of the current selection in the editor.
46392      */
46393     updateToolbar: function(){
46394
46395         if(!this.editorcore.activated){
46396             this.editor.onFirstFocus();
46397             return;
46398         }
46399
46400         var btns = this.tb.items.map, 
46401             doc = this.editorcore.doc,
46402             frameId = this.editorcore.frameId;
46403
46404         if(!this.disable.font && !Roo.isSafari){
46405             /*
46406             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46407             if(name != this.fontSelect.dom.value){
46408                 this.fontSelect.dom.value = name;
46409             }
46410             */
46411         }
46412         if(!this.disable.format){
46413             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46414             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46415             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46416             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46417         }
46418         if(!this.disable.alignments){
46419             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46420             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46421             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46422         }
46423         if(!Roo.isSafari && !this.disable.lists){
46424             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46425             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46426         }
46427         
46428         var ans = this.editorcore.getAllAncestors();
46429         if (this.formatCombo) {
46430             
46431             
46432             var store = this.formatCombo.store;
46433             this.formatCombo.setValue("");
46434             for (var i =0; i < ans.length;i++) {
46435                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46436                     // select it..
46437                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46438                     break;
46439                 }
46440             }
46441         }
46442         
46443         
46444         
46445         // hides menus... - so this cant be on a menu...
46446         Roo.menu.MenuMgr.hideAll();
46447
46448         //this.editorsyncValue();
46449     },
46450    
46451     
46452     createFontOptions : function(){
46453         var buf = [], fs = this.fontFamilies, ff, lc;
46454         
46455         
46456         
46457         for(var i = 0, len = fs.length; i< len; i++){
46458             ff = fs[i];
46459             lc = ff.toLowerCase();
46460             buf.push(
46461                 '<option value="',lc,'" style="font-family:',ff,';"',
46462                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46463                     ff,
46464                 '</option>'
46465             );
46466         }
46467         return buf.join('');
46468     },
46469     
46470     toggleSourceEdit : function(sourceEditMode){
46471         
46472         Roo.log("toolbar toogle");
46473         if(sourceEditMode === undefined){
46474             sourceEditMode = !this.sourceEditMode;
46475         }
46476         this.sourceEditMode = sourceEditMode === true;
46477         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46478         // just toggle the button?
46479         if(btn.pressed !== this.sourceEditMode){
46480             btn.toggle(this.sourceEditMode);
46481             return;
46482         }
46483         
46484         if(sourceEditMode){
46485             Roo.log("disabling buttons");
46486             this.tb.items.each(function(item){
46487                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46488                     item.disable();
46489                 }
46490             });
46491           
46492         }else{
46493             Roo.log("enabling buttons");
46494             if(this.editorcore.initialized){
46495                 this.tb.items.each(function(item){
46496                     item.enable();
46497                 });
46498             }
46499             
46500         }
46501         Roo.log("calling toggole on editor");
46502         // tell the editor that it's been pressed..
46503         this.editor.toggleSourceEdit(sourceEditMode);
46504        
46505     },
46506      /**
46507      * Object collection of toolbar tooltips for the buttons in the editor. The key
46508      * is the command id associated with that button and the value is a valid QuickTips object.
46509      * For example:
46510 <pre><code>
46511 {
46512     bold : {
46513         title: 'Bold (Ctrl+B)',
46514         text: 'Make the selected text bold.',
46515         cls: 'x-html-editor-tip'
46516     },
46517     italic : {
46518         title: 'Italic (Ctrl+I)',
46519         text: 'Make the selected text italic.',
46520         cls: 'x-html-editor-tip'
46521     },
46522     ...
46523 </code></pre>
46524     * @type Object
46525      */
46526     buttonTips : {
46527         bold : {
46528             title: 'Bold (Ctrl+B)',
46529             text: 'Make the selected text bold.',
46530             cls: 'x-html-editor-tip'
46531         },
46532         italic : {
46533             title: 'Italic (Ctrl+I)',
46534             text: 'Make the selected text italic.',
46535             cls: 'x-html-editor-tip'
46536         },
46537         underline : {
46538             title: 'Underline (Ctrl+U)',
46539             text: 'Underline the selected text.',
46540             cls: 'x-html-editor-tip'
46541         },
46542         strikethrough : {
46543             title: 'Strikethrough',
46544             text: 'Strikethrough the selected text.',
46545             cls: 'x-html-editor-tip'
46546         },
46547         increasefontsize : {
46548             title: 'Grow Text',
46549             text: 'Increase the font size.',
46550             cls: 'x-html-editor-tip'
46551         },
46552         decreasefontsize : {
46553             title: 'Shrink Text',
46554             text: 'Decrease the font size.',
46555             cls: 'x-html-editor-tip'
46556         },
46557         backcolor : {
46558             title: 'Text Highlight Color',
46559             text: 'Change the background color of the selected text.',
46560             cls: 'x-html-editor-tip'
46561         },
46562         forecolor : {
46563             title: 'Font Color',
46564             text: 'Change the color of the selected text.',
46565             cls: 'x-html-editor-tip'
46566         },
46567         justifyleft : {
46568             title: 'Align Text Left',
46569             text: 'Align text to the left.',
46570             cls: 'x-html-editor-tip'
46571         },
46572         justifycenter : {
46573             title: 'Center Text',
46574             text: 'Center text in the editor.',
46575             cls: 'x-html-editor-tip'
46576         },
46577         justifyright : {
46578             title: 'Align Text Right',
46579             text: 'Align text to the right.',
46580             cls: 'x-html-editor-tip'
46581         },
46582         insertunorderedlist : {
46583             title: 'Bullet List',
46584             text: 'Start a bulleted list.',
46585             cls: 'x-html-editor-tip'
46586         },
46587         insertorderedlist : {
46588             title: 'Numbered List',
46589             text: 'Start a numbered list.',
46590             cls: 'x-html-editor-tip'
46591         },
46592         createlink : {
46593             title: 'Hyperlink',
46594             text: 'Make the selected text a hyperlink.',
46595             cls: 'x-html-editor-tip'
46596         },
46597         sourceedit : {
46598             title: 'Source Edit',
46599             text: 'Switch to source editing mode.',
46600             cls: 'x-html-editor-tip'
46601         }
46602     },
46603     // private
46604     onDestroy : function(){
46605         if(this.rendered){
46606             
46607             this.tb.items.each(function(item){
46608                 if(item.menu){
46609                     item.menu.removeAll();
46610                     if(item.menu.el){
46611                         item.menu.el.destroy();
46612                     }
46613                 }
46614                 item.destroy();
46615             });
46616              
46617         }
46618     },
46619     onFirstFocus: function() {
46620         this.tb.items.each(function(item){
46621            item.enable();
46622         });
46623     }
46624 });
46625
46626
46627
46628
46629 // <script type="text/javascript">
46630 /*
46631  * Based on
46632  * Ext JS Library 1.1.1
46633  * Copyright(c) 2006-2007, Ext JS, LLC.
46634  *  
46635  
46636  */
46637
46638  
46639 /**
46640  * @class Roo.form.HtmlEditor.ToolbarContext
46641  * Context Toolbar
46642  * 
46643  * Usage:
46644  *
46645  new Roo.form.HtmlEditor({
46646     ....
46647     toolbars : [
46648         { xtype: 'ToolbarStandard', styles : {} }
46649         { xtype: 'ToolbarContext', disable : {} }
46650     ]
46651 })
46652
46653      
46654  * 
46655  * @config : {Object} disable List of elements to disable.. (not done yet.)
46656  * @config : {Object} styles  Map of styles available.
46657  * 
46658  */
46659
46660 Roo.form.HtmlEditor.ToolbarContext = function(config)
46661 {
46662     
46663     Roo.apply(this, config);
46664     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46665     // dont call parent... till later.
46666     this.styles = this.styles || {};
46667 }
46668
46669  
46670
46671 Roo.form.HtmlEditor.ToolbarContext.types = {
46672     'IMG' : {
46673         width : {
46674             title: "Width",
46675             width: 40
46676         },
46677         height:  {
46678             title: "Height",
46679             width: 40
46680         },
46681         align: {
46682             title: "Align",
46683             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46684             width : 80
46685             
46686         },
46687         border: {
46688             title: "Border",
46689             width: 40
46690         },
46691         alt: {
46692             title: "Alt",
46693             width: 120
46694         },
46695         src : {
46696             title: "Src",
46697             width: 220
46698         }
46699         
46700     },
46701     'A' : {
46702         name : {
46703             title: "Name",
46704             width: 50
46705         },
46706         target:  {
46707             title: "Target",
46708             width: 120
46709         },
46710         href:  {
46711             title: "Href",
46712             width: 220
46713         } // border?
46714         
46715     },
46716     'TABLE' : {
46717         rows : {
46718             title: "Rows",
46719             width: 20
46720         },
46721         cols : {
46722             title: "Cols",
46723             width: 20
46724         },
46725         width : {
46726             title: "Width",
46727             width: 40
46728         },
46729         height : {
46730             title: "Height",
46731             width: 40
46732         },
46733         border : {
46734             title: "Border",
46735             width: 20
46736         }
46737     },
46738     'TD' : {
46739         width : {
46740             title: "Width",
46741             width: 40
46742         },
46743         height : {
46744             title: "Height",
46745             width: 40
46746         },   
46747         align: {
46748             title: "Align",
46749             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46750             width: 80
46751         },
46752         valign: {
46753             title: "Valign",
46754             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46755             width: 80
46756         },
46757         colspan: {
46758             title: "Colspan",
46759             width: 20
46760             
46761         },
46762          'font-family'  : {
46763             title : "Font",
46764             style : 'fontFamily',
46765             displayField: 'display',
46766             optname : 'font-family',
46767             width: 140
46768         }
46769     },
46770     'INPUT' : {
46771         name : {
46772             title: "name",
46773             width: 120
46774         },
46775         value : {
46776             title: "Value",
46777             width: 120
46778         },
46779         width : {
46780             title: "Width",
46781             width: 40
46782         }
46783     },
46784     'LABEL' : {
46785         'for' : {
46786             title: "For",
46787             width: 120
46788         }
46789     },
46790     'TEXTAREA' : {
46791           name : {
46792             title: "name",
46793             width: 120
46794         },
46795         rows : {
46796             title: "Rows",
46797             width: 20
46798         },
46799         cols : {
46800             title: "Cols",
46801             width: 20
46802         }
46803     },
46804     'SELECT' : {
46805         name : {
46806             title: "name",
46807             width: 120
46808         },
46809         selectoptions : {
46810             title: "Options",
46811             width: 200
46812         }
46813     },
46814     
46815     // should we really allow this??
46816     // should this just be 
46817     'BODY' : {
46818         title : {
46819             title: "Title",
46820             width: 200,
46821             disabled : true
46822         }
46823     },
46824     'SPAN' : {
46825         'font-family'  : {
46826             title : "Font",
46827             style : 'fontFamily',
46828             displayField: 'display',
46829             optname : 'font-family',
46830             width: 140
46831         }
46832     },
46833     'DIV' : {
46834         'font-family'  : {
46835             title : "Font",
46836             style : 'fontFamily',
46837             displayField: 'display',
46838             optname : 'font-family',
46839             width: 140
46840         }
46841     },
46842      'P' : {
46843         'font-family'  : {
46844             title : "Font",
46845             style : 'fontFamily',
46846             displayField: 'display',
46847             optname : 'font-family',
46848             width: 140
46849         }
46850     },
46851     
46852     '*' : {
46853         // empty..
46854     }
46855
46856 };
46857
46858 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46859 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46860
46861 Roo.form.HtmlEditor.ToolbarContext.options = {
46862         'font-family'  : [ 
46863                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46864                 [ 'Courier New', 'Courier New'],
46865                 [ 'Tahoma', 'Tahoma'],
46866                 [ 'Times New Roman,serif', 'Times'],
46867                 [ 'Verdana','Verdana' ]
46868         ]
46869 };
46870
46871 // fixme - these need to be configurable..
46872  
46873
46874 //Roo.form.HtmlEditor.ToolbarContext.types
46875
46876
46877 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46878     
46879     tb: false,
46880     
46881     rendered: false,
46882     
46883     editor : false,
46884     editorcore : false,
46885     /**
46886      * @cfg {Object} disable  List of toolbar elements to disable
46887          
46888      */
46889     disable : false,
46890     /**
46891      * @cfg {Object} styles List of styles 
46892      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46893      *
46894      * These must be defined in the page, so they get rendered correctly..
46895      * .headline { }
46896      * TD.underline { }
46897      * 
46898      */
46899     styles : false,
46900     
46901     options: false,
46902     
46903     toolbars : false,
46904     
46905     init : function(editor)
46906     {
46907         this.editor = editor;
46908         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46909         var editorcore = this.editorcore;
46910         
46911         var fid = editorcore.frameId;
46912         var etb = this;
46913         function btn(id, toggle, handler){
46914             var xid = fid + '-'+ id ;
46915             return {
46916                 id : xid,
46917                 cmd : id,
46918                 cls : 'x-btn-icon x-edit-'+id,
46919                 enableToggle:toggle !== false,
46920                 scope: editorcore, // was editor...
46921                 handler:handler||editorcore.relayBtnCmd,
46922                 clickEvent:'mousedown',
46923                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46924                 tabIndex:-1
46925             };
46926         }
46927         // create a new element.
46928         var wdiv = editor.wrap.createChild({
46929                 tag: 'div'
46930             }, editor.wrap.dom.firstChild.nextSibling, true);
46931         
46932         // can we do this more than once??
46933         
46934          // stop form submits
46935       
46936  
46937         // disable everything...
46938         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46939         this.toolbars = {};
46940            
46941         for (var i in  ty) {
46942           
46943             this.toolbars[i] = this.buildToolbar(ty[i],i);
46944         }
46945         this.tb = this.toolbars.BODY;
46946         this.tb.el.show();
46947         this.buildFooter();
46948         this.footer.show();
46949         editor.on('hide', function( ) { this.footer.hide() }, this);
46950         editor.on('show', function( ) { this.footer.show() }, this);
46951         
46952          
46953         this.rendered = true;
46954         
46955         // the all the btns;
46956         editor.on('editorevent', this.updateToolbar, this);
46957         // other toolbars need to implement this..
46958         //editor.on('editmodechange', this.updateToolbar, this);
46959     },
46960     
46961     
46962     
46963     /**
46964      * Protected method that will not generally be called directly. It triggers
46965      * a toolbar update by reading the markup state of the current selection in the editor.
46966      *
46967      * Note you can force an update by calling on('editorevent', scope, false)
46968      */
46969     updateToolbar: function(editor,ev,sel){
46970
46971         //Roo.log(ev);
46972         // capture mouse up - this is handy for selecting images..
46973         // perhaps should go somewhere else...
46974         if(!this.editorcore.activated){
46975              this.editor.onFirstFocus();
46976             return;
46977         }
46978         
46979         
46980         
46981         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46982         // selectNode - might want to handle IE?
46983         if (ev &&
46984             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46985             ev.target && ev.target.tagName == 'IMG') {
46986             // they have click on an image...
46987             // let's see if we can change the selection...
46988             sel = ev.target;
46989          
46990               var nodeRange = sel.ownerDocument.createRange();
46991             try {
46992                 nodeRange.selectNode(sel);
46993             } catch (e) {
46994                 nodeRange.selectNodeContents(sel);
46995             }
46996             //nodeRange.collapse(true);
46997             var s = this.editorcore.win.getSelection();
46998             s.removeAllRanges();
46999             s.addRange(nodeRange);
47000         }  
47001         
47002       
47003         var updateFooter = sel ? false : true;
47004         
47005         
47006         var ans = this.editorcore.getAllAncestors();
47007         
47008         // pick
47009         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47010         
47011         if (!sel) { 
47012             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47013             sel = sel ? sel : this.editorcore.doc.body;
47014             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47015             
47016         }
47017         // pick a menu that exists..
47018         var tn = sel.tagName.toUpperCase();
47019         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47020         
47021         tn = sel.tagName.toUpperCase();
47022         
47023         var lastSel = this.tb.selectedNode;
47024         
47025         this.tb.selectedNode = sel;
47026         
47027         // if current menu does not match..
47028         
47029         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47030                 
47031             this.tb.el.hide();
47032             ///console.log("show: " + tn);
47033             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47034             this.tb.el.show();
47035             // update name
47036             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47037             
47038             
47039             // update attributes
47040             if (this.tb.fields) {
47041                 this.tb.fields.each(function(e) {
47042                     if (e.stylename) {
47043                         e.setValue(sel.style[e.stylename]);
47044                         return;
47045                     } 
47046                    e.setValue(sel.getAttribute(e.attrname));
47047                 });
47048             }
47049             
47050             var hasStyles = false;
47051             for(var i in this.styles) {
47052                 hasStyles = true;
47053                 break;
47054             }
47055             
47056             // update styles
47057             if (hasStyles) { 
47058                 var st = this.tb.fields.item(0);
47059                 
47060                 st.store.removeAll();
47061                
47062                 
47063                 var cn = sel.className.split(/\s+/);
47064                 
47065                 var avs = [];
47066                 if (this.styles['*']) {
47067                     
47068                     Roo.each(this.styles['*'], function(v) {
47069                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47070                     });
47071                 }
47072                 if (this.styles[tn]) { 
47073                     Roo.each(this.styles[tn], function(v) {
47074                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47075                     });
47076                 }
47077                 
47078                 st.store.loadData(avs);
47079                 st.collapse();
47080                 st.setValue(cn);
47081             }
47082             // flag our selected Node.
47083             this.tb.selectedNode = sel;
47084            
47085            
47086             Roo.menu.MenuMgr.hideAll();
47087
47088         }
47089         
47090         if (!updateFooter) {
47091             //this.footDisp.dom.innerHTML = ''; 
47092             return;
47093         }
47094         // update the footer
47095         //
47096         var html = '';
47097         
47098         this.footerEls = ans.reverse();
47099         Roo.each(this.footerEls, function(a,i) {
47100             if (!a) { return; }
47101             html += html.length ? ' &gt; '  :  '';
47102             
47103             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47104             
47105         });
47106        
47107         // 
47108         var sz = this.footDisp.up('td').getSize();
47109         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47110         this.footDisp.dom.style.marginLeft = '5px';
47111         
47112         this.footDisp.dom.style.overflow = 'hidden';
47113         
47114         this.footDisp.dom.innerHTML = html;
47115             
47116         //this.editorsyncValue();
47117     },
47118      
47119     
47120    
47121        
47122     // private
47123     onDestroy : function(){
47124         if(this.rendered){
47125             
47126             this.tb.items.each(function(item){
47127                 if(item.menu){
47128                     item.menu.removeAll();
47129                     if(item.menu.el){
47130                         item.menu.el.destroy();
47131                     }
47132                 }
47133                 item.destroy();
47134             });
47135              
47136         }
47137     },
47138     onFirstFocus: function() {
47139         // need to do this for all the toolbars..
47140         this.tb.items.each(function(item){
47141            item.enable();
47142         });
47143     },
47144     buildToolbar: function(tlist, nm)
47145     {
47146         var editor = this.editor;
47147         var editorcore = this.editorcore;
47148          // create a new element.
47149         var wdiv = editor.wrap.createChild({
47150                 tag: 'div'
47151             }, editor.wrap.dom.firstChild.nextSibling, true);
47152         
47153        
47154         var tb = new Roo.Toolbar(wdiv);
47155         // add the name..
47156         
47157         tb.add(nm+ ":&nbsp;");
47158         
47159         var styles = [];
47160         for(var i in this.styles) {
47161             styles.push(i);
47162         }
47163         
47164         // styles...
47165         if (styles && styles.length) {
47166             
47167             // this needs a multi-select checkbox...
47168             tb.addField( new Roo.form.ComboBox({
47169                 store: new Roo.data.SimpleStore({
47170                     id : 'val',
47171                     fields: ['val', 'selected'],
47172                     data : [] 
47173                 }),
47174                 name : '-roo-edit-className',
47175                 attrname : 'className',
47176                 displayField: 'val',
47177                 typeAhead: false,
47178                 mode: 'local',
47179                 editable : false,
47180                 triggerAction: 'all',
47181                 emptyText:'Select Style',
47182                 selectOnFocus:true,
47183                 width: 130,
47184                 listeners : {
47185                     'select': function(c, r, i) {
47186                         // initial support only for on class per el..
47187                         tb.selectedNode.className =  r ? r.get('val') : '';
47188                         editorcore.syncValue();
47189                     }
47190                 }
47191     
47192             }));
47193         }
47194         
47195         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47196         var tbops = tbc.options;
47197         
47198         for (var i in tlist) {
47199             
47200             var item = tlist[i];
47201             tb.add(item.title + ":&nbsp;");
47202             
47203             
47204             //optname == used so you can configure the options available..
47205             var opts = item.opts ? item.opts : false;
47206             if (item.optname) {
47207                 opts = tbops[item.optname];
47208            
47209             }
47210             
47211             if (opts) {
47212                 // opts == pulldown..
47213                 tb.addField( new Roo.form.ComboBox({
47214                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47215                         id : 'val',
47216                         fields: ['val', 'display'],
47217                         data : opts  
47218                     }),
47219                     name : '-roo-edit-' + i,
47220                     attrname : i,
47221                     stylename : item.style ? item.style : false,
47222                     displayField: item.displayField ? item.displayField : 'val',
47223                     valueField :  'val',
47224                     typeAhead: false,
47225                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47226                     editable : false,
47227                     triggerAction: 'all',
47228                     emptyText:'Select',
47229                     selectOnFocus:true,
47230                     width: item.width ? item.width  : 130,
47231                     listeners : {
47232                         'select': function(c, r, i) {
47233                             if (c.stylename) {
47234                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47235                                 return;
47236                             }
47237                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47238                         }
47239                     }
47240
47241                 }));
47242                 continue;
47243                     
47244                  
47245                 
47246                 tb.addField( new Roo.form.TextField({
47247                     name: i,
47248                     width: 100,
47249                     //allowBlank:false,
47250                     value: ''
47251                 }));
47252                 continue;
47253             }
47254             tb.addField( new Roo.form.TextField({
47255                 name: '-roo-edit-' + i,
47256                 attrname : i,
47257                 
47258                 width: item.width,
47259                 //allowBlank:true,
47260                 value: '',
47261                 listeners: {
47262                     'change' : function(f, nv, ov) {
47263                         tb.selectedNode.setAttribute(f.attrname, nv);
47264                         editorcore.syncValue();
47265                     }
47266                 }
47267             }));
47268              
47269         }
47270         
47271         var _this = this;
47272         
47273         if(nm == 'BODY'){
47274             tb.addSeparator();
47275         
47276             tb.addButton( {
47277                 text: 'Stylesheets',
47278
47279                 listeners : {
47280                     click : function ()
47281                     {
47282                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47283                     }
47284                 }
47285             });
47286         }
47287         
47288         tb.addFill();
47289         tb.addButton( {
47290             text: 'Remove Tag',
47291     
47292             listeners : {
47293                 click : function ()
47294                 {
47295                     // remove
47296                     // undo does not work.
47297                      
47298                     var sn = tb.selectedNode;
47299                     
47300                     var pn = sn.parentNode;
47301                     
47302                     var stn =  sn.childNodes[0];
47303                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47304                     while (sn.childNodes.length) {
47305                         var node = sn.childNodes[0];
47306                         sn.removeChild(node);
47307                         //Roo.log(node);
47308                         pn.insertBefore(node, sn);
47309                         
47310                     }
47311                     pn.removeChild(sn);
47312                     var range = editorcore.createRange();
47313         
47314                     range.setStart(stn,0);
47315                     range.setEnd(en,0); //????
47316                     //range.selectNode(sel);
47317                     
47318                     
47319                     var selection = editorcore.getSelection();
47320                     selection.removeAllRanges();
47321                     selection.addRange(range);
47322                     
47323                     
47324                     
47325                     //_this.updateToolbar(null, null, pn);
47326                     _this.updateToolbar(null, null, null);
47327                     _this.footDisp.dom.innerHTML = ''; 
47328                 }
47329             }
47330             
47331                     
47332                 
47333             
47334         });
47335         
47336         
47337         tb.el.on('click', function(e){
47338             e.preventDefault(); // what does this do?
47339         });
47340         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47341         tb.el.hide();
47342         tb.name = nm;
47343         // dont need to disable them... as they will get hidden
47344         return tb;
47345          
47346         
47347     },
47348     buildFooter : function()
47349     {
47350         
47351         var fel = this.editor.wrap.createChild();
47352         this.footer = new Roo.Toolbar(fel);
47353         // toolbar has scrolly on left / right?
47354         var footDisp= new Roo.Toolbar.Fill();
47355         var _t = this;
47356         this.footer.add(
47357             {
47358                 text : '&lt;',
47359                 xtype: 'Button',
47360                 handler : function() {
47361                     _t.footDisp.scrollTo('left',0,true)
47362                 }
47363             }
47364         );
47365         this.footer.add( footDisp );
47366         this.footer.add( 
47367             {
47368                 text : '&gt;',
47369                 xtype: 'Button',
47370                 handler : function() {
47371                     // no animation..
47372                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47373                 }
47374             }
47375         );
47376         var fel = Roo.get(footDisp.el);
47377         fel.addClass('x-editor-context');
47378         this.footDispWrap = fel; 
47379         this.footDispWrap.overflow  = 'hidden';
47380         
47381         this.footDisp = fel.createChild();
47382         this.footDispWrap.on('click', this.onContextClick, this)
47383         
47384         
47385     },
47386     onContextClick : function (ev,dom)
47387     {
47388         ev.preventDefault();
47389         var  cn = dom.className;
47390         //Roo.log(cn);
47391         if (!cn.match(/x-ed-loc-/)) {
47392             return;
47393         }
47394         var n = cn.split('-').pop();
47395         var ans = this.footerEls;
47396         var sel = ans[n];
47397         
47398          // pick
47399         var range = this.editorcore.createRange();
47400         
47401         range.selectNodeContents(sel);
47402         //range.selectNode(sel);
47403         
47404         
47405         var selection = this.editorcore.getSelection();
47406         selection.removeAllRanges();
47407         selection.addRange(range);
47408         
47409         
47410         
47411         this.updateToolbar(null, null, sel);
47412         
47413         
47414     }
47415     
47416     
47417     
47418     
47419     
47420 });
47421
47422
47423
47424
47425
47426 /*
47427  * Based on:
47428  * Ext JS Library 1.1.1
47429  * Copyright(c) 2006-2007, Ext JS, LLC.
47430  *
47431  * Originally Released Under LGPL - original licence link has changed is not relivant.
47432  *
47433  * Fork - LGPL
47434  * <script type="text/javascript">
47435  */
47436  
47437 /**
47438  * @class Roo.form.BasicForm
47439  * @extends Roo.util.Observable
47440  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47441  * @constructor
47442  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47443  * @param {Object} config Configuration options
47444  */
47445 Roo.form.BasicForm = function(el, config){
47446     this.allItems = [];
47447     this.childForms = [];
47448     Roo.apply(this, config);
47449     /*
47450      * The Roo.form.Field items in this form.
47451      * @type MixedCollection
47452      */
47453      
47454      
47455     this.items = new Roo.util.MixedCollection(false, function(o){
47456         return o.id || (o.id = Roo.id());
47457     });
47458     this.addEvents({
47459         /**
47460          * @event beforeaction
47461          * Fires before any action is performed. Return false to cancel the action.
47462          * @param {Form} this
47463          * @param {Action} action The action to be performed
47464          */
47465         beforeaction: true,
47466         /**
47467          * @event actionfailed
47468          * Fires when an action fails.
47469          * @param {Form} this
47470          * @param {Action} action The action that failed
47471          */
47472         actionfailed : true,
47473         /**
47474          * @event actioncomplete
47475          * Fires when an action is completed.
47476          * @param {Form} this
47477          * @param {Action} action The action that completed
47478          */
47479         actioncomplete : true
47480     });
47481     if(el){
47482         this.initEl(el);
47483     }
47484     Roo.form.BasicForm.superclass.constructor.call(this);
47485     
47486     Roo.form.BasicForm.popover.apply();
47487 };
47488
47489 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47490     /**
47491      * @cfg {String} method
47492      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47493      */
47494     /**
47495      * @cfg {DataReader} reader
47496      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47497      * This is optional as there is built-in support for processing JSON.
47498      */
47499     /**
47500      * @cfg {DataReader} errorReader
47501      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47502      * This is completely optional as there is built-in support for processing JSON.
47503      */
47504     /**
47505      * @cfg {String} url
47506      * The URL to use for form actions if one isn't supplied in the action options.
47507      */
47508     /**
47509      * @cfg {Boolean} fileUpload
47510      * Set to true if this form is a file upload.
47511      */
47512      
47513     /**
47514      * @cfg {Object} baseParams
47515      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47516      */
47517      /**
47518      
47519     /**
47520      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47521      */
47522     timeout: 30,
47523
47524     // private
47525     activeAction : null,
47526
47527     /**
47528      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47529      * or setValues() data instead of when the form was first created.
47530      */
47531     trackResetOnLoad : false,
47532     
47533     
47534     /**
47535      * childForms - used for multi-tab forms
47536      * @type {Array}
47537      */
47538     childForms : false,
47539     
47540     /**
47541      * allItems - full list of fields.
47542      * @type {Array}
47543      */
47544     allItems : false,
47545     
47546     /**
47547      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47548      * element by passing it or its id or mask the form itself by passing in true.
47549      * @type Mixed
47550      */
47551     waitMsgTarget : false,
47552     
47553     /**
47554      * @type Boolean
47555      */
47556     disableMask : false,
47557     
47558     /**
47559      * @cfg {Boolean} errorMask (true|false) default false
47560      */
47561     errorMask : false,
47562     
47563     /**
47564      * @cfg {Number} maskOffset Default 100
47565      */
47566     maskOffset : 100,
47567
47568     // private
47569     initEl : function(el){
47570         this.el = Roo.get(el);
47571         this.id = this.el.id || Roo.id();
47572         this.el.on('submit', this.onSubmit, this);
47573         this.el.addClass('x-form');
47574     },
47575
47576     // private
47577     onSubmit : function(e){
47578         e.stopEvent();
47579     },
47580
47581     /**
47582      * Returns true if client-side validation on the form is successful.
47583      * @return Boolean
47584      */
47585     isValid : function(){
47586         var valid = true;
47587         var target = false;
47588         this.items.each(function(f){
47589             if(f.validate()){
47590                 return;
47591             }
47592             
47593             valid = false;
47594                 
47595             if(!target && f.el.isVisible(true)){
47596                 target = f;
47597             }
47598         });
47599         
47600         if(this.errorMask && !valid){
47601             Roo.form.BasicForm.popover.mask(this, target);
47602         }
47603         
47604         return valid;
47605     },
47606     /**
47607      * Returns array of invalid form fields.
47608      * @return Array
47609      */
47610     
47611     invalidFields : function()
47612     {
47613         var ret = [];
47614         this.items.each(function(f){
47615             if(f.validate()){
47616                 return;
47617             }
47618             ret.push(f);
47619             
47620         });
47621         
47622         return ret;
47623     },
47624     
47625     
47626     /**
47627      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47628      * @return Boolean
47629      */
47630     isDirty : function(){
47631         var dirty = false;
47632         this.items.each(function(f){
47633            if(f.isDirty()){
47634                dirty = true;
47635                return false;
47636            }
47637         });
47638         return dirty;
47639     },
47640     
47641     /**
47642      * Returns true if any fields in this form have changed since their original load. (New version)
47643      * @return Boolean
47644      */
47645     
47646     hasChanged : function()
47647     {
47648         var dirty = false;
47649         this.items.each(function(f){
47650            if(f.hasChanged()){
47651                dirty = true;
47652                return false;
47653            }
47654         });
47655         return dirty;
47656         
47657     },
47658     /**
47659      * Resets all hasChanged to 'false' -
47660      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47661      * So hasChanged storage is only to be used for this purpose
47662      * @return Boolean
47663      */
47664     resetHasChanged : function()
47665     {
47666         this.items.each(function(f){
47667            f.resetHasChanged();
47668         });
47669         
47670     },
47671     
47672     
47673     /**
47674      * Performs a predefined action (submit or load) or custom actions you define on this form.
47675      * @param {String} actionName The name of the action type
47676      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47677      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47678      * accept other config options):
47679      * <pre>
47680 Property          Type             Description
47681 ----------------  ---------------  ----------------------------------------------------------------------------------
47682 url               String           The url for the action (defaults to the form's url)
47683 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47684 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47685 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47686                                    validate the form on the client (defaults to false)
47687      * </pre>
47688      * @return {BasicForm} this
47689      */
47690     doAction : function(action, options){
47691         if(typeof action == 'string'){
47692             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47693         }
47694         if(this.fireEvent('beforeaction', this, action) !== false){
47695             this.beforeAction(action);
47696             action.run.defer(100, action);
47697         }
47698         return this;
47699     },
47700
47701     /**
47702      * Shortcut to do a submit action.
47703      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47704      * @return {BasicForm} this
47705      */
47706     submit : function(options){
47707         this.doAction('submit', options);
47708         return this;
47709     },
47710
47711     /**
47712      * Shortcut to do a load action.
47713      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47714      * @return {BasicForm} this
47715      */
47716     load : function(options){
47717         this.doAction('load', options);
47718         return this;
47719     },
47720
47721     /**
47722      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47723      * @param {Record} record The record to edit
47724      * @return {BasicForm} this
47725      */
47726     updateRecord : function(record){
47727         record.beginEdit();
47728         var fs = record.fields;
47729         fs.each(function(f){
47730             var field = this.findField(f.name);
47731             if(field){
47732                 record.set(f.name, field.getValue());
47733             }
47734         }, this);
47735         record.endEdit();
47736         return this;
47737     },
47738
47739     /**
47740      * Loads an Roo.data.Record into this form.
47741      * @param {Record} record The record to load
47742      * @return {BasicForm} this
47743      */
47744     loadRecord : function(record){
47745         this.setValues(record.data);
47746         return this;
47747     },
47748
47749     // private
47750     beforeAction : function(action){
47751         var o = action.options;
47752         
47753         if(!this.disableMask) {
47754             if(this.waitMsgTarget === true){
47755                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47756             }else if(this.waitMsgTarget){
47757                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47758                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47759             }else {
47760                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47761             }
47762         }
47763         
47764          
47765     },
47766
47767     // private
47768     afterAction : function(action, success){
47769         this.activeAction = null;
47770         var o = action.options;
47771         
47772         if(!this.disableMask) {
47773             if(this.waitMsgTarget === true){
47774                 this.el.unmask();
47775             }else if(this.waitMsgTarget){
47776                 this.waitMsgTarget.unmask();
47777             }else{
47778                 Roo.MessageBox.updateProgress(1);
47779                 Roo.MessageBox.hide();
47780             }
47781         }
47782         
47783         if(success){
47784             if(o.reset){
47785                 this.reset();
47786             }
47787             Roo.callback(o.success, o.scope, [this, action]);
47788             this.fireEvent('actioncomplete', this, action);
47789             
47790         }else{
47791             
47792             // failure condition..
47793             // we have a scenario where updates need confirming.
47794             // eg. if a locking scenario exists..
47795             // we look for { errors : { needs_confirm : true }} in the response.
47796             if (
47797                 (typeof(action.result) != 'undefined')  &&
47798                 (typeof(action.result.errors) != 'undefined')  &&
47799                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47800            ){
47801                 var _t = this;
47802                 Roo.MessageBox.confirm(
47803                     "Change requires confirmation",
47804                     action.result.errorMsg,
47805                     function(r) {
47806                         if (r != 'yes') {
47807                             return;
47808                         }
47809                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47810                     }
47811                     
47812                 );
47813                 
47814                 
47815                 
47816                 return;
47817             }
47818             
47819             Roo.callback(o.failure, o.scope, [this, action]);
47820             // show an error message if no failed handler is set..
47821             if (!this.hasListener('actionfailed')) {
47822                 Roo.MessageBox.alert("Error",
47823                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47824                         action.result.errorMsg :
47825                         "Saving Failed, please check your entries or try again"
47826                 );
47827             }
47828             
47829             this.fireEvent('actionfailed', this, action);
47830         }
47831         
47832     },
47833
47834     /**
47835      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47836      * @param {String} id The value to search for
47837      * @return Field
47838      */
47839     findField : function(id){
47840         var field = this.items.get(id);
47841         if(!field){
47842             this.items.each(function(f){
47843                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47844                     field = f;
47845                     return false;
47846                 }
47847             });
47848         }
47849         return field || null;
47850     },
47851
47852     /**
47853      * Add a secondary form to this one, 
47854      * Used to provide tabbed forms. One form is primary, with hidden values 
47855      * which mirror the elements from the other forms.
47856      * 
47857      * @param {Roo.form.Form} form to add.
47858      * 
47859      */
47860     addForm : function(form)
47861     {
47862        
47863         if (this.childForms.indexOf(form) > -1) {
47864             // already added..
47865             return;
47866         }
47867         this.childForms.push(form);
47868         var n = '';
47869         Roo.each(form.allItems, function (fe) {
47870             
47871             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47872             if (this.findField(n)) { // already added..
47873                 return;
47874             }
47875             var add = new Roo.form.Hidden({
47876                 name : n
47877             });
47878             add.render(this.el);
47879             
47880             this.add( add );
47881         }, this);
47882         
47883     },
47884     /**
47885      * Mark fields in this form invalid in bulk.
47886      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47887      * @return {BasicForm} this
47888      */
47889     markInvalid : function(errors){
47890         if(errors instanceof Array){
47891             for(var i = 0, len = errors.length; i < len; i++){
47892                 var fieldError = errors[i];
47893                 var f = this.findField(fieldError.id);
47894                 if(f){
47895                     f.markInvalid(fieldError.msg);
47896                 }
47897             }
47898         }else{
47899             var field, id;
47900             for(id in errors){
47901                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47902                     field.markInvalid(errors[id]);
47903                 }
47904             }
47905         }
47906         Roo.each(this.childForms || [], function (f) {
47907             f.markInvalid(errors);
47908         });
47909         
47910         return this;
47911     },
47912
47913     /**
47914      * Set values for fields in this form in bulk.
47915      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47916      * @return {BasicForm} this
47917      */
47918     setValues : function(values){
47919         if(values instanceof Array){ // array of objects
47920             for(var i = 0, len = values.length; i < len; i++){
47921                 var v = values[i];
47922                 var f = this.findField(v.id);
47923                 if(f){
47924                     f.setValue(v.value);
47925                     if(this.trackResetOnLoad){
47926                         f.originalValue = f.getValue();
47927                     }
47928                 }
47929             }
47930         }else{ // object hash
47931             var field, id;
47932             for(id in values){
47933                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47934                     
47935                     if (field.setFromData && 
47936                         field.valueField && 
47937                         field.displayField &&
47938                         // combos' with local stores can 
47939                         // be queried via setValue()
47940                         // to set their value..
47941                         (field.store && !field.store.isLocal)
47942                         ) {
47943                         // it's a combo
47944                         var sd = { };
47945                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47946                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47947                         field.setFromData(sd);
47948                         
47949                     } else {
47950                         field.setValue(values[id]);
47951                     }
47952                     
47953                     
47954                     if(this.trackResetOnLoad){
47955                         field.originalValue = field.getValue();
47956                     }
47957                 }
47958             }
47959         }
47960         this.resetHasChanged();
47961         
47962         
47963         Roo.each(this.childForms || [], function (f) {
47964             f.setValues(values);
47965             f.resetHasChanged();
47966         });
47967                 
47968         return this;
47969     },
47970  
47971     /**
47972      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47973      * they are returned as an array.
47974      * @param {Boolean} asString
47975      * @return {Object}
47976      */
47977     getValues : function(asString){
47978         if (this.childForms) {
47979             // copy values from the child forms
47980             Roo.each(this.childForms, function (f) {
47981                 this.setValues(f.getValues());
47982             }, this);
47983         }
47984         
47985         // use formdata
47986         if (typeof(FormData) != 'undefined' && asString !== true) {
47987             // this relies on a 'recent' version of chrome apparently...
47988             try {
47989                 var fd = (new FormData(this.el.dom)).entries();
47990                 var ret = {};
47991                 var ent = fd.next();
47992                 while (!ent.done) {
47993                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47994                     ent = fd.next();
47995                 };
47996                 return ret;
47997             } catch(e) {
47998                 
47999             }
48000             
48001         }
48002         
48003         
48004         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48005         if(asString === true){
48006             return fs;
48007         }
48008         return Roo.urlDecode(fs);
48009     },
48010     
48011     /**
48012      * Returns the fields in this form as an object with key/value pairs. 
48013      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48014      * @return {Object}
48015      */
48016     getFieldValues : function(with_hidden)
48017     {
48018         if (this.childForms) {
48019             // copy values from the child forms
48020             // should this call getFieldValues - probably not as we do not currently copy
48021             // hidden fields when we generate..
48022             Roo.each(this.childForms, function (f) {
48023                 this.setValues(f.getValues());
48024             }, this);
48025         }
48026         
48027         var ret = {};
48028         this.items.each(function(f){
48029             if (!f.getName()) {
48030                 return;
48031             }
48032             var v = f.getValue();
48033             if (f.inputType =='radio') {
48034                 if (typeof(ret[f.getName()]) == 'undefined') {
48035                     ret[f.getName()] = ''; // empty..
48036                 }
48037                 
48038                 if (!f.el.dom.checked) {
48039                     return;
48040                     
48041                 }
48042                 v = f.el.dom.value;
48043                 
48044             }
48045             
48046             // not sure if this supported any more..
48047             if ((typeof(v) == 'object') && f.getRawValue) {
48048                 v = f.getRawValue() ; // dates..
48049             }
48050             // combo boxes where name != hiddenName...
48051             if (f.name != f.getName()) {
48052                 ret[f.name] = f.getRawValue();
48053             }
48054             ret[f.getName()] = v;
48055         });
48056         
48057         return ret;
48058     },
48059
48060     /**
48061      * Clears all invalid messages in this form.
48062      * @return {BasicForm} this
48063      */
48064     clearInvalid : function(){
48065         this.items.each(function(f){
48066            f.clearInvalid();
48067         });
48068         
48069         Roo.each(this.childForms || [], function (f) {
48070             f.clearInvalid();
48071         });
48072         
48073         
48074         return this;
48075     },
48076
48077     /**
48078      * Resets this form.
48079      * @return {BasicForm} this
48080      */
48081     reset : function(){
48082         this.items.each(function(f){
48083             f.reset();
48084         });
48085         
48086         Roo.each(this.childForms || [], function (f) {
48087             f.reset();
48088         });
48089         this.resetHasChanged();
48090         
48091         return this;
48092     },
48093
48094     /**
48095      * Add Roo.form components to this form.
48096      * @param {Field} field1
48097      * @param {Field} field2 (optional)
48098      * @param {Field} etc (optional)
48099      * @return {BasicForm} this
48100      */
48101     add : function(){
48102         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48103         return this;
48104     },
48105
48106
48107     /**
48108      * Removes a field from the items collection (does NOT remove its markup).
48109      * @param {Field} field
48110      * @return {BasicForm} this
48111      */
48112     remove : function(field){
48113         this.items.remove(field);
48114         return this;
48115     },
48116
48117     /**
48118      * Looks at the fields in this form, checks them for an id attribute,
48119      * and calls applyTo on the existing dom element with that id.
48120      * @return {BasicForm} this
48121      */
48122     render : function(){
48123         this.items.each(function(f){
48124             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48125                 f.applyTo(f.id);
48126             }
48127         });
48128         return this;
48129     },
48130
48131     /**
48132      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48133      * @param {Object} values
48134      * @return {BasicForm} this
48135      */
48136     applyToFields : function(o){
48137         this.items.each(function(f){
48138            Roo.apply(f, o);
48139         });
48140         return this;
48141     },
48142
48143     /**
48144      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48145      * @param {Object} values
48146      * @return {BasicForm} this
48147      */
48148     applyIfToFields : function(o){
48149         this.items.each(function(f){
48150            Roo.applyIf(f, o);
48151         });
48152         return this;
48153     }
48154 });
48155
48156 // back compat
48157 Roo.BasicForm = Roo.form.BasicForm;
48158
48159 Roo.apply(Roo.form.BasicForm, {
48160     
48161     popover : {
48162         
48163         padding : 5,
48164         
48165         isApplied : false,
48166         
48167         isMasked : false,
48168         
48169         form : false,
48170         
48171         target : false,
48172         
48173         intervalID : false,
48174         
48175         maskEl : false,
48176         
48177         apply : function()
48178         {
48179             if(this.isApplied){
48180                 return;
48181             }
48182             
48183             this.maskEl = {
48184                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48185                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48186                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48187                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48188             };
48189             
48190             this.maskEl.top.enableDisplayMode("block");
48191             this.maskEl.left.enableDisplayMode("block");
48192             this.maskEl.bottom.enableDisplayMode("block");
48193             this.maskEl.right.enableDisplayMode("block");
48194             
48195             Roo.get(document.body).on('click', function(){
48196                 this.unmask();
48197             }, this);
48198             
48199             Roo.get(document.body).on('touchstart', function(){
48200                 this.unmask();
48201             }, this);
48202             
48203             this.isApplied = true
48204         },
48205         
48206         mask : function(form, target)
48207         {
48208             this.form = form;
48209             
48210             this.target = target;
48211             
48212             if(!this.form.errorMask || !target.el){
48213                 return;
48214             }
48215             
48216             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48217             
48218             var ot = this.target.el.calcOffsetsTo(scrollable);
48219             
48220             var scrollTo = ot[1] - this.form.maskOffset;
48221             
48222             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48223             
48224             scrollable.scrollTo('top', scrollTo);
48225             
48226             var el = this.target.wrap || this.target.el;
48227             
48228             var box = el.getBox();
48229             
48230             this.maskEl.top.setStyle('position', 'absolute');
48231             this.maskEl.top.setStyle('z-index', 10000);
48232             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48233             this.maskEl.top.setLeft(0);
48234             this.maskEl.top.setTop(0);
48235             this.maskEl.top.show();
48236             
48237             this.maskEl.left.setStyle('position', 'absolute');
48238             this.maskEl.left.setStyle('z-index', 10000);
48239             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48240             this.maskEl.left.setLeft(0);
48241             this.maskEl.left.setTop(box.y - this.padding);
48242             this.maskEl.left.show();
48243
48244             this.maskEl.bottom.setStyle('position', 'absolute');
48245             this.maskEl.bottom.setStyle('z-index', 10000);
48246             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48247             this.maskEl.bottom.setLeft(0);
48248             this.maskEl.bottom.setTop(box.bottom + this.padding);
48249             this.maskEl.bottom.show();
48250
48251             this.maskEl.right.setStyle('position', 'absolute');
48252             this.maskEl.right.setStyle('z-index', 10000);
48253             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48254             this.maskEl.right.setLeft(box.right + this.padding);
48255             this.maskEl.right.setTop(box.y - this.padding);
48256             this.maskEl.right.show();
48257
48258             this.intervalID = window.setInterval(function() {
48259                 Roo.form.BasicForm.popover.unmask();
48260             }, 10000);
48261
48262             window.onwheel = function(){ return false;};
48263             
48264             (function(){ this.isMasked = true; }).defer(500, this);
48265             
48266         },
48267         
48268         unmask : function()
48269         {
48270             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48271                 return;
48272             }
48273             
48274             this.maskEl.top.setStyle('position', 'absolute');
48275             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48276             this.maskEl.top.hide();
48277
48278             this.maskEl.left.setStyle('position', 'absolute');
48279             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48280             this.maskEl.left.hide();
48281
48282             this.maskEl.bottom.setStyle('position', 'absolute');
48283             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48284             this.maskEl.bottom.hide();
48285
48286             this.maskEl.right.setStyle('position', 'absolute');
48287             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48288             this.maskEl.right.hide();
48289             
48290             window.onwheel = function(){ return true;};
48291             
48292             if(this.intervalID){
48293                 window.clearInterval(this.intervalID);
48294                 this.intervalID = false;
48295             }
48296             
48297             this.isMasked = false;
48298             
48299         }
48300         
48301     }
48302     
48303 });/*
48304  * Based on:
48305  * Ext JS Library 1.1.1
48306  * Copyright(c) 2006-2007, Ext JS, LLC.
48307  *
48308  * Originally Released Under LGPL - original licence link has changed is not relivant.
48309  *
48310  * Fork - LGPL
48311  * <script type="text/javascript">
48312  */
48313
48314 /**
48315  * @class Roo.form.Form
48316  * @extends Roo.form.BasicForm
48317  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48318  * @constructor
48319  * @param {Object} config Configuration options
48320  */
48321 Roo.form.Form = function(config){
48322     var xitems =  [];
48323     if (config.items) {
48324         xitems = config.items;
48325         delete config.items;
48326     }
48327    
48328     
48329     Roo.form.Form.superclass.constructor.call(this, null, config);
48330     this.url = this.url || this.action;
48331     if(!this.root){
48332         this.root = new Roo.form.Layout(Roo.applyIf({
48333             id: Roo.id()
48334         }, config));
48335     }
48336     this.active = this.root;
48337     /**
48338      * Array of all the buttons that have been added to this form via {@link addButton}
48339      * @type Array
48340      */
48341     this.buttons = [];
48342     this.allItems = [];
48343     this.addEvents({
48344         /**
48345          * @event clientvalidation
48346          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48347          * @param {Form} this
48348          * @param {Boolean} valid true if the form has passed client-side validation
48349          */
48350         clientvalidation: true,
48351         /**
48352          * @event rendered
48353          * Fires when the form is rendered
48354          * @param {Roo.form.Form} form
48355          */
48356         rendered : true
48357     });
48358     
48359     if (this.progressUrl) {
48360             // push a hidden field onto the list of fields..
48361             this.addxtype( {
48362                     xns: Roo.form, 
48363                     xtype : 'Hidden', 
48364                     name : 'UPLOAD_IDENTIFIER' 
48365             });
48366         }
48367         
48368     
48369     Roo.each(xitems, this.addxtype, this);
48370     
48371 };
48372
48373 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48374     /**
48375      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48376      */
48377     /**
48378      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48379      */
48380     /**
48381      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48382      */
48383     buttonAlign:'center',
48384
48385     /**
48386      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48387      */
48388     minButtonWidth:75,
48389
48390     /**
48391      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48392      * This property cascades to child containers if not set.
48393      */
48394     labelAlign:'left',
48395
48396     /**
48397      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48398      * fires a looping event with that state. This is required to bind buttons to the valid
48399      * state using the config value formBind:true on the button.
48400      */
48401     monitorValid : false,
48402
48403     /**
48404      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48405      */
48406     monitorPoll : 200,
48407     
48408     /**
48409      * @cfg {String} progressUrl - Url to return progress data 
48410      */
48411     
48412     progressUrl : false,
48413     /**
48414      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48415      * sending a formdata with extra parameters - eg uploaded elements.
48416      */
48417     
48418     formData : false,
48419     
48420     /**
48421      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48422      * fields are added and the column is closed. If no fields are passed the column remains open
48423      * until end() is called.
48424      * @param {Object} config The config to pass to the column
48425      * @param {Field} field1 (optional)
48426      * @param {Field} field2 (optional)
48427      * @param {Field} etc (optional)
48428      * @return Column The column container object
48429      */
48430     column : function(c){
48431         var col = new Roo.form.Column(c);
48432         this.start(col);
48433         if(arguments.length > 1){ // duplicate code required because of Opera
48434             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48435             this.end();
48436         }
48437         return col;
48438     },
48439
48440     /**
48441      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48442      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48443      * until end() is called.
48444      * @param {Object} config The config to pass to the fieldset
48445      * @param {Field} field1 (optional)
48446      * @param {Field} field2 (optional)
48447      * @param {Field} etc (optional)
48448      * @return FieldSet The fieldset container object
48449      */
48450     fieldset : function(c){
48451         var fs = new Roo.form.FieldSet(c);
48452         this.start(fs);
48453         if(arguments.length > 1){ // duplicate code required because of Opera
48454             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48455             this.end();
48456         }
48457         return fs;
48458     },
48459
48460     /**
48461      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48462      * fields are added and the container is closed. If no fields are passed the container remains open
48463      * until end() is called.
48464      * @param {Object} config The config to pass to the Layout
48465      * @param {Field} field1 (optional)
48466      * @param {Field} field2 (optional)
48467      * @param {Field} etc (optional)
48468      * @return Layout The container object
48469      */
48470     container : function(c){
48471         var l = new Roo.form.Layout(c);
48472         this.start(l);
48473         if(arguments.length > 1){ // duplicate code required because of Opera
48474             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48475             this.end();
48476         }
48477         return l;
48478     },
48479
48480     /**
48481      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48482      * @param {Object} container A Roo.form.Layout or subclass of Layout
48483      * @return {Form} this
48484      */
48485     start : function(c){
48486         // cascade label info
48487         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48488         this.active.stack.push(c);
48489         c.ownerCt = this.active;
48490         this.active = c;
48491         return this;
48492     },
48493
48494     /**
48495      * Closes the current open container
48496      * @return {Form} this
48497      */
48498     end : function(){
48499         if(this.active == this.root){
48500             return this;
48501         }
48502         this.active = this.active.ownerCt;
48503         return this;
48504     },
48505
48506     /**
48507      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48508      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48509      * as the label of the field.
48510      * @param {Field} field1
48511      * @param {Field} field2 (optional)
48512      * @param {Field} etc. (optional)
48513      * @return {Form} this
48514      */
48515     add : function(){
48516         this.active.stack.push.apply(this.active.stack, arguments);
48517         this.allItems.push.apply(this.allItems,arguments);
48518         var r = [];
48519         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48520             if(a[i].isFormField){
48521                 r.push(a[i]);
48522             }
48523         }
48524         if(r.length > 0){
48525             Roo.form.Form.superclass.add.apply(this, r);
48526         }
48527         return this;
48528     },
48529     
48530
48531     
48532     
48533     
48534      /**
48535      * Find any element that has been added to a form, using it's ID or name
48536      * This can include framesets, columns etc. along with regular fields..
48537      * @param {String} id - id or name to find.
48538      
48539      * @return {Element} e - or false if nothing found.
48540      */
48541     findbyId : function(id)
48542     {
48543         var ret = false;
48544         if (!id) {
48545             return ret;
48546         }
48547         Roo.each(this.allItems, function(f){
48548             if (f.id == id || f.name == id ){
48549                 ret = f;
48550                 return false;
48551             }
48552         });
48553         return ret;
48554     },
48555
48556     
48557     
48558     /**
48559      * Render this form into the passed container. This should only be called once!
48560      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48561      * @return {Form} this
48562      */
48563     render : function(ct)
48564     {
48565         
48566         
48567         
48568         ct = Roo.get(ct);
48569         var o = this.autoCreate || {
48570             tag: 'form',
48571             method : this.method || 'POST',
48572             id : this.id || Roo.id()
48573         };
48574         this.initEl(ct.createChild(o));
48575
48576         this.root.render(this.el);
48577         
48578        
48579              
48580         this.items.each(function(f){
48581             f.render('x-form-el-'+f.id);
48582         });
48583
48584         if(this.buttons.length > 0){
48585             // tables are required to maintain order and for correct IE layout
48586             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48587                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48588                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48589             }}, null, true);
48590             var tr = tb.getElementsByTagName('tr')[0];
48591             for(var i = 0, len = this.buttons.length; i < len; i++) {
48592                 var b = this.buttons[i];
48593                 var td = document.createElement('td');
48594                 td.className = 'x-form-btn-td';
48595                 b.render(tr.appendChild(td));
48596             }
48597         }
48598         if(this.monitorValid){ // initialize after render
48599             this.startMonitoring();
48600         }
48601         this.fireEvent('rendered', this);
48602         return this;
48603     },
48604
48605     /**
48606      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48607      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48608      * object or a valid Roo.DomHelper element config
48609      * @param {Function} handler The function called when the button is clicked
48610      * @param {Object} scope (optional) The scope of the handler function
48611      * @return {Roo.Button}
48612      */
48613     addButton : function(config, handler, scope){
48614         var bc = {
48615             handler: handler,
48616             scope: scope,
48617             minWidth: this.minButtonWidth,
48618             hideParent:true
48619         };
48620         if(typeof config == "string"){
48621             bc.text = config;
48622         }else{
48623             Roo.apply(bc, config);
48624         }
48625         var btn = new Roo.Button(null, bc);
48626         this.buttons.push(btn);
48627         return btn;
48628     },
48629
48630      /**
48631      * Adds a series of form elements (using the xtype property as the factory method.
48632      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48633      * @param {Object} config 
48634      */
48635     
48636     addxtype : function()
48637     {
48638         var ar = Array.prototype.slice.call(arguments, 0);
48639         var ret = false;
48640         for(var i = 0; i < ar.length; i++) {
48641             if (!ar[i]) {
48642                 continue; // skip -- if this happends something invalid got sent, we 
48643                 // should ignore it, as basically that interface element will not show up
48644                 // and that should be pretty obvious!!
48645             }
48646             
48647             if (Roo.form[ar[i].xtype]) {
48648                 ar[i].form = this;
48649                 var fe = Roo.factory(ar[i], Roo.form);
48650                 if (!ret) {
48651                     ret = fe;
48652                 }
48653                 fe.form = this;
48654                 if (fe.store) {
48655                     fe.store.form = this;
48656                 }
48657                 if (fe.isLayout) {  
48658                          
48659                     this.start(fe);
48660                     this.allItems.push(fe);
48661                     if (fe.items && fe.addxtype) {
48662                         fe.addxtype.apply(fe, fe.items);
48663                         delete fe.items;
48664                     }
48665                      this.end();
48666                     continue;
48667                 }
48668                 
48669                 
48670                  
48671                 this.add(fe);
48672               //  console.log('adding ' + ar[i].xtype);
48673             }
48674             if (ar[i].xtype == 'Button') {  
48675                 //console.log('adding button');
48676                 //console.log(ar[i]);
48677                 this.addButton(ar[i]);
48678                 this.allItems.push(fe);
48679                 continue;
48680             }
48681             
48682             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48683                 alert('end is not supported on xtype any more, use items');
48684             //    this.end();
48685             //    //console.log('adding end');
48686             }
48687             
48688         }
48689         return ret;
48690     },
48691     
48692     /**
48693      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48694      * option "monitorValid"
48695      */
48696     startMonitoring : function(){
48697         if(!this.bound){
48698             this.bound = true;
48699             Roo.TaskMgr.start({
48700                 run : this.bindHandler,
48701                 interval : this.monitorPoll || 200,
48702                 scope: this
48703             });
48704         }
48705     },
48706
48707     /**
48708      * Stops monitoring of the valid state of this form
48709      */
48710     stopMonitoring : function(){
48711         this.bound = false;
48712     },
48713
48714     // private
48715     bindHandler : function(){
48716         if(!this.bound){
48717             return false; // stops binding
48718         }
48719         var valid = true;
48720         this.items.each(function(f){
48721             if(!f.isValid(true)){
48722                 valid = false;
48723                 return false;
48724             }
48725         });
48726         for(var i = 0, len = this.buttons.length; i < len; i++){
48727             var btn = this.buttons[i];
48728             if(btn.formBind === true && btn.disabled === valid){
48729                 btn.setDisabled(!valid);
48730             }
48731         }
48732         this.fireEvent('clientvalidation', this, valid);
48733     }
48734     
48735     
48736     
48737     
48738     
48739     
48740     
48741     
48742 });
48743
48744
48745 // back compat
48746 Roo.Form = Roo.form.Form;
48747 /*
48748  * Based on:
48749  * Ext JS Library 1.1.1
48750  * Copyright(c) 2006-2007, Ext JS, LLC.
48751  *
48752  * Originally Released Under LGPL - original licence link has changed is not relivant.
48753  *
48754  * Fork - LGPL
48755  * <script type="text/javascript">
48756  */
48757
48758 // as we use this in bootstrap.
48759 Roo.namespace('Roo.form');
48760  /**
48761  * @class Roo.form.Action
48762  * Internal Class used to handle form actions
48763  * @constructor
48764  * @param {Roo.form.BasicForm} el The form element or its id
48765  * @param {Object} config Configuration options
48766  */
48767
48768  
48769  
48770 // define the action interface
48771 Roo.form.Action = function(form, options){
48772     this.form = form;
48773     this.options = options || {};
48774 };
48775 /**
48776  * Client Validation Failed
48777  * @const 
48778  */
48779 Roo.form.Action.CLIENT_INVALID = 'client';
48780 /**
48781  * Server Validation Failed
48782  * @const 
48783  */
48784 Roo.form.Action.SERVER_INVALID = 'server';
48785  /**
48786  * Connect to Server Failed
48787  * @const 
48788  */
48789 Roo.form.Action.CONNECT_FAILURE = 'connect';
48790 /**
48791  * Reading Data from Server Failed
48792  * @const 
48793  */
48794 Roo.form.Action.LOAD_FAILURE = 'load';
48795
48796 Roo.form.Action.prototype = {
48797     type : 'default',
48798     failureType : undefined,
48799     response : undefined,
48800     result : undefined,
48801
48802     // interface method
48803     run : function(options){
48804
48805     },
48806
48807     // interface method
48808     success : function(response){
48809
48810     },
48811
48812     // interface method
48813     handleResponse : function(response){
48814
48815     },
48816
48817     // default connection failure
48818     failure : function(response){
48819         
48820         this.response = response;
48821         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48822         this.form.afterAction(this, false);
48823     },
48824
48825     processResponse : function(response){
48826         this.response = response;
48827         if(!response.responseText){
48828             return true;
48829         }
48830         this.result = this.handleResponse(response);
48831         return this.result;
48832     },
48833
48834     // utility functions used internally
48835     getUrl : function(appendParams){
48836         var url = this.options.url || this.form.url || this.form.el.dom.action;
48837         if(appendParams){
48838             var p = this.getParams();
48839             if(p){
48840                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48841             }
48842         }
48843         return url;
48844     },
48845
48846     getMethod : function(){
48847         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48848     },
48849
48850     getParams : function(){
48851         var bp = this.form.baseParams;
48852         var p = this.options.params;
48853         if(p){
48854             if(typeof p == "object"){
48855                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48856             }else if(typeof p == 'string' && bp){
48857                 p += '&' + Roo.urlEncode(bp);
48858             }
48859         }else if(bp){
48860             p = Roo.urlEncode(bp);
48861         }
48862         return p;
48863     },
48864
48865     createCallback : function(){
48866         return {
48867             success: this.success,
48868             failure: this.failure,
48869             scope: this,
48870             timeout: (this.form.timeout*1000),
48871             upload: this.form.fileUpload ? this.success : undefined
48872         };
48873     }
48874 };
48875
48876 Roo.form.Action.Submit = function(form, options){
48877     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48878 };
48879
48880 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48881     type : 'submit',
48882
48883     haveProgress : false,
48884     uploadComplete : false,
48885     
48886     // uploadProgress indicator.
48887     uploadProgress : function()
48888     {
48889         if (!this.form.progressUrl) {
48890             return;
48891         }
48892         
48893         if (!this.haveProgress) {
48894             Roo.MessageBox.progress("Uploading", "Uploading");
48895         }
48896         if (this.uploadComplete) {
48897            Roo.MessageBox.hide();
48898            return;
48899         }
48900         
48901         this.haveProgress = true;
48902    
48903         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48904         
48905         var c = new Roo.data.Connection();
48906         c.request({
48907             url : this.form.progressUrl,
48908             params: {
48909                 id : uid
48910             },
48911             method: 'GET',
48912             success : function(req){
48913                //console.log(data);
48914                 var rdata = false;
48915                 var edata;
48916                 try  {
48917                    rdata = Roo.decode(req.responseText)
48918                 } catch (e) {
48919                     Roo.log("Invalid data from server..");
48920                     Roo.log(edata);
48921                     return;
48922                 }
48923                 if (!rdata || !rdata.success) {
48924                     Roo.log(rdata);
48925                     Roo.MessageBox.alert(Roo.encode(rdata));
48926                     return;
48927                 }
48928                 var data = rdata.data;
48929                 
48930                 if (this.uploadComplete) {
48931                    Roo.MessageBox.hide();
48932                    return;
48933                 }
48934                    
48935                 if (data){
48936                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48937                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48938                     );
48939                 }
48940                 this.uploadProgress.defer(2000,this);
48941             },
48942        
48943             failure: function(data) {
48944                 Roo.log('progress url failed ');
48945                 Roo.log(data);
48946             },
48947             scope : this
48948         });
48949            
48950     },
48951     
48952     
48953     run : function()
48954     {
48955         // run get Values on the form, so it syncs any secondary forms.
48956         this.form.getValues();
48957         
48958         var o = this.options;
48959         var method = this.getMethod();
48960         var isPost = method == 'POST';
48961         if(o.clientValidation === false || this.form.isValid()){
48962             
48963             if (this.form.progressUrl) {
48964                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48965                     (new Date() * 1) + '' + Math.random());
48966                     
48967             } 
48968             
48969             
48970             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48971                 form:this.form.el.dom,
48972                 url:this.getUrl(!isPost),
48973                 method: method,
48974                 params:isPost ? this.getParams() : null,
48975                 isUpload: this.form.fileUpload,
48976                 formData : this.form.formData
48977             }));
48978             
48979             this.uploadProgress();
48980
48981         }else if (o.clientValidation !== false){ // client validation failed
48982             this.failureType = Roo.form.Action.CLIENT_INVALID;
48983             this.form.afterAction(this, false);
48984         }
48985     },
48986
48987     success : function(response)
48988     {
48989         this.uploadComplete= true;
48990         if (this.haveProgress) {
48991             Roo.MessageBox.hide();
48992         }
48993         
48994         
48995         var result = this.processResponse(response);
48996         if(result === true || result.success){
48997             this.form.afterAction(this, true);
48998             return;
48999         }
49000         if(result.errors){
49001             this.form.markInvalid(result.errors);
49002             this.failureType = Roo.form.Action.SERVER_INVALID;
49003         }
49004         this.form.afterAction(this, false);
49005     },
49006     failure : function(response)
49007     {
49008         this.uploadComplete= true;
49009         if (this.haveProgress) {
49010             Roo.MessageBox.hide();
49011         }
49012         
49013         this.response = response;
49014         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49015         this.form.afterAction(this, false);
49016     },
49017     
49018     handleResponse : function(response){
49019         if(this.form.errorReader){
49020             var rs = this.form.errorReader.read(response);
49021             var errors = [];
49022             if(rs.records){
49023                 for(var i = 0, len = rs.records.length; i < len; i++) {
49024                     var r = rs.records[i];
49025                     errors[i] = r.data;
49026                 }
49027             }
49028             if(errors.length < 1){
49029                 errors = null;
49030             }
49031             return {
49032                 success : rs.success,
49033                 errors : errors
49034             };
49035         }
49036         var ret = false;
49037         try {
49038             ret = Roo.decode(response.responseText);
49039         } catch (e) {
49040             ret = {
49041                 success: false,
49042                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49043                 errors : []
49044             };
49045         }
49046         return ret;
49047         
49048     }
49049 });
49050
49051
49052 Roo.form.Action.Load = function(form, options){
49053     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49054     this.reader = this.form.reader;
49055 };
49056
49057 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49058     type : 'load',
49059
49060     run : function(){
49061         
49062         Roo.Ajax.request(Roo.apply(
49063                 this.createCallback(), {
49064                     method:this.getMethod(),
49065                     url:this.getUrl(false),
49066                     params:this.getParams()
49067         }));
49068     },
49069
49070     success : function(response){
49071         
49072         var result = this.processResponse(response);
49073         if(result === true || !result.success || !result.data){
49074             this.failureType = Roo.form.Action.LOAD_FAILURE;
49075             this.form.afterAction(this, false);
49076             return;
49077         }
49078         this.form.clearInvalid();
49079         this.form.setValues(result.data);
49080         this.form.afterAction(this, true);
49081     },
49082
49083     handleResponse : function(response){
49084         if(this.form.reader){
49085             var rs = this.form.reader.read(response);
49086             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49087             return {
49088                 success : rs.success,
49089                 data : data
49090             };
49091         }
49092         return Roo.decode(response.responseText);
49093     }
49094 });
49095
49096 Roo.form.Action.ACTION_TYPES = {
49097     'load' : Roo.form.Action.Load,
49098     'submit' : Roo.form.Action.Submit
49099 };/*
49100  * Based on:
49101  * Ext JS Library 1.1.1
49102  * Copyright(c) 2006-2007, Ext JS, LLC.
49103  *
49104  * Originally Released Under LGPL - original licence link has changed is not relivant.
49105  *
49106  * Fork - LGPL
49107  * <script type="text/javascript">
49108  */
49109  
49110 /**
49111  * @class Roo.form.Layout
49112  * @extends Roo.Component
49113  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49114  * @constructor
49115  * @param {Object} config Configuration options
49116  */
49117 Roo.form.Layout = function(config){
49118     var xitems = [];
49119     if (config.items) {
49120         xitems = config.items;
49121         delete config.items;
49122     }
49123     Roo.form.Layout.superclass.constructor.call(this, config);
49124     this.stack = [];
49125     Roo.each(xitems, this.addxtype, this);
49126      
49127 };
49128
49129 Roo.extend(Roo.form.Layout, Roo.Component, {
49130     /**
49131      * @cfg {String/Object} autoCreate
49132      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49133      */
49134     /**
49135      * @cfg {String/Object/Function} style
49136      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49137      * a function which returns such a specification.
49138      */
49139     /**
49140      * @cfg {String} labelAlign
49141      * Valid values are "left," "top" and "right" (defaults to "left")
49142      */
49143     /**
49144      * @cfg {Number} labelWidth
49145      * Fixed width in pixels of all field labels (defaults to undefined)
49146      */
49147     /**
49148      * @cfg {Boolean} clear
49149      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49150      */
49151     clear : true,
49152     /**
49153      * @cfg {String} labelSeparator
49154      * The separator to use after field labels (defaults to ':')
49155      */
49156     labelSeparator : ':',
49157     /**
49158      * @cfg {Boolean} hideLabels
49159      * True to suppress the display of field labels in this layout (defaults to false)
49160      */
49161     hideLabels : false,
49162
49163     // private
49164     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49165     
49166     isLayout : true,
49167     
49168     // private
49169     onRender : function(ct, position){
49170         if(this.el){ // from markup
49171             this.el = Roo.get(this.el);
49172         }else {  // generate
49173             var cfg = this.getAutoCreate();
49174             this.el = ct.createChild(cfg, position);
49175         }
49176         if(this.style){
49177             this.el.applyStyles(this.style);
49178         }
49179         if(this.labelAlign){
49180             this.el.addClass('x-form-label-'+this.labelAlign);
49181         }
49182         if(this.hideLabels){
49183             this.labelStyle = "display:none";
49184             this.elementStyle = "padding-left:0;";
49185         }else{
49186             if(typeof this.labelWidth == 'number'){
49187                 this.labelStyle = "width:"+this.labelWidth+"px;";
49188                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49189             }
49190             if(this.labelAlign == 'top'){
49191                 this.labelStyle = "width:auto;";
49192                 this.elementStyle = "padding-left:0;";
49193             }
49194         }
49195         var stack = this.stack;
49196         var slen = stack.length;
49197         if(slen > 0){
49198             if(!this.fieldTpl){
49199                 var t = new Roo.Template(
49200                     '<div class="x-form-item {5}">',
49201                         '<label for="{0}" style="{2}">{1}{4}</label>',
49202                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49203                         '</div>',
49204                     '</div><div class="x-form-clear-left"></div>'
49205                 );
49206                 t.disableFormats = true;
49207                 t.compile();
49208                 Roo.form.Layout.prototype.fieldTpl = t;
49209             }
49210             for(var i = 0; i < slen; i++) {
49211                 if(stack[i].isFormField){
49212                     this.renderField(stack[i]);
49213                 }else{
49214                     this.renderComponent(stack[i]);
49215                 }
49216             }
49217         }
49218         if(this.clear){
49219             this.el.createChild({cls:'x-form-clear'});
49220         }
49221     },
49222
49223     // private
49224     renderField : function(f){
49225         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49226                f.id, //0
49227                f.fieldLabel, //1
49228                f.labelStyle||this.labelStyle||'', //2
49229                this.elementStyle||'', //3
49230                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49231                f.itemCls||this.itemCls||''  //5
49232        ], true).getPrevSibling());
49233     },
49234
49235     // private
49236     renderComponent : function(c){
49237         c.render(c.isLayout ? this.el : this.el.createChild());    
49238     },
49239     /**
49240      * Adds a object form elements (using the xtype property as the factory method.)
49241      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49242      * @param {Object} config 
49243      */
49244     addxtype : function(o)
49245     {
49246         // create the lement.
49247         o.form = this.form;
49248         var fe = Roo.factory(o, Roo.form);
49249         this.form.allItems.push(fe);
49250         this.stack.push(fe);
49251         
49252         if (fe.isFormField) {
49253             this.form.items.add(fe);
49254         }
49255          
49256         return fe;
49257     }
49258 });
49259
49260 /**
49261  * @class Roo.form.Column
49262  * @extends Roo.form.Layout
49263  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49264  * @constructor
49265  * @param {Object} config Configuration options
49266  */
49267 Roo.form.Column = function(config){
49268     Roo.form.Column.superclass.constructor.call(this, config);
49269 };
49270
49271 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49272     /**
49273      * @cfg {Number/String} width
49274      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49275      */
49276     /**
49277      * @cfg {String/Object} autoCreate
49278      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49279      */
49280
49281     // private
49282     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49283
49284     // private
49285     onRender : function(ct, position){
49286         Roo.form.Column.superclass.onRender.call(this, ct, position);
49287         if(this.width){
49288             this.el.setWidth(this.width);
49289         }
49290     }
49291 });
49292
49293
49294 /**
49295  * @class Roo.form.Row
49296  * @extends Roo.form.Layout
49297  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49298  * @constructor
49299  * @param {Object} config Configuration options
49300  */
49301
49302  
49303 Roo.form.Row = function(config){
49304     Roo.form.Row.superclass.constructor.call(this, config);
49305 };
49306  
49307 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49308       /**
49309      * @cfg {Number/String} width
49310      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49311      */
49312     /**
49313      * @cfg {Number/String} height
49314      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49315      */
49316     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49317     
49318     padWidth : 20,
49319     // private
49320     onRender : function(ct, position){
49321         //console.log('row render');
49322         if(!this.rowTpl){
49323             var t = new Roo.Template(
49324                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49325                     '<label for="{0}" style="{2}">{1}{4}</label>',
49326                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49327                     '</div>',
49328                 '</div>'
49329             );
49330             t.disableFormats = true;
49331             t.compile();
49332             Roo.form.Layout.prototype.rowTpl = t;
49333         }
49334         this.fieldTpl = this.rowTpl;
49335         
49336         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49337         var labelWidth = 100;
49338         
49339         if ((this.labelAlign != 'top')) {
49340             if (typeof this.labelWidth == 'number') {
49341                 labelWidth = this.labelWidth
49342             }
49343             this.padWidth =  20 + labelWidth;
49344             
49345         }
49346         
49347         Roo.form.Column.superclass.onRender.call(this, ct, position);
49348         if(this.width){
49349             this.el.setWidth(this.width);
49350         }
49351         if(this.height){
49352             this.el.setHeight(this.height);
49353         }
49354     },
49355     
49356     // private
49357     renderField : function(f){
49358         f.fieldEl = this.fieldTpl.append(this.el, [
49359                f.id, f.fieldLabel,
49360                f.labelStyle||this.labelStyle||'',
49361                this.elementStyle||'',
49362                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49363                f.itemCls||this.itemCls||'',
49364                f.width ? f.width + this.padWidth : 160 + this.padWidth
49365        ],true);
49366     }
49367 });
49368  
49369
49370 /**
49371  * @class Roo.form.FieldSet
49372  * @extends Roo.form.Layout
49373  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49374  * @constructor
49375  * @param {Object} config Configuration options
49376  */
49377 Roo.form.FieldSet = function(config){
49378     Roo.form.FieldSet.superclass.constructor.call(this, config);
49379 };
49380
49381 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49382     /**
49383      * @cfg {String} legend
49384      * The text to display as the legend for the FieldSet (defaults to '')
49385      */
49386     /**
49387      * @cfg {String/Object} autoCreate
49388      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49389      */
49390
49391     // private
49392     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49393
49394     // private
49395     onRender : function(ct, position){
49396         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49397         if(this.legend){
49398             this.setLegend(this.legend);
49399         }
49400     },
49401
49402     // private
49403     setLegend : function(text){
49404         if(this.rendered){
49405             this.el.child('legend').update(text);
49406         }
49407     }
49408 });/*
49409  * Based on:
49410  * Ext JS Library 1.1.1
49411  * Copyright(c) 2006-2007, Ext JS, LLC.
49412  *
49413  * Originally Released Under LGPL - original licence link has changed is not relivant.
49414  *
49415  * Fork - LGPL
49416  * <script type="text/javascript">
49417  */
49418 /**
49419  * @class Roo.form.VTypes
49420  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49421  * @singleton
49422  */
49423 Roo.form.VTypes = function(){
49424     // closure these in so they are only created once.
49425     var alpha = /^[a-zA-Z_]+$/;
49426     var alphanum = /^[a-zA-Z0-9_]+$/;
49427     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49428     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49429
49430     // All these messages and functions are configurable
49431     return {
49432         /**
49433          * The function used to validate email addresses
49434          * @param {String} value The email address
49435          */
49436         'email' : function(v){
49437             return email.test(v);
49438         },
49439         /**
49440          * The error text to display when the email validation function returns false
49441          * @type String
49442          */
49443         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49444         /**
49445          * The keystroke filter mask to be applied on email input
49446          * @type RegExp
49447          */
49448         'emailMask' : /[a-z0-9_\.\-@]/i,
49449
49450         /**
49451          * The function used to validate URLs
49452          * @param {String} value The URL
49453          */
49454         'url' : function(v){
49455             return url.test(v);
49456         },
49457         /**
49458          * The error text to display when the url validation function returns false
49459          * @type String
49460          */
49461         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49462         
49463         /**
49464          * The function used to validate alpha values
49465          * @param {String} value The value
49466          */
49467         'alpha' : function(v){
49468             return alpha.test(v);
49469         },
49470         /**
49471          * The error text to display when the alpha validation function returns false
49472          * @type String
49473          */
49474         'alphaText' : 'This field should only contain letters and _',
49475         /**
49476          * The keystroke filter mask to be applied on alpha input
49477          * @type RegExp
49478          */
49479         'alphaMask' : /[a-z_]/i,
49480
49481         /**
49482          * The function used to validate alphanumeric values
49483          * @param {String} value The value
49484          */
49485         'alphanum' : function(v){
49486             return alphanum.test(v);
49487         },
49488         /**
49489          * The error text to display when the alphanumeric validation function returns false
49490          * @type String
49491          */
49492         'alphanumText' : 'This field should only contain letters, numbers and _',
49493         /**
49494          * The keystroke filter mask to be applied on alphanumeric input
49495          * @type RegExp
49496          */
49497         'alphanumMask' : /[a-z0-9_]/i
49498     };
49499 }();//<script type="text/javascript">
49500
49501 /**
49502  * @class Roo.form.FCKeditor
49503  * @extends Roo.form.TextArea
49504  * Wrapper around the FCKEditor http://www.fckeditor.net
49505  * @constructor
49506  * Creates a new FCKeditor
49507  * @param {Object} config Configuration options
49508  */
49509 Roo.form.FCKeditor = function(config){
49510     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49511     this.addEvents({
49512          /**
49513          * @event editorinit
49514          * Fired when the editor is initialized - you can add extra handlers here..
49515          * @param {FCKeditor} this
49516          * @param {Object} the FCK object.
49517          */
49518         editorinit : true
49519     });
49520     
49521     
49522 };
49523 Roo.form.FCKeditor.editors = { };
49524 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49525 {
49526     //defaultAutoCreate : {
49527     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49528     //},
49529     // private
49530     /**
49531      * @cfg {Object} fck options - see fck manual for details.
49532      */
49533     fckconfig : false,
49534     
49535     /**
49536      * @cfg {Object} fck toolbar set (Basic or Default)
49537      */
49538     toolbarSet : 'Basic',
49539     /**
49540      * @cfg {Object} fck BasePath
49541      */ 
49542     basePath : '/fckeditor/',
49543     
49544     
49545     frame : false,
49546     
49547     value : '',
49548     
49549    
49550     onRender : function(ct, position)
49551     {
49552         if(!this.el){
49553             this.defaultAutoCreate = {
49554                 tag: "textarea",
49555                 style:"width:300px;height:60px;",
49556                 autocomplete: "new-password"
49557             };
49558         }
49559         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49560         /*
49561         if(this.grow){
49562             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49563             if(this.preventScrollbars){
49564                 this.el.setStyle("overflow", "hidden");
49565             }
49566             this.el.setHeight(this.growMin);
49567         }
49568         */
49569         //console.log('onrender' + this.getId() );
49570         Roo.form.FCKeditor.editors[this.getId()] = this;
49571          
49572
49573         this.replaceTextarea() ;
49574         
49575     },
49576     
49577     getEditor : function() {
49578         return this.fckEditor;
49579     },
49580     /**
49581      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49582      * @param {Mixed} value The value to set
49583      */
49584     
49585     
49586     setValue : function(value)
49587     {
49588         //console.log('setValue: ' + value);
49589         
49590         if(typeof(value) == 'undefined') { // not sure why this is happending...
49591             return;
49592         }
49593         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49594         
49595         //if(!this.el || !this.getEditor()) {
49596         //    this.value = value;
49597             //this.setValue.defer(100,this,[value]);    
49598         //    return;
49599         //} 
49600         
49601         if(!this.getEditor()) {
49602             return;
49603         }
49604         
49605         this.getEditor().SetData(value);
49606         
49607         //
49608
49609     },
49610
49611     /**
49612      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49613      * @return {Mixed} value The field value
49614      */
49615     getValue : function()
49616     {
49617         
49618         if (this.frame && this.frame.dom.style.display == 'none') {
49619             return Roo.form.FCKeditor.superclass.getValue.call(this);
49620         }
49621         
49622         if(!this.el || !this.getEditor()) {
49623            
49624            // this.getValue.defer(100,this); 
49625             return this.value;
49626         }
49627        
49628         
49629         var value=this.getEditor().GetData();
49630         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49631         return Roo.form.FCKeditor.superclass.getValue.call(this);
49632         
49633
49634     },
49635
49636     /**
49637      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49638      * @return {Mixed} value The field value
49639      */
49640     getRawValue : function()
49641     {
49642         if (this.frame && this.frame.dom.style.display == 'none') {
49643             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49644         }
49645         
49646         if(!this.el || !this.getEditor()) {
49647             //this.getRawValue.defer(100,this); 
49648             return this.value;
49649             return;
49650         }
49651         
49652         
49653         
49654         var value=this.getEditor().GetData();
49655         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49656         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49657          
49658     },
49659     
49660     setSize : function(w,h) {
49661         
49662         
49663         
49664         //if (this.frame && this.frame.dom.style.display == 'none') {
49665         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49666         //    return;
49667         //}
49668         //if(!this.el || !this.getEditor()) {
49669         //    this.setSize.defer(100,this, [w,h]); 
49670         //    return;
49671         //}
49672         
49673         
49674         
49675         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49676         
49677         this.frame.dom.setAttribute('width', w);
49678         this.frame.dom.setAttribute('height', h);
49679         this.frame.setSize(w,h);
49680         
49681     },
49682     
49683     toggleSourceEdit : function(value) {
49684         
49685       
49686          
49687         this.el.dom.style.display = value ? '' : 'none';
49688         this.frame.dom.style.display = value ?  'none' : '';
49689         
49690     },
49691     
49692     
49693     focus: function(tag)
49694     {
49695         if (this.frame.dom.style.display == 'none') {
49696             return Roo.form.FCKeditor.superclass.focus.call(this);
49697         }
49698         if(!this.el || !this.getEditor()) {
49699             this.focus.defer(100,this, [tag]); 
49700             return;
49701         }
49702         
49703         
49704         
49705         
49706         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49707         this.getEditor().Focus();
49708         if (tgs.length) {
49709             if (!this.getEditor().Selection.GetSelection()) {
49710                 this.focus.defer(100,this, [tag]); 
49711                 return;
49712             }
49713             
49714             
49715             var r = this.getEditor().EditorDocument.createRange();
49716             r.setStart(tgs[0],0);
49717             r.setEnd(tgs[0],0);
49718             this.getEditor().Selection.GetSelection().removeAllRanges();
49719             this.getEditor().Selection.GetSelection().addRange(r);
49720             this.getEditor().Focus();
49721         }
49722         
49723     },
49724     
49725     
49726     
49727     replaceTextarea : function()
49728     {
49729         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49730             return ;
49731         }
49732         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49733         //{
49734             // We must check the elements firstly using the Id and then the name.
49735         var oTextarea = document.getElementById( this.getId() );
49736         
49737         var colElementsByName = document.getElementsByName( this.getId() ) ;
49738          
49739         oTextarea.style.display = 'none' ;
49740
49741         if ( oTextarea.tabIndex ) {            
49742             this.TabIndex = oTextarea.tabIndex ;
49743         }
49744         
49745         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49746         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49747         this.frame = Roo.get(this.getId() + '___Frame')
49748     },
49749     
49750     _getConfigHtml : function()
49751     {
49752         var sConfig = '' ;
49753
49754         for ( var o in this.fckconfig ) {
49755             sConfig += sConfig.length > 0  ? '&amp;' : '';
49756             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49757         }
49758
49759         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49760     },
49761     
49762     
49763     _getIFrameHtml : function()
49764     {
49765         var sFile = 'fckeditor.html' ;
49766         /* no idea what this is about..
49767         try
49768         {
49769             if ( (/fcksource=true/i).test( window.top.location.search ) )
49770                 sFile = 'fckeditor.original.html' ;
49771         }
49772         catch (e) { 
49773         */
49774
49775         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49776         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49777         
49778         
49779         var html = '<iframe id="' + this.getId() +
49780             '___Frame" src="' + sLink +
49781             '" width="' + this.width +
49782             '" height="' + this.height + '"' +
49783             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49784             ' frameborder="0" scrolling="no"></iframe>' ;
49785
49786         return html ;
49787     },
49788     
49789     _insertHtmlBefore : function( html, element )
49790     {
49791         if ( element.insertAdjacentHTML )       {
49792             // IE
49793             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49794         } else { // Gecko
49795             var oRange = document.createRange() ;
49796             oRange.setStartBefore( element ) ;
49797             var oFragment = oRange.createContextualFragment( html );
49798             element.parentNode.insertBefore( oFragment, element ) ;
49799         }
49800     }
49801     
49802     
49803   
49804     
49805     
49806     
49807     
49808
49809 });
49810
49811 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49812
49813 function FCKeditor_OnComplete(editorInstance){
49814     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49815     f.fckEditor = editorInstance;
49816     //console.log("loaded");
49817     f.fireEvent('editorinit', f, editorInstance);
49818
49819   
49820
49821  
49822
49823
49824
49825
49826
49827
49828
49829
49830
49831
49832
49833
49834
49835
49836
49837 //<script type="text/javascript">
49838 /**
49839  * @class Roo.form.GridField
49840  * @extends Roo.form.Field
49841  * Embed a grid (or editable grid into a form)
49842  * STATUS ALPHA
49843  * 
49844  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49845  * it needs 
49846  * xgrid.store = Roo.data.Store
49847  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49848  * xgrid.store.reader = Roo.data.JsonReader 
49849  * 
49850  * 
49851  * @constructor
49852  * Creates a new GridField
49853  * @param {Object} config Configuration options
49854  */
49855 Roo.form.GridField = function(config){
49856     Roo.form.GridField.superclass.constructor.call(this, config);
49857      
49858 };
49859
49860 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49861     /**
49862      * @cfg {Number} width  - used to restrict width of grid..
49863      */
49864     width : 100,
49865     /**
49866      * @cfg {Number} height - used to restrict height of grid..
49867      */
49868     height : 50,
49869      /**
49870      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49871          * 
49872          *}
49873      */
49874     xgrid : false, 
49875     /**
49876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49877      * {tag: "input", type: "checkbox", autocomplete: "off"})
49878      */
49879    // defaultAutoCreate : { tag: 'div' },
49880     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49881     /**
49882      * @cfg {String} addTitle Text to include for adding a title.
49883      */
49884     addTitle : false,
49885     //
49886     onResize : function(){
49887         Roo.form.Field.superclass.onResize.apply(this, arguments);
49888     },
49889
49890     initEvents : function(){
49891         // Roo.form.Checkbox.superclass.initEvents.call(this);
49892         // has no events...
49893        
49894     },
49895
49896
49897     getResizeEl : function(){
49898         return this.wrap;
49899     },
49900
49901     getPositionEl : function(){
49902         return this.wrap;
49903     },
49904
49905     // private
49906     onRender : function(ct, position){
49907         
49908         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49909         var style = this.style;
49910         delete this.style;
49911         
49912         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49913         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49914         this.viewEl = this.wrap.createChild({ tag: 'div' });
49915         if (style) {
49916             this.viewEl.applyStyles(style);
49917         }
49918         if (this.width) {
49919             this.viewEl.setWidth(this.width);
49920         }
49921         if (this.height) {
49922             this.viewEl.setHeight(this.height);
49923         }
49924         //if(this.inputValue !== undefined){
49925         //this.setValue(this.value);
49926         
49927         
49928         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49929         
49930         
49931         this.grid.render();
49932         this.grid.getDataSource().on('remove', this.refreshValue, this);
49933         this.grid.getDataSource().on('update', this.refreshValue, this);
49934         this.grid.on('afteredit', this.refreshValue, this);
49935  
49936     },
49937      
49938     
49939     /**
49940      * Sets the value of the item. 
49941      * @param {String} either an object  or a string..
49942      */
49943     setValue : function(v){
49944         //this.value = v;
49945         v = v || []; // empty set..
49946         // this does not seem smart - it really only affects memoryproxy grids..
49947         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49948             var ds = this.grid.getDataSource();
49949             // assumes a json reader..
49950             var data = {}
49951             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49952             ds.loadData( data);
49953         }
49954         // clear selection so it does not get stale.
49955         if (this.grid.sm) { 
49956             this.grid.sm.clearSelections();
49957         }
49958         
49959         Roo.form.GridField.superclass.setValue.call(this, v);
49960         this.refreshValue();
49961         // should load data in the grid really....
49962     },
49963     
49964     // private
49965     refreshValue: function() {
49966          var val = [];
49967         this.grid.getDataSource().each(function(r) {
49968             val.push(r.data);
49969         });
49970         this.el.dom.value = Roo.encode(val);
49971     }
49972     
49973      
49974     
49975     
49976 });/*
49977  * Based on:
49978  * Ext JS Library 1.1.1
49979  * Copyright(c) 2006-2007, Ext JS, LLC.
49980  *
49981  * Originally Released Under LGPL - original licence link has changed is not relivant.
49982  *
49983  * Fork - LGPL
49984  * <script type="text/javascript">
49985  */
49986 /**
49987  * @class Roo.form.DisplayField
49988  * @extends Roo.form.Field
49989  * A generic Field to display non-editable data.
49990  * @cfg {Boolean} closable (true|false) default false
49991  * @constructor
49992  * Creates a new Display Field item.
49993  * @param {Object} config Configuration options
49994  */
49995 Roo.form.DisplayField = function(config){
49996     Roo.form.DisplayField.superclass.constructor.call(this, config);
49997     
49998     this.addEvents({
49999         /**
50000          * @event close
50001          * Fires after the click the close btn
50002              * @param {Roo.form.DisplayField} this
50003              */
50004         close : true
50005     });
50006 };
50007
50008 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50009     inputType:      'hidden',
50010     allowBlank:     true,
50011     readOnly:         true,
50012     
50013  
50014     /**
50015      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50016      */
50017     focusClass : undefined,
50018     /**
50019      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50020      */
50021     fieldClass: 'x-form-field',
50022     
50023      /**
50024      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50025      */
50026     valueRenderer: undefined,
50027     
50028     width: 100,
50029     /**
50030      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50031      * {tag: "input", type: "checkbox", autocomplete: "off"})
50032      */
50033      
50034  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50035  
50036     closable : false,
50037     
50038     onResize : function(){
50039         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50040         
50041     },
50042
50043     initEvents : function(){
50044         // Roo.form.Checkbox.superclass.initEvents.call(this);
50045         // has no events...
50046         
50047         if(this.closable){
50048             this.closeEl.on('click', this.onClose, this);
50049         }
50050        
50051     },
50052
50053
50054     getResizeEl : function(){
50055         return this.wrap;
50056     },
50057
50058     getPositionEl : function(){
50059         return this.wrap;
50060     },
50061
50062     // private
50063     onRender : function(ct, position){
50064         
50065         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50066         //if(this.inputValue !== undefined){
50067         this.wrap = this.el.wrap();
50068         
50069         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50070         
50071         if(this.closable){
50072             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50073         }
50074         
50075         if (this.bodyStyle) {
50076             this.viewEl.applyStyles(this.bodyStyle);
50077         }
50078         //this.viewEl.setStyle('padding', '2px');
50079         
50080         this.setValue(this.value);
50081         
50082     },
50083 /*
50084     // private
50085     initValue : Roo.emptyFn,
50086
50087   */
50088
50089         // private
50090     onClick : function(){
50091         
50092     },
50093
50094     /**
50095      * Sets the checked state of the checkbox.
50096      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50097      */
50098     setValue : function(v){
50099         this.value = v;
50100         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50101         // this might be called before we have a dom element..
50102         if (!this.viewEl) {
50103             return;
50104         }
50105         this.viewEl.dom.innerHTML = html;
50106         Roo.form.DisplayField.superclass.setValue.call(this, v);
50107
50108     },
50109     
50110     onClose : function(e)
50111     {
50112         e.preventDefault();
50113         
50114         this.fireEvent('close', this);
50115     }
50116 });/*
50117  * 
50118  * Licence- LGPL
50119  * 
50120  */
50121
50122 /**
50123  * @class Roo.form.DayPicker
50124  * @extends Roo.form.Field
50125  * A Day picker show [M] [T] [W] ....
50126  * @constructor
50127  * Creates a new Day Picker
50128  * @param {Object} config Configuration options
50129  */
50130 Roo.form.DayPicker= function(config){
50131     Roo.form.DayPicker.superclass.constructor.call(this, config);
50132      
50133 };
50134
50135 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50136     /**
50137      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50138      */
50139     focusClass : undefined,
50140     /**
50141      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50142      */
50143     fieldClass: "x-form-field",
50144    
50145     /**
50146      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50147      * {tag: "input", type: "checkbox", autocomplete: "off"})
50148      */
50149     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50150     
50151    
50152     actionMode : 'viewEl', 
50153     //
50154     // private
50155  
50156     inputType : 'hidden',
50157     
50158      
50159     inputElement: false, // real input element?
50160     basedOn: false, // ????
50161     
50162     isFormField: true, // not sure where this is needed!!!!
50163
50164     onResize : function(){
50165         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50166         if(!this.boxLabel){
50167             this.el.alignTo(this.wrap, 'c-c');
50168         }
50169     },
50170
50171     initEvents : function(){
50172         Roo.form.Checkbox.superclass.initEvents.call(this);
50173         this.el.on("click", this.onClick,  this);
50174         this.el.on("change", this.onClick,  this);
50175     },
50176
50177
50178     getResizeEl : function(){
50179         return this.wrap;
50180     },
50181
50182     getPositionEl : function(){
50183         return this.wrap;
50184     },
50185
50186     
50187     // private
50188     onRender : function(ct, position){
50189         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50190        
50191         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50192         
50193         var r1 = '<table><tr>';
50194         var r2 = '<tr class="x-form-daypick-icons">';
50195         for (var i=0; i < 7; i++) {
50196             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50197             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50198         }
50199         
50200         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50201         viewEl.select('img').on('click', this.onClick, this);
50202         this.viewEl = viewEl;   
50203         
50204         
50205         // this will not work on Chrome!!!
50206         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50207         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50208         
50209         
50210           
50211
50212     },
50213
50214     // private
50215     initValue : Roo.emptyFn,
50216
50217     /**
50218      * Returns the checked state of the checkbox.
50219      * @return {Boolean} True if checked, else false
50220      */
50221     getValue : function(){
50222         return this.el.dom.value;
50223         
50224     },
50225
50226         // private
50227     onClick : function(e){ 
50228         //this.setChecked(!this.checked);
50229         Roo.get(e.target).toggleClass('x-menu-item-checked');
50230         this.refreshValue();
50231         //if(this.el.dom.checked != this.checked){
50232         //    this.setValue(this.el.dom.checked);
50233        // }
50234     },
50235     
50236     // private
50237     refreshValue : function()
50238     {
50239         var val = '';
50240         this.viewEl.select('img',true).each(function(e,i,n)  {
50241             val += e.is(".x-menu-item-checked") ? String(n) : '';
50242         });
50243         this.setValue(val, true);
50244     },
50245
50246     /**
50247      * Sets the checked state of the checkbox.
50248      * On is always based on a string comparison between inputValue and the param.
50249      * @param {Boolean/String} value - the value to set 
50250      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50251      */
50252     setValue : function(v,suppressEvent){
50253         if (!this.el.dom) {
50254             return;
50255         }
50256         var old = this.el.dom.value ;
50257         this.el.dom.value = v;
50258         if (suppressEvent) {
50259             return ;
50260         }
50261          
50262         // update display..
50263         this.viewEl.select('img',true).each(function(e,i,n)  {
50264             
50265             var on = e.is(".x-menu-item-checked");
50266             var newv = v.indexOf(String(n)) > -1;
50267             if (on != newv) {
50268                 e.toggleClass('x-menu-item-checked');
50269             }
50270             
50271         });
50272         
50273         
50274         this.fireEvent('change', this, v, old);
50275         
50276         
50277     },
50278    
50279     // handle setting of hidden value by some other method!!?!?
50280     setFromHidden: function()
50281     {
50282         if(!this.el){
50283             return;
50284         }
50285         //console.log("SET FROM HIDDEN");
50286         //alert('setFrom hidden');
50287         this.setValue(this.el.dom.value);
50288     },
50289     
50290     onDestroy : function()
50291     {
50292         if(this.viewEl){
50293             Roo.get(this.viewEl).remove();
50294         }
50295          
50296         Roo.form.DayPicker.superclass.onDestroy.call(this);
50297     }
50298
50299 });/*
50300  * RooJS Library 1.1.1
50301  * Copyright(c) 2008-2011  Alan Knowles
50302  *
50303  * License - LGPL
50304  */
50305  
50306
50307 /**
50308  * @class Roo.form.ComboCheck
50309  * @extends Roo.form.ComboBox
50310  * A combobox for multiple select items.
50311  *
50312  * FIXME - could do with a reset button..
50313  * 
50314  * @constructor
50315  * Create a new ComboCheck
50316  * @param {Object} config Configuration options
50317  */
50318 Roo.form.ComboCheck = function(config){
50319     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50320     // should verify some data...
50321     // like
50322     // hiddenName = required..
50323     // displayField = required
50324     // valudField == required
50325     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50326     var _t = this;
50327     Roo.each(req, function(e) {
50328         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50329             throw "Roo.form.ComboCheck : missing value for: " + e;
50330         }
50331     });
50332     
50333     
50334 };
50335
50336 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50337      
50338      
50339     editable : false,
50340      
50341     selectedClass: 'x-menu-item-checked', 
50342     
50343     // private
50344     onRender : function(ct, position){
50345         var _t = this;
50346         
50347         
50348         
50349         if(!this.tpl){
50350             var cls = 'x-combo-list';
50351
50352             
50353             this.tpl =  new Roo.Template({
50354                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50355                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50356                    '<span>{' + this.displayField + '}</span>' +
50357                     '</div>' 
50358                 
50359             });
50360         }
50361  
50362         
50363         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50364         this.view.singleSelect = false;
50365         this.view.multiSelect = true;
50366         this.view.toggleSelect = true;
50367         this.pageTb.add(new Roo.Toolbar.Fill(), {
50368             
50369             text: 'Done',
50370             handler: function()
50371             {
50372                 _t.collapse();
50373             }
50374         });
50375     },
50376     
50377     onViewOver : function(e, t){
50378         // do nothing...
50379         return;
50380         
50381     },
50382     
50383     onViewClick : function(doFocus,index){
50384         return;
50385         
50386     },
50387     select: function () {
50388         //Roo.log("SELECT CALLED");
50389     },
50390      
50391     selectByValue : function(xv, scrollIntoView){
50392         var ar = this.getValueArray();
50393         var sels = [];
50394         
50395         Roo.each(ar, function(v) {
50396             if(v === undefined || v === null){
50397                 return;
50398             }
50399             var r = this.findRecord(this.valueField, v);
50400             if(r){
50401                 sels.push(this.store.indexOf(r))
50402                 
50403             }
50404         },this);
50405         this.view.select(sels);
50406         return false;
50407     },
50408     
50409     
50410     
50411     onSelect : function(record, index){
50412        // Roo.log("onselect Called");
50413        // this is only called by the clear button now..
50414         this.view.clearSelections();
50415         this.setValue('[]');
50416         if (this.value != this.valueBefore) {
50417             this.fireEvent('change', this, this.value, this.valueBefore);
50418             this.valueBefore = this.value;
50419         }
50420     },
50421     getValueArray : function()
50422     {
50423         var ar = [] ;
50424         
50425         try {
50426             //Roo.log(this.value);
50427             if (typeof(this.value) == 'undefined') {
50428                 return [];
50429             }
50430             var ar = Roo.decode(this.value);
50431             return  ar instanceof Array ? ar : []; //?? valid?
50432             
50433         } catch(e) {
50434             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50435             return [];
50436         }
50437          
50438     },
50439     expand : function ()
50440     {
50441         
50442         Roo.form.ComboCheck.superclass.expand.call(this);
50443         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50444         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50445         
50446
50447     },
50448     
50449     collapse : function(){
50450         Roo.form.ComboCheck.superclass.collapse.call(this);
50451         var sl = this.view.getSelectedIndexes();
50452         var st = this.store;
50453         var nv = [];
50454         var tv = [];
50455         var r;
50456         Roo.each(sl, function(i) {
50457             r = st.getAt(i);
50458             nv.push(r.get(this.valueField));
50459         },this);
50460         this.setValue(Roo.encode(nv));
50461         if (this.value != this.valueBefore) {
50462
50463             this.fireEvent('change', this, this.value, this.valueBefore);
50464             this.valueBefore = this.value;
50465         }
50466         
50467     },
50468     
50469     setValue : function(v){
50470         // Roo.log(v);
50471         this.value = v;
50472         
50473         var vals = this.getValueArray();
50474         var tv = [];
50475         Roo.each(vals, function(k) {
50476             var r = this.findRecord(this.valueField, k);
50477             if(r){
50478                 tv.push(r.data[this.displayField]);
50479             }else if(this.valueNotFoundText !== undefined){
50480                 tv.push( this.valueNotFoundText );
50481             }
50482         },this);
50483        // Roo.log(tv);
50484         
50485         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50486         this.hiddenField.value = v;
50487         this.value = v;
50488     }
50489     
50490 });/*
50491  * Based on:
50492  * Ext JS Library 1.1.1
50493  * Copyright(c) 2006-2007, Ext JS, LLC.
50494  *
50495  * Originally Released Under LGPL - original licence link has changed is not relivant.
50496  *
50497  * Fork - LGPL
50498  * <script type="text/javascript">
50499  */
50500  
50501 /**
50502  * @class Roo.form.Signature
50503  * @extends Roo.form.Field
50504  * Signature field.  
50505  * @constructor
50506  * 
50507  * @param {Object} config Configuration options
50508  */
50509
50510 Roo.form.Signature = function(config){
50511     Roo.form.Signature.superclass.constructor.call(this, config);
50512     
50513     this.addEvents({// not in used??
50514          /**
50515          * @event confirm
50516          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50517              * @param {Roo.form.Signature} combo This combo box
50518              */
50519         'confirm' : true,
50520         /**
50521          * @event reset
50522          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50523              * @param {Roo.form.ComboBox} combo This combo box
50524              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50525              */
50526         'reset' : true
50527     });
50528 };
50529
50530 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50531     /**
50532      * @cfg {Object} labels Label to use when rendering a form.
50533      * defaults to 
50534      * labels : { 
50535      *      clear : "Clear",
50536      *      confirm : "Confirm"
50537      *  }
50538      */
50539     labels : { 
50540         clear : "Clear",
50541         confirm : "Confirm"
50542     },
50543     /**
50544      * @cfg {Number} width The signature panel width (defaults to 300)
50545      */
50546     width: 300,
50547     /**
50548      * @cfg {Number} height The signature panel height (defaults to 100)
50549      */
50550     height : 100,
50551     /**
50552      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50553      */
50554     allowBlank : false,
50555     
50556     //private
50557     // {Object} signPanel The signature SVG panel element (defaults to {})
50558     signPanel : {},
50559     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50560     isMouseDown : false,
50561     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50562     isConfirmed : false,
50563     // {String} signatureTmp SVG mapping string (defaults to empty string)
50564     signatureTmp : '',
50565     
50566     
50567     defaultAutoCreate : { // modified by initCompnoent..
50568         tag: "input",
50569         type:"hidden"
50570     },
50571
50572     // private
50573     onRender : function(ct, position){
50574         
50575         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50576         
50577         this.wrap = this.el.wrap({
50578             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50579         });
50580         
50581         this.createToolbar(this);
50582         this.signPanel = this.wrap.createChild({
50583                 tag: 'div',
50584                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50585             }, this.el
50586         );
50587             
50588         this.svgID = Roo.id();
50589         this.svgEl = this.signPanel.createChild({
50590               xmlns : 'http://www.w3.org/2000/svg',
50591               tag : 'svg',
50592               id : this.svgID + "-svg",
50593               width: this.width,
50594               height: this.height,
50595               viewBox: '0 0 '+this.width+' '+this.height,
50596               cn : [
50597                 {
50598                     tag: "rect",
50599                     id: this.svgID + "-svg-r",
50600                     width: this.width,
50601                     height: this.height,
50602                     fill: "#ffa"
50603                 },
50604                 {
50605                     tag: "line",
50606                     id: this.svgID + "-svg-l",
50607                     x1: "0", // start
50608                     y1: (this.height*0.8), // start set the line in 80% of height
50609                     x2: this.width, // end
50610                     y2: (this.height*0.8), // end set the line in 80% of height
50611                     'stroke': "#666",
50612                     'stroke-width': "1",
50613                     'stroke-dasharray': "3",
50614                     'shape-rendering': "crispEdges",
50615                     'pointer-events': "none"
50616                 },
50617                 {
50618                     tag: "path",
50619                     id: this.svgID + "-svg-p",
50620                     'stroke': "navy",
50621                     'stroke-width': "3",
50622                     'fill': "none",
50623                     'pointer-events': 'none'
50624                 }
50625               ]
50626         });
50627         this.createSVG();
50628         this.svgBox = this.svgEl.dom.getScreenCTM();
50629     },
50630     createSVG : function(){ 
50631         var svg = this.signPanel;
50632         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50633         var t = this;
50634
50635         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50636         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50637         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50638         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50639         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50640         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50641         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50642         
50643     },
50644     isTouchEvent : function(e){
50645         return e.type.match(/^touch/);
50646     },
50647     getCoords : function (e) {
50648         var pt    = this.svgEl.dom.createSVGPoint();
50649         pt.x = e.clientX; 
50650         pt.y = e.clientY;
50651         if (this.isTouchEvent(e)) {
50652             pt.x =  e.targetTouches[0].clientX;
50653             pt.y = e.targetTouches[0].clientY;
50654         }
50655         var a = this.svgEl.dom.getScreenCTM();
50656         var b = a.inverse();
50657         var mx = pt.matrixTransform(b);
50658         return mx.x + ',' + mx.y;
50659     },
50660     //mouse event headler 
50661     down : function (e) {
50662         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50663         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50664         
50665         this.isMouseDown = true;
50666         
50667         e.preventDefault();
50668     },
50669     move : function (e) {
50670         if (this.isMouseDown) {
50671             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50672             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50673         }
50674         
50675         e.preventDefault();
50676     },
50677     up : function (e) {
50678         this.isMouseDown = false;
50679         var sp = this.signatureTmp.split(' ');
50680         
50681         if(sp.length > 1){
50682             if(!sp[sp.length-2].match(/^L/)){
50683                 sp.pop();
50684                 sp.pop();
50685                 sp.push("");
50686                 this.signatureTmp = sp.join(" ");
50687             }
50688         }
50689         if(this.getValue() != this.signatureTmp){
50690             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50691             this.isConfirmed = false;
50692         }
50693         e.preventDefault();
50694     },
50695     
50696     /**
50697      * Protected method that will not generally be called directly. It
50698      * is called when the editor creates its toolbar. Override this method if you need to
50699      * add custom toolbar buttons.
50700      * @param {HtmlEditor} editor
50701      */
50702     createToolbar : function(editor){
50703          function btn(id, toggle, handler){
50704             var xid = fid + '-'+ id ;
50705             return {
50706                 id : xid,
50707                 cmd : id,
50708                 cls : 'x-btn-icon x-edit-'+id,
50709                 enableToggle:toggle !== false,
50710                 scope: editor, // was editor...
50711                 handler:handler||editor.relayBtnCmd,
50712                 clickEvent:'mousedown',
50713                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50714                 tabIndex:-1
50715             };
50716         }
50717         
50718         
50719         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50720         this.tb = tb;
50721         this.tb.add(
50722            {
50723                 cls : ' x-signature-btn x-signature-'+id,
50724                 scope: editor, // was editor...
50725                 handler: this.reset,
50726                 clickEvent:'mousedown',
50727                 text: this.labels.clear
50728             },
50729             {
50730                  xtype : 'Fill',
50731                  xns: Roo.Toolbar
50732             }, 
50733             {
50734                 cls : '  x-signature-btn x-signature-'+id,
50735                 scope: editor, // was editor...
50736                 handler: this.confirmHandler,
50737                 clickEvent:'mousedown',
50738                 text: this.labels.confirm
50739             }
50740         );
50741     
50742     },
50743     //public
50744     /**
50745      * when user is clicked confirm then show this image.....
50746      * 
50747      * @return {String} Image Data URI
50748      */
50749     getImageDataURI : function(){
50750         var svg = this.svgEl.dom.parentNode.innerHTML;
50751         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50752         return src; 
50753     },
50754     /**
50755      * 
50756      * @return {Boolean} this.isConfirmed
50757      */
50758     getConfirmed : function(){
50759         return this.isConfirmed;
50760     },
50761     /**
50762      * 
50763      * @return {Number} this.width
50764      */
50765     getWidth : function(){
50766         return this.width;
50767     },
50768     /**
50769      * 
50770      * @return {Number} this.height
50771      */
50772     getHeight : function(){
50773         return this.height;
50774     },
50775     // private
50776     getSignature : function(){
50777         return this.signatureTmp;
50778     },
50779     // private
50780     reset : function(){
50781         this.signatureTmp = '';
50782         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50783         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50784         this.isConfirmed = false;
50785         Roo.form.Signature.superclass.reset.call(this);
50786     },
50787     setSignature : function(s){
50788         this.signatureTmp = s;
50789         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50790         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50791         this.setValue(s);
50792         this.isConfirmed = false;
50793         Roo.form.Signature.superclass.reset.call(this);
50794     }, 
50795     test : function(){
50796 //        Roo.log(this.signPanel.dom.contentWindow.up())
50797     },
50798     //private
50799     setConfirmed : function(){
50800         
50801         
50802         
50803 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50804     },
50805     // private
50806     confirmHandler : function(){
50807         if(!this.getSignature()){
50808             return;
50809         }
50810         
50811         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50812         this.setValue(this.getSignature());
50813         this.isConfirmed = true;
50814         
50815         this.fireEvent('confirm', this);
50816     },
50817     // private
50818     // Subclasses should provide the validation implementation by overriding this
50819     validateValue : function(value){
50820         if(this.allowBlank){
50821             return true;
50822         }
50823         
50824         if(this.isConfirmed){
50825             return true;
50826         }
50827         return false;
50828     }
50829 });/*
50830  * Based on:
50831  * Ext JS Library 1.1.1
50832  * Copyright(c) 2006-2007, Ext JS, LLC.
50833  *
50834  * Originally Released Under LGPL - original licence link has changed is not relivant.
50835  *
50836  * Fork - LGPL
50837  * <script type="text/javascript">
50838  */
50839  
50840
50841 /**
50842  * @class Roo.form.ComboBox
50843  * @extends Roo.form.TriggerField
50844  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50845  * @constructor
50846  * Create a new ComboBox.
50847  * @param {Object} config Configuration options
50848  */
50849 Roo.form.Select = function(config){
50850     Roo.form.Select.superclass.constructor.call(this, config);
50851      
50852 };
50853
50854 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50855     /**
50856      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50857      */
50858     /**
50859      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50860      * rendering into an Roo.Editor, defaults to false)
50861      */
50862     /**
50863      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50864      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50865      */
50866     /**
50867      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50868      */
50869     /**
50870      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50871      * the dropdown list (defaults to undefined, with no header element)
50872      */
50873
50874      /**
50875      * @cfg {String/Roo.Template} tpl The template to use to render the output
50876      */
50877      
50878     // private
50879     defaultAutoCreate : {tag: "select"  },
50880     /**
50881      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50882      */
50883     listWidth: undefined,
50884     /**
50885      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50886      * mode = 'remote' or 'text' if mode = 'local')
50887      */
50888     displayField: undefined,
50889     /**
50890      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50891      * mode = 'remote' or 'value' if mode = 'local'). 
50892      * Note: use of a valueField requires the user make a selection
50893      * in order for a value to be mapped.
50894      */
50895     valueField: undefined,
50896     
50897     
50898     /**
50899      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50900      * field's data value (defaults to the underlying DOM element's name)
50901      */
50902     hiddenName: undefined,
50903     /**
50904      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50905      */
50906     listClass: '',
50907     /**
50908      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50909      */
50910     selectedClass: 'x-combo-selected',
50911     /**
50912      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50913      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50914      * which displays a downward arrow icon).
50915      */
50916     triggerClass : 'x-form-arrow-trigger',
50917     /**
50918      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50919      */
50920     shadow:'sides',
50921     /**
50922      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50923      * anchor positions (defaults to 'tl-bl')
50924      */
50925     listAlign: 'tl-bl?',
50926     /**
50927      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50928      */
50929     maxHeight: 300,
50930     /**
50931      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50932      * query specified by the allQuery config option (defaults to 'query')
50933      */
50934     triggerAction: 'query',
50935     /**
50936      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50937      * (defaults to 4, does not apply if editable = false)
50938      */
50939     minChars : 4,
50940     /**
50941      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50942      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50943      */
50944     typeAhead: false,
50945     /**
50946      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50947      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50948      */
50949     queryDelay: 500,
50950     /**
50951      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50952      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50953      */
50954     pageSize: 0,
50955     /**
50956      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50957      * when editable = true (defaults to false)
50958      */
50959     selectOnFocus:false,
50960     /**
50961      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50962      */
50963     queryParam: 'query',
50964     /**
50965      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50966      * when mode = 'remote' (defaults to 'Loading...')
50967      */
50968     loadingText: 'Loading...',
50969     /**
50970      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50971      */
50972     resizable: false,
50973     /**
50974      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50975      */
50976     handleHeight : 8,
50977     /**
50978      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50979      * traditional select (defaults to true)
50980      */
50981     editable: true,
50982     /**
50983      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50984      */
50985     allQuery: '',
50986     /**
50987      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50988      */
50989     mode: 'remote',
50990     /**
50991      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50992      * listWidth has a higher value)
50993      */
50994     minListWidth : 70,
50995     /**
50996      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50997      * allow the user to set arbitrary text into the field (defaults to false)
50998      */
50999     forceSelection:false,
51000     /**
51001      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51002      * if typeAhead = true (defaults to 250)
51003      */
51004     typeAheadDelay : 250,
51005     /**
51006      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51007      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51008      */
51009     valueNotFoundText : undefined,
51010     
51011     /**
51012      * @cfg {String} defaultValue The value displayed after loading the store.
51013      */
51014     defaultValue: '',
51015     
51016     /**
51017      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51018      */
51019     blockFocus : false,
51020     
51021     /**
51022      * @cfg {Boolean} disableClear Disable showing of clear button.
51023      */
51024     disableClear : false,
51025     /**
51026      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51027      */
51028     alwaysQuery : false,
51029     
51030     //private
51031     addicon : false,
51032     editicon: false,
51033     
51034     // element that contains real text value.. (when hidden is used..)
51035      
51036     // private
51037     onRender : function(ct, position){
51038         Roo.form.Field.prototype.onRender.call(this, ct, position);
51039         
51040         if(this.store){
51041             this.store.on('beforeload', this.onBeforeLoad, this);
51042             this.store.on('load', this.onLoad, this);
51043             this.store.on('loadexception', this.onLoadException, this);
51044             this.store.load({});
51045         }
51046         
51047         
51048         
51049     },
51050
51051     // private
51052     initEvents : function(){
51053         //Roo.form.ComboBox.superclass.initEvents.call(this);
51054  
51055     },
51056
51057     onDestroy : function(){
51058        
51059         if(this.store){
51060             this.store.un('beforeload', this.onBeforeLoad, this);
51061             this.store.un('load', this.onLoad, this);
51062             this.store.un('loadexception', this.onLoadException, this);
51063         }
51064         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51065     },
51066
51067     // private
51068     fireKey : function(e){
51069         if(e.isNavKeyPress() && !this.list.isVisible()){
51070             this.fireEvent("specialkey", this, e);
51071         }
51072     },
51073
51074     // private
51075     onResize: function(w, h){
51076         
51077         return; 
51078     
51079         
51080     },
51081
51082     /**
51083      * Allow or prevent the user from directly editing the field text.  If false is passed,
51084      * the user will only be able to select from the items defined in the dropdown list.  This method
51085      * is the runtime equivalent of setting the 'editable' config option at config time.
51086      * @param {Boolean} value True to allow the user to directly edit the field text
51087      */
51088     setEditable : function(value){
51089          
51090     },
51091
51092     // private
51093     onBeforeLoad : function(){
51094         
51095         Roo.log("Select before load");
51096         return;
51097     
51098         this.innerList.update(this.loadingText ?
51099                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51100         //this.restrictHeight();
51101         this.selectedIndex = -1;
51102     },
51103
51104     // private
51105     onLoad : function(){
51106
51107     
51108         var dom = this.el.dom;
51109         dom.innerHTML = '';
51110          var od = dom.ownerDocument;
51111          
51112         if (this.emptyText) {
51113             var op = od.createElement('option');
51114             op.setAttribute('value', '');
51115             op.innerHTML = String.format('{0}', this.emptyText);
51116             dom.appendChild(op);
51117         }
51118         if(this.store.getCount() > 0){
51119            
51120             var vf = this.valueField;
51121             var df = this.displayField;
51122             this.store.data.each(function(r) {
51123                 // which colmsn to use... testing - cdoe / title..
51124                 var op = od.createElement('option');
51125                 op.setAttribute('value', r.data[vf]);
51126                 op.innerHTML = String.format('{0}', r.data[df]);
51127                 dom.appendChild(op);
51128             });
51129             if (typeof(this.defaultValue != 'undefined')) {
51130                 this.setValue(this.defaultValue);
51131             }
51132             
51133              
51134         }else{
51135             //this.onEmptyResults();
51136         }
51137         //this.el.focus();
51138     },
51139     // private
51140     onLoadException : function()
51141     {
51142         dom.innerHTML = '';
51143             
51144         Roo.log("Select on load exception");
51145         return;
51146     
51147         this.collapse();
51148         Roo.log(this.store.reader.jsonData);
51149         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51150             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51151         }
51152         
51153         
51154     },
51155     // private
51156     onTypeAhead : function(){
51157          
51158     },
51159
51160     // private
51161     onSelect : function(record, index){
51162         Roo.log('on select?');
51163         return;
51164         if(this.fireEvent('beforeselect', this, record, index) !== false){
51165             this.setFromData(index > -1 ? record.data : false);
51166             this.collapse();
51167             this.fireEvent('select', this, record, index);
51168         }
51169     },
51170
51171     /**
51172      * Returns the currently selected field value or empty string if no value is set.
51173      * @return {String} value The selected value
51174      */
51175     getValue : function(){
51176         var dom = this.el.dom;
51177         this.value = dom.options[dom.selectedIndex].value;
51178         return this.value;
51179         
51180     },
51181
51182     /**
51183      * Clears any text/value currently set in the field
51184      */
51185     clearValue : function(){
51186         this.value = '';
51187         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51188         
51189     },
51190
51191     /**
51192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51193      * will be displayed in the field.  If the value does not match the data value of an existing item,
51194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51195      * Otherwise the field will be blank (although the value will still be set).
51196      * @param {String} value The value to match
51197      */
51198     setValue : function(v){
51199         var d = this.el.dom;
51200         for (var i =0; i < d.options.length;i++) {
51201             if (v == d.options[i].value) {
51202                 d.selectedIndex = i;
51203                 this.value = v;
51204                 return;
51205             }
51206         }
51207         this.clearValue();
51208     },
51209     /**
51210      * @property {Object} the last set data for the element
51211      */
51212     
51213     lastData : false,
51214     /**
51215      * Sets the value of the field based on a object which is related to the record format for the store.
51216      * @param {Object} value the value to set as. or false on reset?
51217      */
51218     setFromData : function(o){
51219         Roo.log('setfrom data?');
51220          
51221         
51222         
51223     },
51224     // private
51225     reset : function(){
51226         this.clearValue();
51227     },
51228     // private
51229     findRecord : function(prop, value){
51230         
51231         return false;
51232     
51233         var record;
51234         if(this.store.getCount() > 0){
51235             this.store.each(function(r){
51236                 if(r.data[prop] == value){
51237                     record = r;
51238                     return false;
51239                 }
51240                 return true;
51241             });
51242         }
51243         return record;
51244     },
51245     
51246     getName: function()
51247     {
51248         // returns hidden if it's set..
51249         if (!this.rendered) {return ''};
51250         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51251         
51252     },
51253      
51254
51255     
51256
51257     // private
51258     onEmptyResults : function(){
51259         Roo.log('empty results');
51260         //this.collapse();
51261     },
51262
51263     /**
51264      * Returns true if the dropdown list is expanded, else false.
51265      */
51266     isExpanded : function(){
51267         return false;
51268     },
51269
51270     /**
51271      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51272      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51273      * @param {String} value The data value of the item to select
51274      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51275      * selected item if it is not currently in view (defaults to true)
51276      * @return {Boolean} True if the value matched an item in the list, else false
51277      */
51278     selectByValue : function(v, scrollIntoView){
51279         Roo.log('select By Value');
51280         return false;
51281     
51282         if(v !== undefined && v !== null){
51283             var r = this.findRecord(this.valueField || this.displayField, v);
51284             if(r){
51285                 this.select(this.store.indexOf(r), scrollIntoView);
51286                 return true;
51287             }
51288         }
51289         return false;
51290     },
51291
51292     /**
51293      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51294      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51295      * @param {Number} index The zero-based index of the list item to select
51296      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51297      * selected item if it is not currently in view (defaults to true)
51298      */
51299     select : function(index, scrollIntoView){
51300         Roo.log('select ');
51301         return  ;
51302         
51303         this.selectedIndex = index;
51304         this.view.select(index);
51305         if(scrollIntoView !== false){
51306             var el = this.view.getNode(index);
51307             if(el){
51308                 this.innerList.scrollChildIntoView(el, false);
51309             }
51310         }
51311     },
51312
51313       
51314
51315     // private
51316     validateBlur : function(){
51317         
51318         return;
51319         
51320     },
51321
51322     // private
51323     initQuery : function(){
51324         this.doQuery(this.getRawValue());
51325     },
51326
51327     // private
51328     doForce : function(){
51329         if(this.el.dom.value.length > 0){
51330             this.el.dom.value =
51331                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51332              
51333         }
51334     },
51335
51336     /**
51337      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51338      * query allowing the query action to be canceled if needed.
51339      * @param {String} query The SQL query to execute
51340      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51341      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51342      * saved in the current store (defaults to false)
51343      */
51344     doQuery : function(q, forceAll){
51345         
51346         Roo.log('doQuery?');
51347         if(q === undefined || q === null){
51348             q = '';
51349         }
51350         var qe = {
51351             query: q,
51352             forceAll: forceAll,
51353             combo: this,
51354             cancel:false
51355         };
51356         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51357             return false;
51358         }
51359         q = qe.query;
51360         forceAll = qe.forceAll;
51361         if(forceAll === true || (q.length >= this.minChars)){
51362             if(this.lastQuery != q || this.alwaysQuery){
51363                 this.lastQuery = q;
51364                 if(this.mode == 'local'){
51365                     this.selectedIndex = -1;
51366                     if(forceAll){
51367                         this.store.clearFilter();
51368                     }else{
51369                         this.store.filter(this.displayField, q);
51370                     }
51371                     this.onLoad();
51372                 }else{
51373                     this.store.baseParams[this.queryParam] = q;
51374                     this.store.load({
51375                         params: this.getParams(q)
51376                     });
51377                     this.expand();
51378                 }
51379             }else{
51380                 this.selectedIndex = -1;
51381                 this.onLoad();   
51382             }
51383         }
51384     },
51385
51386     // private
51387     getParams : function(q){
51388         var p = {};
51389         //p[this.queryParam] = q;
51390         if(this.pageSize){
51391             p.start = 0;
51392             p.limit = this.pageSize;
51393         }
51394         return p;
51395     },
51396
51397     /**
51398      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51399      */
51400     collapse : function(){
51401         
51402     },
51403
51404     // private
51405     collapseIf : function(e){
51406         
51407     },
51408
51409     /**
51410      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51411      */
51412     expand : function(){
51413         
51414     } ,
51415
51416     // private
51417      
51418
51419     /** 
51420     * @cfg {Boolean} grow 
51421     * @hide 
51422     */
51423     /** 
51424     * @cfg {Number} growMin 
51425     * @hide 
51426     */
51427     /** 
51428     * @cfg {Number} growMax 
51429     * @hide 
51430     */
51431     /**
51432      * @hide
51433      * @method autoSize
51434      */
51435     
51436     setWidth : function()
51437     {
51438         
51439     },
51440     getResizeEl : function(){
51441         return this.el;
51442     }
51443 });//<script type="text/javasscript">
51444  
51445
51446 /**
51447  * @class Roo.DDView
51448  * A DnD enabled version of Roo.View.
51449  * @param {Element/String} container The Element in which to create the View.
51450  * @param {String} tpl The template string used to create the markup for each element of the View
51451  * @param {Object} config The configuration properties. These include all the config options of
51452  * {@link Roo.View} plus some specific to this class.<br>
51453  * <p>
51454  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51455  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51456  * <p>
51457  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51458 .x-view-drag-insert-above {
51459         border-top:1px dotted #3366cc;
51460 }
51461 .x-view-drag-insert-below {
51462         border-bottom:1px dotted #3366cc;
51463 }
51464 </code></pre>
51465  * 
51466  */
51467  
51468 Roo.DDView = function(container, tpl, config) {
51469     Roo.DDView.superclass.constructor.apply(this, arguments);
51470     this.getEl().setStyle("outline", "0px none");
51471     this.getEl().unselectable();
51472     if (this.dragGroup) {
51473                 this.setDraggable(this.dragGroup.split(","));
51474     }
51475     if (this.dropGroup) {
51476                 this.setDroppable(this.dropGroup.split(","));
51477     }
51478     if (this.deletable) {
51479         this.setDeletable();
51480     }
51481     this.isDirtyFlag = false;
51482         this.addEvents({
51483                 "drop" : true
51484         });
51485 };
51486
51487 Roo.extend(Roo.DDView, Roo.View, {
51488 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51489 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51490 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51491 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51492
51493         isFormField: true,
51494
51495         reset: Roo.emptyFn,
51496         
51497         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51498
51499         validate: function() {
51500                 return true;
51501         },
51502         
51503         destroy: function() {
51504                 this.purgeListeners();
51505                 this.getEl.removeAllListeners();
51506                 this.getEl().remove();
51507                 if (this.dragZone) {
51508                         if (this.dragZone.destroy) {
51509                                 this.dragZone.destroy();
51510                         }
51511                 }
51512                 if (this.dropZone) {
51513                         if (this.dropZone.destroy) {
51514                                 this.dropZone.destroy();
51515                         }
51516                 }
51517         },
51518
51519 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51520         getName: function() {
51521                 return this.name;
51522         },
51523
51524 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51525         setValue: function(v) {
51526                 if (!this.store) {
51527                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51528                 }
51529                 var data = {};
51530                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51531                 this.store.proxy = new Roo.data.MemoryProxy(data);
51532                 this.store.load();
51533         },
51534
51535 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51536         getValue: function() {
51537                 var result = '(';
51538                 this.store.each(function(rec) {
51539                         result += rec.id + ',';
51540                 });
51541                 return result.substr(0, result.length - 1) + ')';
51542         },
51543         
51544         getIds: function() {
51545                 var i = 0, result = new Array(this.store.getCount());
51546                 this.store.each(function(rec) {
51547                         result[i++] = rec.id;
51548                 });
51549                 return result;
51550         },
51551         
51552         isDirty: function() {
51553                 return this.isDirtyFlag;
51554         },
51555
51556 /**
51557  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51558  *      whole Element becomes the target, and this causes the drop gesture to append.
51559  */
51560     getTargetFromEvent : function(e) {
51561                 var target = e.getTarget();
51562                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51563                 target = target.parentNode;
51564                 }
51565                 if (!target) {
51566                         target = this.el.dom.lastChild || this.el.dom;
51567                 }
51568                 return target;
51569     },
51570
51571 /**
51572  *      Create the drag data which consists of an object which has the property "ddel" as
51573  *      the drag proxy element. 
51574  */
51575     getDragData : function(e) {
51576         var target = this.findItemFromChild(e.getTarget());
51577                 if(target) {
51578                         this.handleSelection(e);
51579                         var selNodes = this.getSelectedNodes();
51580             var dragData = {
51581                 source: this,
51582                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51583                 nodes: selNodes,
51584                 records: []
51585                         };
51586                         var selectedIndices = this.getSelectedIndexes();
51587                         for (var i = 0; i < selectedIndices.length; i++) {
51588                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51589                         }
51590                         if (selNodes.length == 1) {
51591                                 dragData.ddel = target.cloneNode(true); // the div element
51592                         } else {
51593                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51594                                 div.className = 'multi-proxy';
51595                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51596                                         div.appendChild(selNodes[i].cloneNode(true));
51597                                 }
51598                                 dragData.ddel = div;
51599                         }
51600             //console.log(dragData)
51601             //console.log(dragData.ddel.innerHTML)
51602                         return dragData;
51603                 }
51604         //console.log('nodragData')
51605                 return false;
51606     },
51607     
51608 /**     Specify to which ddGroup items in this DDView may be dragged. */
51609     setDraggable: function(ddGroup) {
51610         if (ddGroup instanceof Array) {
51611                 Roo.each(ddGroup, this.setDraggable, this);
51612                 return;
51613         }
51614         if (this.dragZone) {
51615                 this.dragZone.addToGroup(ddGroup);
51616         } else {
51617                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51618                                 containerScroll: true,
51619                                 ddGroup: ddGroup 
51620
51621                         });
51622 //                      Draggability implies selection. DragZone's mousedown selects the element.
51623                         if (!this.multiSelect) { this.singleSelect = true; }
51624
51625 //                      Wire the DragZone's handlers up to methods in *this*
51626                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51627                 }
51628     },
51629
51630 /**     Specify from which ddGroup this DDView accepts drops. */
51631     setDroppable: function(ddGroup) {
51632         if (ddGroup instanceof Array) {
51633                 Roo.each(ddGroup, this.setDroppable, this);
51634                 return;
51635         }
51636         if (this.dropZone) {
51637                 this.dropZone.addToGroup(ddGroup);
51638         } else {
51639                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51640                                 containerScroll: true,
51641                                 ddGroup: ddGroup
51642                         });
51643
51644 //                      Wire the DropZone's handlers up to methods in *this*
51645                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51646                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51647                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51648                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51649                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51650                 }
51651     },
51652
51653 /**     Decide whether to drop above or below a View node. */
51654     getDropPoint : function(e, n, dd){
51655         if (n == this.el.dom) { return "above"; }
51656                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51657                 var c = t + (b - t) / 2;
51658                 var y = Roo.lib.Event.getPageY(e);
51659                 if(y <= c) {
51660                         return "above";
51661                 }else{
51662                         return "below";
51663                 }
51664     },
51665
51666     onNodeEnter : function(n, dd, e, data){
51667                 return false;
51668     },
51669     
51670     onNodeOver : function(n, dd, e, data){
51671                 var pt = this.getDropPoint(e, n, dd);
51672                 // set the insert point style on the target node
51673                 var dragElClass = this.dropNotAllowed;
51674                 if (pt) {
51675                         var targetElClass;
51676                         if (pt == "above"){
51677                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51678                                 targetElClass = "x-view-drag-insert-above";
51679                         } else {
51680                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51681                                 targetElClass = "x-view-drag-insert-below";
51682                         }
51683                         if (this.lastInsertClass != targetElClass){
51684                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51685                                 this.lastInsertClass = targetElClass;
51686                         }
51687                 }
51688                 return dragElClass;
51689         },
51690
51691     onNodeOut : function(n, dd, e, data){
51692                 this.removeDropIndicators(n);
51693     },
51694
51695     onNodeDrop : function(n, dd, e, data){
51696         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51697                 return false;
51698         }
51699         var pt = this.getDropPoint(e, n, dd);
51700                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51701                 if (pt == "below") { insertAt++; }
51702                 for (var i = 0; i < data.records.length; i++) {
51703                         var r = data.records[i];
51704                         var dup = this.store.getById(r.id);
51705                         if (dup && (dd != this.dragZone)) {
51706                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51707                         } else {
51708                                 if (data.copy) {
51709                                         this.store.insert(insertAt++, r.copy());
51710                                 } else {
51711                                         data.source.isDirtyFlag = true;
51712                                         r.store.remove(r);
51713                                         this.store.insert(insertAt++, r);
51714                                 }
51715                                 this.isDirtyFlag = true;
51716                         }
51717                 }
51718                 this.dragZone.cachedTarget = null;
51719                 return true;
51720     },
51721
51722     removeDropIndicators : function(n){
51723                 if(n){
51724                         Roo.fly(n).removeClass([
51725                                 "x-view-drag-insert-above",
51726                                 "x-view-drag-insert-below"]);
51727                         this.lastInsertClass = "_noclass";
51728                 }
51729     },
51730
51731 /**
51732  *      Utility method. Add a delete option to the DDView's context menu.
51733  *      @param {String} imageUrl The URL of the "delete" icon image.
51734  */
51735         setDeletable: function(imageUrl) {
51736                 if (!this.singleSelect && !this.multiSelect) {
51737                         this.singleSelect = true;
51738                 }
51739                 var c = this.getContextMenu();
51740                 this.contextMenu.on("itemclick", function(item) {
51741                         switch (item.id) {
51742                                 case "delete":
51743                                         this.remove(this.getSelectedIndexes());
51744                                         break;
51745                         }
51746                 }, this);
51747                 this.contextMenu.add({
51748                         icon: imageUrl,
51749                         id: "delete",
51750                         text: 'Delete'
51751                 });
51752         },
51753         
51754 /**     Return the context menu for this DDView. */
51755         getContextMenu: function() {
51756                 if (!this.contextMenu) {
51757 //                      Create the View's context menu
51758                         this.contextMenu = new Roo.menu.Menu({
51759                                 id: this.id + "-contextmenu"
51760                         });
51761                         this.el.on("contextmenu", this.showContextMenu, this);
51762                 }
51763                 return this.contextMenu;
51764         },
51765         
51766         disableContextMenu: function() {
51767                 if (this.contextMenu) {
51768                         this.el.un("contextmenu", this.showContextMenu, this);
51769                 }
51770         },
51771
51772         showContextMenu: function(e, item) {
51773         item = this.findItemFromChild(e.getTarget());
51774                 if (item) {
51775                         e.stopEvent();
51776                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51777                         this.contextMenu.showAt(e.getXY());
51778             }
51779     },
51780
51781 /**
51782  *      Remove {@link Roo.data.Record}s at the specified indices.
51783  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51784  */
51785     remove: function(selectedIndices) {
51786                 selectedIndices = [].concat(selectedIndices);
51787                 for (var i = 0; i < selectedIndices.length; i++) {
51788                         var rec = this.store.getAt(selectedIndices[i]);
51789                         this.store.remove(rec);
51790                 }
51791     },
51792
51793 /**
51794  *      Double click fires the event, but also, if this is draggable, and there is only one other
51795  *      related DropZone, it transfers the selected node.
51796  */
51797     onDblClick : function(e){
51798         var item = this.findItemFromChild(e.getTarget());
51799         if(item){
51800             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51801                 return false;
51802             }
51803             if (this.dragGroup) {
51804                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51805                     while (targets.indexOf(this.dropZone) > -1) {
51806                             targets.remove(this.dropZone);
51807                                 }
51808                     if (targets.length == 1) {
51809                                         this.dragZone.cachedTarget = null;
51810                         var el = Roo.get(targets[0].getEl());
51811                         var box = el.getBox(true);
51812                         targets[0].onNodeDrop(el.dom, {
51813                                 target: el.dom,
51814                                 xy: [box.x, box.y + box.height - 1]
51815                         }, null, this.getDragData(e));
51816                     }
51817                 }
51818         }
51819     },
51820     
51821     handleSelection: function(e) {
51822                 this.dragZone.cachedTarget = null;
51823         var item = this.findItemFromChild(e.getTarget());
51824         if (!item) {
51825                 this.clearSelections(true);
51826                 return;
51827         }
51828                 if (item && (this.multiSelect || this.singleSelect)){
51829                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51830                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51831                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51832                                 this.unselect(item);
51833                         } else {
51834                                 this.select(item, this.multiSelect && e.ctrlKey);
51835                                 this.lastSelection = item;
51836                         }
51837                 }
51838     },
51839
51840     onItemClick : function(item, index, e){
51841                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51842                         return false;
51843                 }
51844                 return true;
51845     },
51846
51847     unselect : function(nodeInfo, suppressEvent){
51848                 var node = this.getNode(nodeInfo);
51849                 if(node && this.isSelected(node)){
51850                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51851                                 Roo.fly(node).removeClass(this.selectedClass);
51852                                 this.selections.remove(node);
51853                                 if(!suppressEvent){
51854                                         this.fireEvent("selectionchange", this, this.selections);
51855                                 }
51856                         }
51857                 }
51858     }
51859 });
51860 /*
51861  * Based on:
51862  * Ext JS Library 1.1.1
51863  * Copyright(c) 2006-2007, Ext JS, LLC.
51864  *
51865  * Originally Released Under LGPL - original licence link has changed is not relivant.
51866  *
51867  * Fork - LGPL
51868  * <script type="text/javascript">
51869  */
51870  
51871 /**
51872  * @class Roo.LayoutManager
51873  * @extends Roo.util.Observable
51874  * Base class for layout managers.
51875  */
51876 Roo.LayoutManager = function(container, config){
51877     Roo.LayoutManager.superclass.constructor.call(this);
51878     this.el = Roo.get(container);
51879     // ie scrollbar fix
51880     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51881         document.body.scroll = "no";
51882     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51883         this.el.position('relative');
51884     }
51885     this.id = this.el.id;
51886     this.el.addClass("x-layout-container");
51887     /** false to disable window resize monitoring @type Boolean */
51888     this.monitorWindowResize = true;
51889     this.regions = {};
51890     this.addEvents({
51891         /**
51892          * @event layout
51893          * Fires when a layout is performed. 
51894          * @param {Roo.LayoutManager} this
51895          */
51896         "layout" : true,
51897         /**
51898          * @event regionresized
51899          * Fires when the user resizes a region. 
51900          * @param {Roo.LayoutRegion} region The resized region
51901          * @param {Number} newSize The new size (width for east/west, height for north/south)
51902          */
51903         "regionresized" : true,
51904         /**
51905          * @event regioncollapsed
51906          * Fires when a region is collapsed. 
51907          * @param {Roo.LayoutRegion} region The collapsed region
51908          */
51909         "regioncollapsed" : true,
51910         /**
51911          * @event regionexpanded
51912          * Fires when a region is expanded.  
51913          * @param {Roo.LayoutRegion} region The expanded region
51914          */
51915         "regionexpanded" : true
51916     });
51917     this.updating = false;
51918     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51919 };
51920
51921 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51922     /**
51923      * Returns true if this layout is currently being updated
51924      * @return {Boolean}
51925      */
51926     isUpdating : function(){
51927         return this.updating; 
51928     },
51929     
51930     /**
51931      * Suspend the LayoutManager from doing auto-layouts while
51932      * making multiple add or remove calls
51933      */
51934     beginUpdate : function(){
51935         this.updating = true;    
51936     },
51937     
51938     /**
51939      * Restore auto-layouts and optionally disable the manager from performing a layout
51940      * @param {Boolean} noLayout true to disable a layout update 
51941      */
51942     endUpdate : function(noLayout){
51943         this.updating = false;
51944         if(!noLayout){
51945             this.layout();
51946         }    
51947     },
51948     
51949     layout: function(){
51950         
51951     },
51952     
51953     onRegionResized : function(region, newSize){
51954         this.fireEvent("regionresized", region, newSize);
51955         this.layout();
51956     },
51957     
51958     onRegionCollapsed : function(region){
51959         this.fireEvent("regioncollapsed", region);
51960     },
51961     
51962     onRegionExpanded : function(region){
51963         this.fireEvent("regionexpanded", region);
51964     },
51965         
51966     /**
51967      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51968      * performs box-model adjustments.
51969      * @return {Object} The size as an object {width: (the width), height: (the height)}
51970      */
51971     getViewSize : function(){
51972         var size;
51973         if(this.el.dom != document.body){
51974             size = this.el.getSize();
51975         }else{
51976             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51977         }
51978         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51979         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51980         return size;
51981     },
51982     
51983     /**
51984      * Returns the Element this layout is bound to.
51985      * @return {Roo.Element}
51986      */
51987     getEl : function(){
51988         return this.el;
51989     },
51990     
51991     /**
51992      * Returns the specified region.
51993      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51994      * @return {Roo.LayoutRegion}
51995      */
51996     getRegion : function(target){
51997         return this.regions[target.toLowerCase()];
51998     },
51999     
52000     onWindowResize : function(){
52001         if(this.monitorWindowResize){
52002             this.layout();
52003         }
52004     }
52005 });/*
52006  * Based on:
52007  * Ext JS Library 1.1.1
52008  * Copyright(c) 2006-2007, Ext JS, LLC.
52009  *
52010  * Originally Released Under LGPL - original licence link has changed is not relivant.
52011  *
52012  * Fork - LGPL
52013  * <script type="text/javascript">
52014  */
52015 /**
52016  * @class Roo.BorderLayout
52017  * @extends Roo.LayoutManager
52018  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52019  * please see: <br><br>
52020  * <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>
52021  * <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>
52022  * Example:
52023  <pre><code>
52024  var layout = new Roo.BorderLayout(document.body, {
52025     north: {
52026         initialSize: 25,
52027         titlebar: false
52028     },
52029     west: {
52030         split:true,
52031         initialSize: 200,
52032         minSize: 175,
52033         maxSize: 400,
52034         titlebar: true,
52035         collapsible: true
52036     },
52037     east: {
52038         split:true,
52039         initialSize: 202,
52040         minSize: 175,
52041         maxSize: 400,
52042         titlebar: true,
52043         collapsible: true
52044     },
52045     south: {
52046         split:true,
52047         initialSize: 100,
52048         minSize: 100,
52049         maxSize: 200,
52050         titlebar: true,
52051         collapsible: true
52052     },
52053     center: {
52054         titlebar: true,
52055         autoScroll:true,
52056         resizeTabs: true,
52057         minTabWidth: 50,
52058         preferredTabWidth: 150
52059     }
52060 });
52061
52062 // shorthand
52063 var CP = Roo.ContentPanel;
52064
52065 layout.beginUpdate();
52066 layout.add("north", new CP("north", "North"));
52067 layout.add("south", new CP("south", {title: "South", closable: true}));
52068 layout.add("west", new CP("west", {title: "West"}));
52069 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52070 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52071 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52072 layout.getRegion("center").showPanel("center1");
52073 layout.endUpdate();
52074 </code></pre>
52075
52076 <b>The container the layout is rendered into can be either the body element or any other element.
52077 If it is not the body element, the container needs to either be an absolute positioned element,
52078 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52079 the container size if it is not the body element.</b>
52080
52081 * @constructor
52082 * Create a new BorderLayout
52083 * @param {String/HTMLElement/Element} container The container this layout is bound to
52084 * @param {Object} config Configuration options
52085  */
52086 Roo.BorderLayout = function(container, config){
52087     config = config || {};
52088     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52089     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52090     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52091         var target = this.factory.validRegions[i];
52092         if(config[target]){
52093             this.addRegion(target, config[target]);
52094         }
52095     }
52096 };
52097
52098 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52099     /**
52100      * Creates and adds a new region if it doesn't already exist.
52101      * @param {String} target The target region key (north, south, east, west or center).
52102      * @param {Object} config The regions config object
52103      * @return {BorderLayoutRegion} The new region
52104      */
52105     addRegion : function(target, config){
52106         if(!this.regions[target]){
52107             var r = this.factory.create(target, this, config);
52108             this.bindRegion(target, r);
52109         }
52110         return this.regions[target];
52111     },
52112
52113     // private (kinda)
52114     bindRegion : function(name, r){
52115         this.regions[name] = r;
52116         r.on("visibilitychange", this.layout, this);
52117         r.on("paneladded", this.layout, this);
52118         r.on("panelremoved", this.layout, this);
52119         r.on("invalidated", this.layout, this);
52120         r.on("resized", this.onRegionResized, this);
52121         r.on("collapsed", this.onRegionCollapsed, this);
52122         r.on("expanded", this.onRegionExpanded, this);
52123     },
52124
52125     /**
52126      * Performs a layout update.
52127      */
52128     layout : function(){
52129         if(this.updating) {
52130             return;
52131         }
52132         var size = this.getViewSize();
52133         var w = size.width;
52134         var h = size.height;
52135         var centerW = w;
52136         var centerH = h;
52137         var centerY = 0;
52138         var centerX = 0;
52139         //var x = 0, y = 0;
52140
52141         var rs = this.regions;
52142         var north = rs["north"];
52143         var south = rs["south"]; 
52144         var west = rs["west"];
52145         var east = rs["east"];
52146         var center = rs["center"];
52147         //if(this.hideOnLayout){ // not supported anymore
52148             //c.el.setStyle("display", "none");
52149         //}
52150         if(north && north.isVisible()){
52151             var b = north.getBox();
52152             var m = north.getMargins();
52153             b.width = w - (m.left+m.right);
52154             b.x = m.left;
52155             b.y = m.top;
52156             centerY = b.height + b.y + m.bottom;
52157             centerH -= centerY;
52158             north.updateBox(this.safeBox(b));
52159         }
52160         if(south && south.isVisible()){
52161             var b = south.getBox();
52162             var m = south.getMargins();
52163             b.width = w - (m.left+m.right);
52164             b.x = m.left;
52165             var totalHeight = (b.height + m.top + m.bottom);
52166             b.y = h - totalHeight + m.top;
52167             centerH -= totalHeight;
52168             south.updateBox(this.safeBox(b));
52169         }
52170         if(west && west.isVisible()){
52171             var b = west.getBox();
52172             var m = west.getMargins();
52173             b.height = centerH - (m.top+m.bottom);
52174             b.x = m.left;
52175             b.y = centerY + m.top;
52176             var totalWidth = (b.width + m.left + m.right);
52177             centerX += totalWidth;
52178             centerW -= totalWidth;
52179             west.updateBox(this.safeBox(b));
52180         }
52181         if(east && east.isVisible()){
52182             var b = east.getBox();
52183             var m = east.getMargins();
52184             b.height = centerH - (m.top+m.bottom);
52185             var totalWidth = (b.width + m.left + m.right);
52186             b.x = w - totalWidth + m.left;
52187             b.y = centerY + m.top;
52188             centerW -= totalWidth;
52189             east.updateBox(this.safeBox(b));
52190         }
52191         if(center){
52192             var m = center.getMargins();
52193             var centerBox = {
52194                 x: centerX + m.left,
52195                 y: centerY + m.top,
52196                 width: centerW - (m.left+m.right),
52197                 height: centerH - (m.top+m.bottom)
52198             };
52199             //if(this.hideOnLayout){
52200                 //center.el.setStyle("display", "block");
52201             //}
52202             center.updateBox(this.safeBox(centerBox));
52203         }
52204         this.el.repaint();
52205         this.fireEvent("layout", this);
52206     },
52207
52208     // private
52209     safeBox : function(box){
52210         box.width = Math.max(0, box.width);
52211         box.height = Math.max(0, box.height);
52212         return box;
52213     },
52214
52215     /**
52216      * Adds a ContentPanel (or subclass) to this layout.
52217      * @param {String} target The target region key (north, south, east, west or center).
52218      * @param {Roo.ContentPanel} panel The panel to add
52219      * @return {Roo.ContentPanel} The added panel
52220      */
52221     add : function(target, panel){
52222          
52223         target = target.toLowerCase();
52224         return this.regions[target].add(panel);
52225     },
52226
52227     /**
52228      * Remove a ContentPanel (or subclass) to this layout.
52229      * @param {String} target The target region key (north, south, east, west or center).
52230      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52231      * @return {Roo.ContentPanel} The removed panel
52232      */
52233     remove : function(target, panel){
52234         target = target.toLowerCase();
52235         return this.regions[target].remove(panel);
52236     },
52237
52238     /**
52239      * Searches all regions for a panel with the specified id
52240      * @param {String} panelId
52241      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52242      */
52243     findPanel : function(panelId){
52244         var rs = this.regions;
52245         for(var target in rs){
52246             if(typeof rs[target] != "function"){
52247                 var p = rs[target].getPanel(panelId);
52248                 if(p){
52249                     return p;
52250                 }
52251             }
52252         }
52253         return null;
52254     },
52255
52256     /**
52257      * Searches all regions for a panel with the specified id and activates (shows) it.
52258      * @param {String/ContentPanel} panelId The panels id or the panel itself
52259      * @return {Roo.ContentPanel} The shown panel or null
52260      */
52261     showPanel : function(panelId) {
52262       var rs = this.regions;
52263       for(var target in rs){
52264          var r = rs[target];
52265          if(typeof r != "function"){
52266             if(r.hasPanel(panelId)){
52267                return r.showPanel(panelId);
52268             }
52269          }
52270       }
52271       return null;
52272    },
52273
52274    /**
52275      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52276      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52277      */
52278     restoreState : function(provider){
52279         if(!provider){
52280             provider = Roo.state.Manager;
52281         }
52282         var sm = new Roo.LayoutStateManager();
52283         sm.init(this, provider);
52284     },
52285
52286     /**
52287      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52288      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52289      * a valid ContentPanel config object.  Example:
52290      * <pre><code>
52291 // Create the main layout
52292 var layout = new Roo.BorderLayout('main-ct', {
52293     west: {
52294         split:true,
52295         minSize: 175,
52296         titlebar: true
52297     },
52298     center: {
52299         title:'Components'
52300     }
52301 }, 'main-ct');
52302
52303 // Create and add multiple ContentPanels at once via configs
52304 layout.batchAdd({
52305    west: {
52306        id: 'source-files',
52307        autoCreate:true,
52308        title:'Ext Source Files',
52309        autoScroll:true,
52310        fitToFrame:true
52311    },
52312    center : {
52313        el: cview,
52314        autoScroll:true,
52315        fitToFrame:true,
52316        toolbar: tb,
52317        resizeEl:'cbody'
52318    }
52319 });
52320 </code></pre>
52321      * @param {Object} regions An object containing ContentPanel configs by region name
52322      */
52323     batchAdd : function(regions){
52324         this.beginUpdate();
52325         for(var rname in regions){
52326             var lr = this.regions[rname];
52327             if(lr){
52328                 this.addTypedPanels(lr, regions[rname]);
52329             }
52330         }
52331         this.endUpdate();
52332     },
52333
52334     // private
52335     addTypedPanels : function(lr, ps){
52336         if(typeof ps == 'string'){
52337             lr.add(new Roo.ContentPanel(ps));
52338         }
52339         else if(ps instanceof Array){
52340             for(var i =0, len = ps.length; i < len; i++){
52341                 this.addTypedPanels(lr, ps[i]);
52342             }
52343         }
52344         else if(!ps.events){ // raw config?
52345             var el = ps.el;
52346             delete ps.el; // prevent conflict
52347             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52348         }
52349         else {  // panel object assumed!
52350             lr.add(ps);
52351         }
52352     },
52353     /**
52354      * Adds a xtype elements to the layout.
52355      * <pre><code>
52356
52357 layout.addxtype({
52358        xtype : 'ContentPanel',
52359        region: 'west',
52360        items: [ .... ]
52361    }
52362 );
52363
52364 layout.addxtype({
52365         xtype : 'NestedLayoutPanel',
52366         region: 'west',
52367         layout: {
52368            center: { },
52369            west: { }   
52370         },
52371         items : [ ... list of content panels or nested layout panels.. ]
52372    }
52373 );
52374 </code></pre>
52375      * @param {Object} cfg Xtype definition of item to add.
52376      */
52377     addxtype : function(cfg)
52378     {
52379         // basically accepts a pannel...
52380         // can accept a layout region..!?!?
52381         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52382         
52383         if (!cfg.xtype.match(/Panel$/)) {
52384             return false;
52385         }
52386         var ret = false;
52387         
52388         if (typeof(cfg.region) == 'undefined') {
52389             Roo.log("Failed to add Panel, region was not set");
52390             Roo.log(cfg);
52391             return false;
52392         }
52393         var region = cfg.region;
52394         delete cfg.region;
52395         
52396           
52397         var xitems = [];
52398         if (cfg.items) {
52399             xitems = cfg.items;
52400             delete cfg.items;
52401         }
52402         var nb = false;
52403         
52404         switch(cfg.xtype) 
52405         {
52406             case 'ContentPanel':  // ContentPanel (el, cfg)
52407             case 'ScrollPanel':  // ContentPanel (el, cfg)
52408             case 'ViewPanel': 
52409                 if(cfg.autoCreate) {
52410                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52411                 } else {
52412                     var el = this.el.createChild();
52413                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52414                 }
52415                 
52416                 this.add(region, ret);
52417                 break;
52418             
52419             
52420             case 'TreePanel': // our new panel!
52421                 cfg.el = this.el.createChild();
52422                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52423                 this.add(region, ret);
52424                 break;
52425             
52426             case 'NestedLayoutPanel': 
52427                 // create a new Layout (which is  a Border Layout...
52428                 var el = this.el.createChild();
52429                 var clayout = cfg.layout;
52430                 delete cfg.layout;
52431                 clayout.items   = clayout.items  || [];
52432                 // replace this exitems with the clayout ones..
52433                 xitems = clayout.items;
52434                  
52435                 
52436                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52437                     cfg.background = false;
52438                 }
52439                 var layout = new Roo.BorderLayout(el, clayout);
52440                 
52441                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52442                 //console.log('adding nested layout panel '  + cfg.toSource());
52443                 this.add(region, ret);
52444                 nb = {}; /// find first...
52445                 break;
52446                 
52447             case 'GridPanel': 
52448             
52449                 // needs grid and region
52450                 
52451                 //var el = this.getRegion(region).el.createChild();
52452                 var el = this.el.createChild();
52453                 // create the grid first...
52454                 
52455                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52456                 delete cfg.grid;
52457                 if (region == 'center' && this.active ) {
52458                     cfg.background = false;
52459                 }
52460                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52461                 
52462                 this.add(region, ret);
52463                 if (cfg.background) {
52464                     ret.on('activate', function(gp) {
52465                         if (!gp.grid.rendered) {
52466                             gp.grid.render();
52467                         }
52468                     });
52469                 } else {
52470                     grid.render();
52471                 }
52472                 break;
52473            
52474            
52475            
52476                 
52477                 
52478                 
52479             default:
52480                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52481                     
52482                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52483                     this.add(region, ret);
52484                 } else {
52485                 
52486                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52487                     return null;
52488                 }
52489                 
52490              // GridPanel (grid, cfg)
52491             
52492         }
52493         this.beginUpdate();
52494         // add children..
52495         var region = '';
52496         var abn = {};
52497         Roo.each(xitems, function(i)  {
52498             region = nb && i.region ? i.region : false;
52499             
52500             var add = ret.addxtype(i);
52501            
52502             if (region) {
52503                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52504                 if (!i.background) {
52505                     abn[region] = nb[region] ;
52506                 }
52507             }
52508             
52509         });
52510         this.endUpdate();
52511
52512         // make the last non-background panel active..
52513         //if (nb) { Roo.log(abn); }
52514         if (nb) {
52515             
52516             for(var r in abn) {
52517                 region = this.getRegion(r);
52518                 if (region) {
52519                     // tried using nb[r], but it does not work..
52520                      
52521                     region.showPanel(abn[r]);
52522                    
52523                 }
52524             }
52525         }
52526         return ret;
52527         
52528     }
52529 });
52530
52531 /**
52532  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52533  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52534  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52535  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52536  * <pre><code>
52537 // shorthand
52538 var CP = Roo.ContentPanel;
52539
52540 var layout = Roo.BorderLayout.create({
52541     north: {
52542         initialSize: 25,
52543         titlebar: false,
52544         panels: [new CP("north", "North")]
52545     },
52546     west: {
52547         split:true,
52548         initialSize: 200,
52549         minSize: 175,
52550         maxSize: 400,
52551         titlebar: true,
52552         collapsible: true,
52553         panels: [new CP("west", {title: "West"})]
52554     },
52555     east: {
52556         split:true,
52557         initialSize: 202,
52558         minSize: 175,
52559         maxSize: 400,
52560         titlebar: true,
52561         collapsible: true,
52562         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52563     },
52564     south: {
52565         split:true,
52566         initialSize: 100,
52567         minSize: 100,
52568         maxSize: 200,
52569         titlebar: true,
52570         collapsible: true,
52571         panels: [new CP("south", {title: "South", closable: true})]
52572     },
52573     center: {
52574         titlebar: true,
52575         autoScroll:true,
52576         resizeTabs: true,
52577         minTabWidth: 50,
52578         preferredTabWidth: 150,
52579         panels: [
52580             new CP("center1", {title: "Close Me", closable: true}),
52581             new CP("center2", {title: "Center Panel", closable: false})
52582         ]
52583     }
52584 }, document.body);
52585
52586 layout.getRegion("center").showPanel("center1");
52587 </code></pre>
52588  * @param config
52589  * @param targetEl
52590  */
52591 Roo.BorderLayout.create = function(config, targetEl){
52592     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52593     layout.beginUpdate();
52594     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52595     for(var j = 0, jlen = regions.length; j < jlen; j++){
52596         var lr = regions[j];
52597         if(layout.regions[lr] && config[lr].panels){
52598             var r = layout.regions[lr];
52599             var ps = config[lr].panels;
52600             layout.addTypedPanels(r, ps);
52601         }
52602     }
52603     layout.endUpdate();
52604     return layout;
52605 };
52606
52607 // private
52608 Roo.BorderLayout.RegionFactory = {
52609     // private
52610     validRegions : ["north","south","east","west","center"],
52611
52612     // private
52613     create : function(target, mgr, config){
52614         target = target.toLowerCase();
52615         if(config.lightweight || config.basic){
52616             return new Roo.BasicLayoutRegion(mgr, config, target);
52617         }
52618         switch(target){
52619             case "north":
52620                 return new Roo.NorthLayoutRegion(mgr, config);
52621             case "south":
52622                 return new Roo.SouthLayoutRegion(mgr, config);
52623             case "east":
52624                 return new Roo.EastLayoutRegion(mgr, config);
52625             case "west":
52626                 return new Roo.WestLayoutRegion(mgr, config);
52627             case "center":
52628                 return new Roo.CenterLayoutRegion(mgr, config);
52629         }
52630         throw 'Layout region "'+target+'" not supported.';
52631     }
52632 };/*
52633  * Based on:
52634  * Ext JS Library 1.1.1
52635  * Copyright(c) 2006-2007, Ext JS, LLC.
52636  *
52637  * Originally Released Under LGPL - original licence link has changed is not relivant.
52638  *
52639  * Fork - LGPL
52640  * <script type="text/javascript">
52641  */
52642  
52643 /**
52644  * @class Roo.BasicLayoutRegion
52645  * @extends Roo.util.Observable
52646  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52647  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52648  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52649  */
52650 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52651     this.mgr = mgr;
52652     this.position  = pos;
52653     this.events = {
52654         /**
52655          * @scope Roo.BasicLayoutRegion
52656          */
52657         
52658         /**
52659          * @event beforeremove
52660          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52661          * @param {Roo.LayoutRegion} this
52662          * @param {Roo.ContentPanel} panel The panel
52663          * @param {Object} e The cancel event object
52664          */
52665         "beforeremove" : true,
52666         /**
52667          * @event invalidated
52668          * Fires when the layout for this region is changed.
52669          * @param {Roo.LayoutRegion} this
52670          */
52671         "invalidated" : true,
52672         /**
52673          * @event visibilitychange
52674          * Fires when this region is shown or hidden 
52675          * @param {Roo.LayoutRegion} this
52676          * @param {Boolean} visibility true or false
52677          */
52678         "visibilitychange" : true,
52679         /**
52680          * @event paneladded
52681          * Fires when a panel is added. 
52682          * @param {Roo.LayoutRegion} this
52683          * @param {Roo.ContentPanel} panel The panel
52684          */
52685         "paneladded" : true,
52686         /**
52687          * @event panelremoved
52688          * Fires when a panel is removed. 
52689          * @param {Roo.LayoutRegion} this
52690          * @param {Roo.ContentPanel} panel The panel
52691          */
52692         "panelremoved" : true,
52693         /**
52694          * @event beforecollapse
52695          * Fires when this region before collapse.
52696          * @param {Roo.LayoutRegion} this
52697          */
52698         "beforecollapse" : true,
52699         /**
52700          * @event collapsed
52701          * Fires when this region is collapsed.
52702          * @param {Roo.LayoutRegion} this
52703          */
52704         "collapsed" : true,
52705         /**
52706          * @event expanded
52707          * Fires when this region is expanded.
52708          * @param {Roo.LayoutRegion} this
52709          */
52710         "expanded" : true,
52711         /**
52712          * @event slideshow
52713          * Fires when this region is slid into view.
52714          * @param {Roo.LayoutRegion} this
52715          */
52716         "slideshow" : true,
52717         /**
52718          * @event slidehide
52719          * Fires when this region slides out of view. 
52720          * @param {Roo.LayoutRegion} this
52721          */
52722         "slidehide" : true,
52723         /**
52724          * @event panelactivated
52725          * Fires when a panel is activated. 
52726          * @param {Roo.LayoutRegion} this
52727          * @param {Roo.ContentPanel} panel The activated panel
52728          */
52729         "panelactivated" : true,
52730         /**
52731          * @event resized
52732          * Fires when the user resizes this region. 
52733          * @param {Roo.LayoutRegion} this
52734          * @param {Number} newSize The new size (width for east/west, height for north/south)
52735          */
52736         "resized" : true
52737     };
52738     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52739     this.panels = new Roo.util.MixedCollection();
52740     this.panels.getKey = this.getPanelId.createDelegate(this);
52741     this.box = null;
52742     this.activePanel = null;
52743     // ensure listeners are added...
52744     
52745     if (config.listeners || config.events) {
52746         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52747             listeners : config.listeners || {},
52748             events : config.events || {}
52749         });
52750     }
52751     
52752     if(skipConfig !== true){
52753         this.applyConfig(config);
52754     }
52755 };
52756
52757 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52758     getPanelId : function(p){
52759         return p.getId();
52760     },
52761     
52762     applyConfig : function(config){
52763         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52764         this.config = config;
52765         
52766     },
52767     
52768     /**
52769      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52770      * the width, for horizontal (north, south) the height.
52771      * @param {Number} newSize The new width or height
52772      */
52773     resizeTo : function(newSize){
52774         var el = this.el ? this.el :
52775                  (this.activePanel ? this.activePanel.getEl() : null);
52776         if(el){
52777             switch(this.position){
52778                 case "east":
52779                 case "west":
52780                     el.setWidth(newSize);
52781                     this.fireEvent("resized", this, newSize);
52782                 break;
52783                 case "north":
52784                 case "south":
52785                     el.setHeight(newSize);
52786                     this.fireEvent("resized", this, newSize);
52787                 break;                
52788             }
52789         }
52790     },
52791     
52792     getBox : function(){
52793         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52794     },
52795     
52796     getMargins : function(){
52797         return this.margins;
52798     },
52799     
52800     updateBox : function(box){
52801         this.box = box;
52802         var el = this.activePanel.getEl();
52803         el.dom.style.left = box.x + "px";
52804         el.dom.style.top = box.y + "px";
52805         this.activePanel.setSize(box.width, box.height);
52806     },
52807     
52808     /**
52809      * Returns the container element for this region.
52810      * @return {Roo.Element}
52811      */
52812     getEl : function(){
52813         return this.activePanel;
52814     },
52815     
52816     /**
52817      * Returns true if this region is currently visible.
52818      * @return {Boolean}
52819      */
52820     isVisible : function(){
52821         return this.activePanel ? true : false;
52822     },
52823     
52824     setActivePanel : function(panel){
52825         panel = this.getPanel(panel);
52826         if(this.activePanel && this.activePanel != panel){
52827             this.activePanel.setActiveState(false);
52828             this.activePanel.getEl().setLeftTop(-10000,-10000);
52829         }
52830         this.activePanel = panel;
52831         panel.setActiveState(true);
52832         if(this.box){
52833             panel.setSize(this.box.width, this.box.height);
52834         }
52835         this.fireEvent("panelactivated", this, panel);
52836         this.fireEvent("invalidated");
52837     },
52838     
52839     /**
52840      * Show the specified panel.
52841      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52842      * @return {Roo.ContentPanel} The shown panel or null
52843      */
52844     showPanel : function(panel){
52845         if(panel = this.getPanel(panel)){
52846             this.setActivePanel(panel);
52847         }
52848         return panel;
52849     },
52850     
52851     /**
52852      * Get the active panel for this region.
52853      * @return {Roo.ContentPanel} The active panel or null
52854      */
52855     getActivePanel : function(){
52856         return this.activePanel;
52857     },
52858     
52859     /**
52860      * Add the passed ContentPanel(s)
52861      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52862      * @return {Roo.ContentPanel} The panel added (if only one was added)
52863      */
52864     add : function(panel){
52865         if(arguments.length > 1){
52866             for(var i = 0, len = arguments.length; i < len; i++) {
52867                 this.add(arguments[i]);
52868             }
52869             return null;
52870         }
52871         if(this.hasPanel(panel)){
52872             this.showPanel(panel);
52873             return panel;
52874         }
52875         var el = panel.getEl();
52876         if(el.dom.parentNode != this.mgr.el.dom){
52877             this.mgr.el.dom.appendChild(el.dom);
52878         }
52879         if(panel.setRegion){
52880             panel.setRegion(this);
52881         }
52882         this.panels.add(panel);
52883         el.setStyle("position", "absolute");
52884         if(!panel.background){
52885             this.setActivePanel(panel);
52886             if(this.config.initialSize && this.panels.getCount()==1){
52887                 this.resizeTo(this.config.initialSize);
52888             }
52889         }
52890         this.fireEvent("paneladded", this, panel);
52891         return panel;
52892     },
52893     
52894     /**
52895      * Returns true if the panel is in this region.
52896      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52897      * @return {Boolean}
52898      */
52899     hasPanel : function(panel){
52900         if(typeof panel == "object"){ // must be panel obj
52901             panel = panel.getId();
52902         }
52903         return this.getPanel(panel) ? true : false;
52904     },
52905     
52906     /**
52907      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52908      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52909      * @param {Boolean} preservePanel Overrides the config preservePanel option
52910      * @return {Roo.ContentPanel} The panel that was removed
52911      */
52912     remove : function(panel, preservePanel){
52913         panel = this.getPanel(panel);
52914         if(!panel){
52915             return null;
52916         }
52917         var e = {};
52918         this.fireEvent("beforeremove", this, panel, e);
52919         if(e.cancel === true){
52920             return null;
52921         }
52922         var panelId = panel.getId();
52923         this.panels.removeKey(panelId);
52924         return panel;
52925     },
52926     
52927     /**
52928      * Returns the panel specified or null if it's not in this region.
52929      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52930      * @return {Roo.ContentPanel}
52931      */
52932     getPanel : function(id){
52933         if(typeof id == "object"){ // must be panel obj
52934             return id;
52935         }
52936         return this.panels.get(id);
52937     },
52938     
52939     /**
52940      * Returns this regions position (north/south/east/west/center).
52941      * @return {String} 
52942      */
52943     getPosition: function(){
52944         return this.position;    
52945     }
52946 });/*
52947  * Based on:
52948  * Ext JS Library 1.1.1
52949  * Copyright(c) 2006-2007, Ext JS, LLC.
52950  *
52951  * Originally Released Under LGPL - original licence link has changed is not relivant.
52952  *
52953  * Fork - LGPL
52954  * <script type="text/javascript">
52955  */
52956  
52957 /**
52958  * @class Roo.LayoutRegion
52959  * @extends Roo.BasicLayoutRegion
52960  * This class represents a region in a layout manager.
52961  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52962  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52963  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52964  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52965  * @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})
52966  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52967  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52968  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52969  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52970  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52971  * @cfg {String}    title           The title for the region (overrides panel titles)
52972  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52973  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52974  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52975  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52976  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52977  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52978  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52979  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52980  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52981  * @cfg {Boolean}   showPin         True to show a pin button
52982  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52983  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52984  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52985  * @cfg {Number}    width           For East/West panels
52986  * @cfg {Number}    height          For North/South panels
52987  * @cfg {Boolean}   split           To show the splitter
52988  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52989  */
52990 Roo.LayoutRegion = function(mgr, config, pos){
52991     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52992     var dh = Roo.DomHelper;
52993     /** This region's container element 
52994     * @type Roo.Element */
52995     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52996     /** This region's title element 
52997     * @type Roo.Element */
52998
52999     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53000         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53001         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53002     ]}, true);
53003     this.titleEl.enableDisplayMode();
53004     /** This region's title text element 
53005     * @type HTMLElement */
53006     this.titleTextEl = this.titleEl.dom.firstChild;
53007     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53008     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53009     this.closeBtn.enableDisplayMode();
53010     this.closeBtn.on("click", this.closeClicked, this);
53011     this.closeBtn.hide();
53012
53013     this.createBody(config);
53014     this.visible = true;
53015     this.collapsed = false;
53016
53017     if(config.hideWhenEmpty){
53018         this.hide();
53019         this.on("paneladded", this.validateVisibility, this);
53020         this.on("panelremoved", this.validateVisibility, this);
53021     }
53022     this.applyConfig(config);
53023 };
53024
53025 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53026
53027     createBody : function(){
53028         /** This region's body element 
53029         * @type Roo.Element */
53030         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53031     },
53032
53033     applyConfig : function(c){
53034         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53035             var dh = Roo.DomHelper;
53036             if(c.titlebar !== false){
53037                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53038                 this.collapseBtn.on("click", this.collapse, this);
53039                 this.collapseBtn.enableDisplayMode();
53040
53041                 if(c.showPin === true || this.showPin){
53042                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53043                     this.stickBtn.enableDisplayMode();
53044                     this.stickBtn.on("click", this.expand, this);
53045                     this.stickBtn.hide();
53046                 }
53047             }
53048             /** This region's collapsed element
53049             * @type Roo.Element */
53050             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53051                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53052             ]}, true);
53053             if(c.floatable !== false){
53054                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53055                this.collapsedEl.on("click", this.collapseClick, this);
53056             }
53057
53058             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53059                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53060                    id: "message", unselectable: "on", style:{"float":"left"}});
53061                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53062              }
53063             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53064             this.expandBtn.on("click", this.expand, this);
53065         }
53066         if(this.collapseBtn){
53067             this.collapseBtn.setVisible(c.collapsible == true);
53068         }
53069         this.cmargins = c.cmargins || this.cmargins ||
53070                          (this.position == "west" || this.position == "east" ?
53071                              {top: 0, left: 2, right:2, bottom: 0} :
53072                              {top: 2, left: 0, right:0, bottom: 2});
53073         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53074         this.bottomTabs = c.tabPosition != "top";
53075         this.autoScroll = c.autoScroll || false;
53076         if(this.autoScroll){
53077             this.bodyEl.setStyle("overflow", "auto");
53078         }else{
53079             this.bodyEl.setStyle("overflow", "hidden");
53080         }
53081         //if(c.titlebar !== false){
53082             if((!c.titlebar && !c.title) || c.titlebar === false){
53083                 this.titleEl.hide();
53084             }else{
53085                 this.titleEl.show();
53086                 if(c.title){
53087                     this.titleTextEl.innerHTML = c.title;
53088                 }
53089             }
53090         //}
53091         this.duration = c.duration || .30;
53092         this.slideDuration = c.slideDuration || .45;
53093         this.config = c;
53094         if(c.collapsed){
53095             this.collapse(true);
53096         }
53097         if(c.hidden){
53098             this.hide();
53099         }
53100     },
53101     /**
53102      * Returns true if this region is currently visible.
53103      * @return {Boolean}
53104      */
53105     isVisible : function(){
53106         return this.visible;
53107     },
53108
53109     /**
53110      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53111      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53112      */
53113     setCollapsedTitle : function(title){
53114         title = title || "&#160;";
53115         if(this.collapsedTitleTextEl){
53116             this.collapsedTitleTextEl.innerHTML = title;
53117         }
53118     },
53119
53120     getBox : function(){
53121         var b;
53122         if(!this.collapsed){
53123             b = this.el.getBox(false, true);
53124         }else{
53125             b = this.collapsedEl.getBox(false, true);
53126         }
53127         return b;
53128     },
53129
53130     getMargins : function(){
53131         return this.collapsed ? this.cmargins : this.margins;
53132     },
53133
53134     highlight : function(){
53135         this.el.addClass("x-layout-panel-dragover");
53136     },
53137
53138     unhighlight : function(){
53139         this.el.removeClass("x-layout-panel-dragover");
53140     },
53141
53142     updateBox : function(box){
53143         this.box = box;
53144         if(!this.collapsed){
53145             this.el.dom.style.left = box.x + "px";
53146             this.el.dom.style.top = box.y + "px";
53147             this.updateBody(box.width, box.height);
53148         }else{
53149             this.collapsedEl.dom.style.left = box.x + "px";
53150             this.collapsedEl.dom.style.top = box.y + "px";
53151             this.collapsedEl.setSize(box.width, box.height);
53152         }
53153         if(this.tabs){
53154             this.tabs.autoSizeTabs();
53155         }
53156     },
53157
53158     updateBody : function(w, h){
53159         if(w !== null){
53160             this.el.setWidth(w);
53161             w -= this.el.getBorderWidth("rl");
53162             if(this.config.adjustments){
53163                 w += this.config.adjustments[0];
53164             }
53165         }
53166         if(h !== null){
53167             this.el.setHeight(h);
53168             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53169             h -= this.el.getBorderWidth("tb");
53170             if(this.config.adjustments){
53171                 h += this.config.adjustments[1];
53172             }
53173             this.bodyEl.setHeight(h);
53174             if(this.tabs){
53175                 h = this.tabs.syncHeight(h);
53176             }
53177         }
53178         if(this.panelSize){
53179             w = w !== null ? w : this.panelSize.width;
53180             h = h !== null ? h : this.panelSize.height;
53181         }
53182         if(this.activePanel){
53183             var el = this.activePanel.getEl();
53184             w = w !== null ? w : el.getWidth();
53185             h = h !== null ? h : el.getHeight();
53186             this.panelSize = {width: w, height: h};
53187             this.activePanel.setSize(w, h);
53188         }
53189         if(Roo.isIE && this.tabs){
53190             this.tabs.el.repaint();
53191         }
53192     },
53193
53194     /**
53195      * Returns the container element for this region.
53196      * @return {Roo.Element}
53197      */
53198     getEl : function(){
53199         return this.el;
53200     },
53201
53202     /**
53203      * Hides this region.
53204      */
53205     hide : function(){
53206         if(!this.collapsed){
53207             this.el.dom.style.left = "-2000px";
53208             this.el.hide();
53209         }else{
53210             this.collapsedEl.dom.style.left = "-2000px";
53211             this.collapsedEl.hide();
53212         }
53213         this.visible = false;
53214         this.fireEvent("visibilitychange", this, false);
53215     },
53216
53217     /**
53218      * Shows this region if it was previously hidden.
53219      */
53220     show : function(){
53221         if(!this.collapsed){
53222             this.el.show();
53223         }else{
53224             this.collapsedEl.show();
53225         }
53226         this.visible = true;
53227         this.fireEvent("visibilitychange", this, true);
53228     },
53229
53230     closeClicked : function(){
53231         if(this.activePanel){
53232             this.remove(this.activePanel);
53233         }
53234     },
53235
53236     collapseClick : function(e){
53237         if(this.isSlid){
53238            e.stopPropagation();
53239            this.slideIn();
53240         }else{
53241            e.stopPropagation();
53242            this.slideOut();
53243         }
53244     },
53245
53246     /**
53247      * Collapses this region.
53248      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53249      */
53250     collapse : function(skipAnim, skipCheck){
53251         if(this.collapsed) {
53252             return;
53253         }
53254         
53255         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53256             
53257             this.collapsed = true;
53258             if(this.split){
53259                 this.split.el.hide();
53260             }
53261             if(this.config.animate && skipAnim !== true){
53262                 this.fireEvent("invalidated", this);
53263                 this.animateCollapse();
53264             }else{
53265                 this.el.setLocation(-20000,-20000);
53266                 this.el.hide();
53267                 this.collapsedEl.show();
53268                 this.fireEvent("collapsed", this);
53269                 this.fireEvent("invalidated", this);
53270             }
53271         }
53272         
53273     },
53274
53275     animateCollapse : function(){
53276         // overridden
53277     },
53278
53279     /**
53280      * Expands this region if it was previously collapsed.
53281      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53282      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53283      */
53284     expand : function(e, skipAnim){
53285         if(e) {
53286             e.stopPropagation();
53287         }
53288         if(!this.collapsed || this.el.hasActiveFx()) {
53289             return;
53290         }
53291         if(this.isSlid){
53292             this.afterSlideIn();
53293             skipAnim = true;
53294         }
53295         this.collapsed = false;
53296         if(this.config.animate && skipAnim !== true){
53297             this.animateExpand();
53298         }else{
53299             this.el.show();
53300             if(this.split){
53301                 this.split.el.show();
53302             }
53303             this.collapsedEl.setLocation(-2000,-2000);
53304             this.collapsedEl.hide();
53305             this.fireEvent("invalidated", this);
53306             this.fireEvent("expanded", this);
53307         }
53308     },
53309
53310     animateExpand : function(){
53311         // overridden
53312     },
53313
53314     initTabs : function()
53315     {
53316         this.bodyEl.setStyle("overflow", "hidden");
53317         var ts = new Roo.TabPanel(
53318                 this.bodyEl.dom,
53319                 {
53320                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53321                     disableTooltips: this.config.disableTabTips,
53322                     toolbar : this.config.toolbar
53323                 }
53324         );
53325         if(this.config.hideTabs){
53326             ts.stripWrap.setDisplayed(false);
53327         }
53328         this.tabs = ts;
53329         ts.resizeTabs = this.config.resizeTabs === true;
53330         ts.minTabWidth = this.config.minTabWidth || 40;
53331         ts.maxTabWidth = this.config.maxTabWidth || 250;
53332         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53333         ts.monitorResize = false;
53334         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53335         ts.bodyEl.addClass('x-layout-tabs-body');
53336         this.panels.each(this.initPanelAsTab, this);
53337     },
53338
53339     initPanelAsTab : function(panel){
53340         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53341                     this.config.closeOnTab && panel.isClosable());
53342         if(panel.tabTip !== undefined){
53343             ti.setTooltip(panel.tabTip);
53344         }
53345         ti.on("activate", function(){
53346               this.setActivePanel(panel);
53347         }, this);
53348         if(this.config.closeOnTab){
53349             ti.on("beforeclose", function(t, e){
53350                 e.cancel = true;
53351                 this.remove(panel);
53352             }, this);
53353         }
53354         return ti;
53355     },
53356
53357     updatePanelTitle : function(panel, title){
53358         if(this.activePanel == panel){
53359             this.updateTitle(title);
53360         }
53361         if(this.tabs){
53362             var ti = this.tabs.getTab(panel.getEl().id);
53363             ti.setText(title);
53364             if(panel.tabTip !== undefined){
53365                 ti.setTooltip(panel.tabTip);
53366             }
53367         }
53368     },
53369
53370     updateTitle : function(title){
53371         if(this.titleTextEl && !this.config.title){
53372             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53373         }
53374     },
53375
53376     setActivePanel : function(panel){
53377         panel = this.getPanel(panel);
53378         if(this.activePanel && this.activePanel != panel){
53379             this.activePanel.setActiveState(false);
53380         }
53381         this.activePanel = panel;
53382         panel.setActiveState(true);
53383         if(this.panelSize){
53384             panel.setSize(this.panelSize.width, this.panelSize.height);
53385         }
53386         if(this.closeBtn){
53387             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53388         }
53389         this.updateTitle(panel.getTitle());
53390         if(this.tabs){
53391             this.fireEvent("invalidated", this);
53392         }
53393         this.fireEvent("panelactivated", this, panel);
53394     },
53395
53396     /**
53397      * Shows the specified panel.
53398      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53399      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53400      */
53401     showPanel : function(panel)
53402     {
53403         panel = this.getPanel(panel);
53404         if(panel){
53405             if(this.tabs){
53406                 var tab = this.tabs.getTab(panel.getEl().id);
53407                 if(tab.isHidden()){
53408                     this.tabs.unhideTab(tab.id);
53409                 }
53410                 tab.activate();
53411             }else{
53412                 this.setActivePanel(panel);
53413             }
53414         }
53415         return panel;
53416     },
53417
53418     /**
53419      * Get the active panel for this region.
53420      * @return {Roo.ContentPanel} The active panel or null
53421      */
53422     getActivePanel : function(){
53423         return this.activePanel;
53424     },
53425
53426     validateVisibility : function(){
53427         if(this.panels.getCount() < 1){
53428             this.updateTitle("&#160;");
53429             this.closeBtn.hide();
53430             this.hide();
53431         }else{
53432             if(!this.isVisible()){
53433                 this.show();
53434             }
53435         }
53436     },
53437
53438     /**
53439      * Adds the passed ContentPanel(s) to this region.
53440      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53441      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53442      */
53443     add : function(panel){
53444         if(arguments.length > 1){
53445             for(var i = 0, len = arguments.length; i < len; i++) {
53446                 this.add(arguments[i]);
53447             }
53448             return null;
53449         }
53450         if(this.hasPanel(panel)){
53451             this.showPanel(panel);
53452             return panel;
53453         }
53454         panel.setRegion(this);
53455         this.panels.add(panel);
53456         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53457             this.bodyEl.dom.appendChild(panel.getEl().dom);
53458             if(panel.background !== true){
53459                 this.setActivePanel(panel);
53460             }
53461             this.fireEvent("paneladded", this, panel);
53462             return panel;
53463         }
53464         if(!this.tabs){
53465             this.initTabs();
53466         }else{
53467             this.initPanelAsTab(panel);
53468         }
53469         if(panel.background !== true){
53470             this.tabs.activate(panel.getEl().id);
53471         }
53472         this.fireEvent("paneladded", this, panel);
53473         return panel;
53474     },
53475
53476     /**
53477      * Hides the tab for the specified panel.
53478      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53479      */
53480     hidePanel : function(panel){
53481         if(this.tabs && (panel = this.getPanel(panel))){
53482             this.tabs.hideTab(panel.getEl().id);
53483         }
53484     },
53485
53486     /**
53487      * Unhides the tab for a previously hidden panel.
53488      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53489      */
53490     unhidePanel : function(panel){
53491         if(this.tabs && (panel = this.getPanel(panel))){
53492             this.tabs.unhideTab(panel.getEl().id);
53493         }
53494     },
53495
53496     clearPanels : function(){
53497         while(this.panels.getCount() > 0){
53498              this.remove(this.panels.first());
53499         }
53500     },
53501
53502     /**
53503      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53504      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53505      * @param {Boolean} preservePanel Overrides the config preservePanel option
53506      * @return {Roo.ContentPanel} The panel that was removed
53507      */
53508     remove : function(panel, preservePanel){
53509         panel = this.getPanel(panel);
53510         if(!panel){
53511             return null;
53512         }
53513         var e = {};
53514         this.fireEvent("beforeremove", this, panel, e);
53515         if(e.cancel === true){
53516             return null;
53517         }
53518         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53519         var panelId = panel.getId();
53520         this.panels.removeKey(panelId);
53521         if(preservePanel){
53522             document.body.appendChild(panel.getEl().dom);
53523         }
53524         if(this.tabs){
53525             this.tabs.removeTab(panel.getEl().id);
53526         }else if (!preservePanel){
53527             this.bodyEl.dom.removeChild(panel.getEl().dom);
53528         }
53529         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53530             var p = this.panels.first();
53531             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53532             tempEl.appendChild(p.getEl().dom);
53533             this.bodyEl.update("");
53534             this.bodyEl.dom.appendChild(p.getEl().dom);
53535             tempEl = null;
53536             this.updateTitle(p.getTitle());
53537             this.tabs = null;
53538             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53539             this.setActivePanel(p);
53540         }
53541         panel.setRegion(null);
53542         if(this.activePanel == panel){
53543             this.activePanel = null;
53544         }
53545         if(this.config.autoDestroy !== false && preservePanel !== true){
53546             try{panel.destroy();}catch(e){}
53547         }
53548         this.fireEvent("panelremoved", this, panel);
53549         return panel;
53550     },
53551
53552     /**
53553      * Returns the TabPanel component used by this region
53554      * @return {Roo.TabPanel}
53555      */
53556     getTabs : function(){
53557         return this.tabs;
53558     },
53559
53560     createTool : function(parentEl, className){
53561         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53562             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53563         btn.addClassOnOver("x-layout-tools-button-over");
53564         return btn;
53565     }
53566 });/*
53567  * Based on:
53568  * Ext JS Library 1.1.1
53569  * Copyright(c) 2006-2007, Ext JS, LLC.
53570  *
53571  * Originally Released Under LGPL - original licence link has changed is not relivant.
53572  *
53573  * Fork - LGPL
53574  * <script type="text/javascript">
53575  */
53576  
53577
53578
53579 /**
53580  * @class Roo.SplitLayoutRegion
53581  * @extends Roo.LayoutRegion
53582  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53583  */
53584 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53585     this.cursor = cursor;
53586     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53587 };
53588
53589 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53590     splitTip : "Drag to resize.",
53591     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53592     useSplitTips : false,
53593
53594     applyConfig : function(config){
53595         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53596         if(config.split){
53597             if(!this.split){
53598                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53599                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53600                 /** The SplitBar for this region 
53601                 * @type Roo.SplitBar */
53602                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53603                 this.split.on("moved", this.onSplitMove, this);
53604                 this.split.useShim = config.useShim === true;
53605                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53606                 if(this.useSplitTips){
53607                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53608                 }
53609                 if(config.collapsible){
53610                     this.split.el.on("dblclick", this.collapse,  this);
53611                 }
53612             }
53613             if(typeof config.minSize != "undefined"){
53614                 this.split.minSize = config.minSize;
53615             }
53616             if(typeof config.maxSize != "undefined"){
53617                 this.split.maxSize = config.maxSize;
53618             }
53619             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53620                 this.hideSplitter();
53621             }
53622         }
53623     },
53624
53625     getHMaxSize : function(){
53626          var cmax = this.config.maxSize || 10000;
53627          var center = this.mgr.getRegion("center");
53628          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53629     },
53630
53631     getVMaxSize : function(){
53632          var cmax = this.config.maxSize || 10000;
53633          var center = this.mgr.getRegion("center");
53634          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53635     },
53636
53637     onSplitMove : function(split, newSize){
53638         this.fireEvent("resized", this, newSize);
53639     },
53640     
53641     /** 
53642      * Returns the {@link Roo.SplitBar} for this region.
53643      * @return {Roo.SplitBar}
53644      */
53645     getSplitBar : function(){
53646         return this.split;
53647     },
53648     
53649     hide : function(){
53650         this.hideSplitter();
53651         Roo.SplitLayoutRegion.superclass.hide.call(this);
53652     },
53653
53654     hideSplitter : function(){
53655         if(this.split){
53656             this.split.el.setLocation(-2000,-2000);
53657             this.split.el.hide();
53658         }
53659     },
53660
53661     show : function(){
53662         if(this.split){
53663             this.split.el.show();
53664         }
53665         Roo.SplitLayoutRegion.superclass.show.call(this);
53666     },
53667     
53668     beforeSlide: function(){
53669         if(Roo.isGecko){// firefox overflow auto bug workaround
53670             this.bodyEl.clip();
53671             if(this.tabs) {
53672                 this.tabs.bodyEl.clip();
53673             }
53674             if(this.activePanel){
53675                 this.activePanel.getEl().clip();
53676                 
53677                 if(this.activePanel.beforeSlide){
53678                     this.activePanel.beforeSlide();
53679                 }
53680             }
53681         }
53682     },
53683     
53684     afterSlide : function(){
53685         if(Roo.isGecko){// firefox overflow auto bug workaround
53686             this.bodyEl.unclip();
53687             if(this.tabs) {
53688                 this.tabs.bodyEl.unclip();
53689             }
53690             if(this.activePanel){
53691                 this.activePanel.getEl().unclip();
53692                 if(this.activePanel.afterSlide){
53693                     this.activePanel.afterSlide();
53694                 }
53695             }
53696         }
53697     },
53698
53699     initAutoHide : function(){
53700         if(this.autoHide !== false){
53701             if(!this.autoHideHd){
53702                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53703                 this.autoHideHd = {
53704                     "mouseout": function(e){
53705                         if(!e.within(this.el, true)){
53706                             st.delay(500);
53707                         }
53708                     },
53709                     "mouseover" : function(e){
53710                         st.cancel();
53711                     },
53712                     scope : this
53713                 };
53714             }
53715             this.el.on(this.autoHideHd);
53716         }
53717     },
53718
53719     clearAutoHide : function(){
53720         if(this.autoHide !== false){
53721             this.el.un("mouseout", this.autoHideHd.mouseout);
53722             this.el.un("mouseover", this.autoHideHd.mouseover);
53723         }
53724     },
53725
53726     clearMonitor : function(){
53727         Roo.get(document).un("click", this.slideInIf, this);
53728     },
53729
53730     // these names are backwards but not changed for compat
53731     slideOut : function(){
53732         if(this.isSlid || this.el.hasActiveFx()){
53733             return;
53734         }
53735         this.isSlid = true;
53736         if(this.collapseBtn){
53737             this.collapseBtn.hide();
53738         }
53739         this.closeBtnState = this.closeBtn.getStyle('display');
53740         this.closeBtn.hide();
53741         if(this.stickBtn){
53742             this.stickBtn.show();
53743         }
53744         this.el.show();
53745         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53746         this.beforeSlide();
53747         this.el.setStyle("z-index", 10001);
53748         this.el.slideIn(this.getSlideAnchor(), {
53749             callback: function(){
53750                 this.afterSlide();
53751                 this.initAutoHide();
53752                 Roo.get(document).on("click", this.slideInIf, this);
53753                 this.fireEvent("slideshow", this);
53754             },
53755             scope: this,
53756             block: true
53757         });
53758     },
53759
53760     afterSlideIn : function(){
53761         this.clearAutoHide();
53762         this.isSlid = false;
53763         this.clearMonitor();
53764         this.el.setStyle("z-index", "");
53765         if(this.collapseBtn){
53766             this.collapseBtn.show();
53767         }
53768         this.closeBtn.setStyle('display', this.closeBtnState);
53769         if(this.stickBtn){
53770             this.stickBtn.hide();
53771         }
53772         this.fireEvent("slidehide", this);
53773     },
53774
53775     slideIn : function(cb){
53776         if(!this.isSlid || this.el.hasActiveFx()){
53777             Roo.callback(cb);
53778             return;
53779         }
53780         this.isSlid = false;
53781         this.beforeSlide();
53782         this.el.slideOut(this.getSlideAnchor(), {
53783             callback: function(){
53784                 this.el.setLeftTop(-10000, -10000);
53785                 this.afterSlide();
53786                 this.afterSlideIn();
53787                 Roo.callback(cb);
53788             },
53789             scope: this,
53790             block: true
53791         });
53792     },
53793     
53794     slideInIf : function(e){
53795         if(!e.within(this.el)){
53796             this.slideIn();
53797         }
53798     },
53799
53800     animateCollapse : function(){
53801         this.beforeSlide();
53802         this.el.setStyle("z-index", 20000);
53803         var anchor = this.getSlideAnchor();
53804         this.el.slideOut(anchor, {
53805             callback : function(){
53806                 this.el.setStyle("z-index", "");
53807                 this.collapsedEl.slideIn(anchor, {duration:.3});
53808                 this.afterSlide();
53809                 this.el.setLocation(-10000,-10000);
53810                 this.el.hide();
53811                 this.fireEvent("collapsed", this);
53812             },
53813             scope: this,
53814             block: true
53815         });
53816     },
53817
53818     animateExpand : function(){
53819         this.beforeSlide();
53820         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53821         this.el.setStyle("z-index", 20000);
53822         this.collapsedEl.hide({
53823             duration:.1
53824         });
53825         this.el.slideIn(this.getSlideAnchor(), {
53826             callback : function(){
53827                 this.el.setStyle("z-index", "");
53828                 this.afterSlide();
53829                 if(this.split){
53830                     this.split.el.show();
53831                 }
53832                 this.fireEvent("invalidated", this);
53833                 this.fireEvent("expanded", this);
53834             },
53835             scope: this,
53836             block: true
53837         });
53838     },
53839
53840     anchors : {
53841         "west" : "left",
53842         "east" : "right",
53843         "north" : "top",
53844         "south" : "bottom"
53845     },
53846
53847     sanchors : {
53848         "west" : "l",
53849         "east" : "r",
53850         "north" : "t",
53851         "south" : "b"
53852     },
53853
53854     canchors : {
53855         "west" : "tl-tr",
53856         "east" : "tr-tl",
53857         "north" : "tl-bl",
53858         "south" : "bl-tl"
53859     },
53860
53861     getAnchor : function(){
53862         return this.anchors[this.position];
53863     },
53864
53865     getCollapseAnchor : function(){
53866         return this.canchors[this.position];
53867     },
53868
53869     getSlideAnchor : function(){
53870         return this.sanchors[this.position];
53871     },
53872
53873     getAlignAdj : function(){
53874         var cm = this.cmargins;
53875         switch(this.position){
53876             case "west":
53877                 return [0, 0];
53878             break;
53879             case "east":
53880                 return [0, 0];
53881             break;
53882             case "north":
53883                 return [0, 0];
53884             break;
53885             case "south":
53886                 return [0, 0];
53887             break;
53888         }
53889     },
53890
53891     getExpandAdj : function(){
53892         var c = this.collapsedEl, cm = this.cmargins;
53893         switch(this.position){
53894             case "west":
53895                 return [-(cm.right+c.getWidth()+cm.left), 0];
53896             break;
53897             case "east":
53898                 return [cm.right+c.getWidth()+cm.left, 0];
53899             break;
53900             case "north":
53901                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53902             break;
53903             case "south":
53904                 return [0, cm.top+cm.bottom+c.getHeight()];
53905             break;
53906         }
53907     }
53908 });/*
53909  * Based on:
53910  * Ext JS Library 1.1.1
53911  * Copyright(c) 2006-2007, Ext JS, LLC.
53912  *
53913  * Originally Released Under LGPL - original licence link has changed is not relivant.
53914  *
53915  * Fork - LGPL
53916  * <script type="text/javascript">
53917  */
53918 /*
53919  * These classes are private internal classes
53920  */
53921 Roo.CenterLayoutRegion = function(mgr, config){
53922     Roo.LayoutRegion.call(this, mgr, config, "center");
53923     this.visible = true;
53924     this.minWidth = config.minWidth || 20;
53925     this.minHeight = config.minHeight || 20;
53926 };
53927
53928 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53929     hide : function(){
53930         // center panel can't be hidden
53931     },
53932     
53933     show : function(){
53934         // center panel can't be hidden
53935     },
53936     
53937     getMinWidth: function(){
53938         return this.minWidth;
53939     },
53940     
53941     getMinHeight: function(){
53942         return this.minHeight;
53943     }
53944 });
53945
53946
53947 Roo.NorthLayoutRegion = function(mgr, config){
53948     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53949     if(this.split){
53950         this.split.placement = Roo.SplitBar.TOP;
53951         this.split.orientation = Roo.SplitBar.VERTICAL;
53952         this.split.el.addClass("x-layout-split-v");
53953     }
53954     var size = config.initialSize || config.height;
53955     if(typeof size != "undefined"){
53956         this.el.setHeight(size);
53957     }
53958 };
53959 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53960     orientation: Roo.SplitBar.VERTICAL,
53961     getBox : function(){
53962         if(this.collapsed){
53963             return this.collapsedEl.getBox();
53964         }
53965         var box = this.el.getBox();
53966         if(this.split){
53967             box.height += this.split.el.getHeight();
53968         }
53969         return box;
53970     },
53971     
53972     updateBox : function(box){
53973         if(this.split && !this.collapsed){
53974             box.height -= this.split.el.getHeight();
53975             this.split.el.setLeft(box.x);
53976             this.split.el.setTop(box.y+box.height);
53977             this.split.el.setWidth(box.width);
53978         }
53979         if(this.collapsed){
53980             this.updateBody(box.width, null);
53981         }
53982         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53983     }
53984 });
53985
53986 Roo.SouthLayoutRegion = function(mgr, config){
53987     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53988     if(this.split){
53989         this.split.placement = Roo.SplitBar.BOTTOM;
53990         this.split.orientation = Roo.SplitBar.VERTICAL;
53991         this.split.el.addClass("x-layout-split-v");
53992     }
53993     var size = config.initialSize || config.height;
53994     if(typeof size != "undefined"){
53995         this.el.setHeight(size);
53996     }
53997 };
53998 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53999     orientation: Roo.SplitBar.VERTICAL,
54000     getBox : function(){
54001         if(this.collapsed){
54002             return this.collapsedEl.getBox();
54003         }
54004         var box = this.el.getBox();
54005         if(this.split){
54006             var sh = this.split.el.getHeight();
54007             box.height += sh;
54008             box.y -= sh;
54009         }
54010         return box;
54011     },
54012     
54013     updateBox : function(box){
54014         if(this.split && !this.collapsed){
54015             var sh = this.split.el.getHeight();
54016             box.height -= sh;
54017             box.y += sh;
54018             this.split.el.setLeft(box.x);
54019             this.split.el.setTop(box.y-sh);
54020             this.split.el.setWidth(box.width);
54021         }
54022         if(this.collapsed){
54023             this.updateBody(box.width, null);
54024         }
54025         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54026     }
54027 });
54028
54029 Roo.EastLayoutRegion = function(mgr, config){
54030     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54031     if(this.split){
54032         this.split.placement = Roo.SplitBar.RIGHT;
54033         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54034         this.split.el.addClass("x-layout-split-h");
54035     }
54036     var size = config.initialSize || config.width;
54037     if(typeof size != "undefined"){
54038         this.el.setWidth(size);
54039     }
54040 };
54041 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54042     orientation: Roo.SplitBar.HORIZONTAL,
54043     getBox : function(){
54044         if(this.collapsed){
54045             return this.collapsedEl.getBox();
54046         }
54047         var box = this.el.getBox();
54048         if(this.split){
54049             var sw = this.split.el.getWidth();
54050             box.width += sw;
54051             box.x -= sw;
54052         }
54053         return box;
54054     },
54055
54056     updateBox : function(box){
54057         if(this.split && !this.collapsed){
54058             var sw = this.split.el.getWidth();
54059             box.width -= sw;
54060             this.split.el.setLeft(box.x);
54061             this.split.el.setTop(box.y);
54062             this.split.el.setHeight(box.height);
54063             box.x += sw;
54064         }
54065         if(this.collapsed){
54066             this.updateBody(null, box.height);
54067         }
54068         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54069     }
54070 });
54071
54072 Roo.WestLayoutRegion = function(mgr, config){
54073     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54074     if(this.split){
54075         this.split.placement = Roo.SplitBar.LEFT;
54076         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54077         this.split.el.addClass("x-layout-split-h");
54078     }
54079     var size = config.initialSize || config.width;
54080     if(typeof size != "undefined"){
54081         this.el.setWidth(size);
54082     }
54083 };
54084 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54085     orientation: Roo.SplitBar.HORIZONTAL,
54086     getBox : function(){
54087         if(this.collapsed){
54088             return this.collapsedEl.getBox();
54089         }
54090         var box = this.el.getBox();
54091         if(this.split){
54092             box.width += this.split.el.getWidth();
54093         }
54094         return box;
54095     },
54096     
54097     updateBox : function(box){
54098         if(this.split && !this.collapsed){
54099             var sw = this.split.el.getWidth();
54100             box.width -= sw;
54101             this.split.el.setLeft(box.x+box.width);
54102             this.split.el.setTop(box.y);
54103             this.split.el.setHeight(box.height);
54104         }
54105         if(this.collapsed){
54106             this.updateBody(null, box.height);
54107         }
54108         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54109     }
54110 });
54111 /*
54112  * Based on:
54113  * Ext JS Library 1.1.1
54114  * Copyright(c) 2006-2007, Ext JS, LLC.
54115  *
54116  * Originally Released Under LGPL - original licence link has changed is not relivant.
54117  *
54118  * Fork - LGPL
54119  * <script type="text/javascript">
54120  */
54121  
54122  
54123 /*
54124  * Private internal class for reading and applying state
54125  */
54126 Roo.LayoutStateManager = function(layout){
54127      // default empty state
54128      this.state = {
54129         north: {},
54130         south: {},
54131         east: {},
54132         west: {}       
54133     };
54134 };
54135
54136 Roo.LayoutStateManager.prototype = {
54137     init : function(layout, provider){
54138         this.provider = provider;
54139         var state = provider.get(layout.id+"-layout-state");
54140         if(state){
54141             var wasUpdating = layout.isUpdating();
54142             if(!wasUpdating){
54143                 layout.beginUpdate();
54144             }
54145             for(var key in state){
54146                 if(typeof state[key] != "function"){
54147                     var rstate = state[key];
54148                     var r = layout.getRegion(key);
54149                     if(r && rstate){
54150                         if(rstate.size){
54151                             r.resizeTo(rstate.size);
54152                         }
54153                         if(rstate.collapsed == true){
54154                             r.collapse(true);
54155                         }else{
54156                             r.expand(null, true);
54157                         }
54158                     }
54159                 }
54160             }
54161             if(!wasUpdating){
54162                 layout.endUpdate();
54163             }
54164             this.state = state; 
54165         }
54166         this.layout = layout;
54167         layout.on("regionresized", this.onRegionResized, this);
54168         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54169         layout.on("regionexpanded", this.onRegionExpanded, this);
54170     },
54171     
54172     storeState : function(){
54173         this.provider.set(this.layout.id+"-layout-state", this.state);
54174     },
54175     
54176     onRegionResized : function(region, newSize){
54177         this.state[region.getPosition()].size = newSize;
54178         this.storeState();
54179     },
54180     
54181     onRegionCollapsed : function(region){
54182         this.state[region.getPosition()].collapsed = true;
54183         this.storeState();
54184     },
54185     
54186     onRegionExpanded : function(region){
54187         this.state[region.getPosition()].collapsed = false;
54188         this.storeState();
54189     }
54190 };/*
54191  * Based on:
54192  * Ext JS Library 1.1.1
54193  * Copyright(c) 2006-2007, Ext JS, LLC.
54194  *
54195  * Originally Released Under LGPL - original licence link has changed is not relivant.
54196  *
54197  * Fork - LGPL
54198  * <script type="text/javascript">
54199  */
54200 /**
54201  * @class Roo.ContentPanel
54202  * @extends Roo.util.Observable
54203  * A basic ContentPanel element.
54204  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54205  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54206  * @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
54207  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54208  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54209  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54210  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54211  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54212  * @cfg {String} title          The title for this panel
54213  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54214  * @cfg {String} url            Calls {@link #setUrl} with this value
54215  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54216  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54217  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54218  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54219  * @cfg {String}    style  Extra style to add to the content panel 
54220
54221  * @constructor
54222  * Create a new ContentPanel.
54223  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54224  * @param {String/Object} config A string to set only the title or a config object
54225  * @param {String} content (optional) Set the HTML content for this panel
54226  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54227  */
54228 Roo.ContentPanel = function(el, config, content){
54229     
54230      
54231     /*
54232     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54233         config = el;
54234         el = Roo.id();
54235     }
54236     if (config && config.parentLayout) { 
54237         el = config.parentLayout.el.createChild(); 
54238     }
54239     */
54240     if(el.autoCreate){ // xtype is available if this is called from factory
54241         config = el;
54242         el = Roo.id();
54243     }
54244     this.el = Roo.get(el);
54245     if(!this.el && config && config.autoCreate){
54246         if(typeof config.autoCreate == "object"){
54247             if(!config.autoCreate.id){
54248                 config.autoCreate.id = config.id||el;
54249             }
54250             this.el = Roo.DomHelper.append(document.body,
54251                         config.autoCreate, true);
54252         }else{
54253             this.el = Roo.DomHelper.append(document.body,
54254                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54255         }
54256     }
54257     
54258     
54259     this.closable = false;
54260     this.loaded = false;
54261     this.active = false;
54262     if(typeof config == "string"){
54263         this.title = config;
54264     }else{
54265         Roo.apply(this, config);
54266     }
54267     
54268     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54269         this.wrapEl = this.el.wrap();
54270         this.toolbar.container = this.el.insertSibling(false, 'before');
54271         this.toolbar = new Roo.Toolbar(this.toolbar);
54272     }
54273     
54274     // xtype created footer. - not sure if will work as we normally have to render first..
54275     if (this.footer && !this.footer.el && this.footer.xtype) {
54276         if (!this.wrapEl) {
54277             this.wrapEl = this.el.wrap();
54278         }
54279     
54280         this.footer.container = this.wrapEl.createChild();
54281          
54282         this.footer = Roo.factory(this.footer, Roo);
54283         
54284     }
54285     
54286     if(this.resizeEl){
54287         this.resizeEl = Roo.get(this.resizeEl, true);
54288     }else{
54289         this.resizeEl = this.el;
54290     }
54291     // handle view.xtype
54292     
54293  
54294     
54295     
54296     this.addEvents({
54297         /**
54298          * @event activate
54299          * Fires when this panel is activated. 
54300          * @param {Roo.ContentPanel} this
54301          */
54302         "activate" : true,
54303         /**
54304          * @event deactivate
54305          * Fires when this panel is activated. 
54306          * @param {Roo.ContentPanel} this
54307          */
54308         "deactivate" : true,
54309
54310         /**
54311          * @event resize
54312          * Fires when this panel is resized if fitToFrame is true.
54313          * @param {Roo.ContentPanel} this
54314          * @param {Number} width The width after any component adjustments
54315          * @param {Number} height The height after any component adjustments
54316          */
54317         "resize" : true,
54318         
54319          /**
54320          * @event render
54321          * Fires when this tab is created
54322          * @param {Roo.ContentPanel} this
54323          */
54324         "render" : true
54325          
54326         
54327     });
54328     
54329
54330     
54331     
54332     if(this.autoScroll){
54333         this.resizeEl.setStyle("overflow", "auto");
54334     } else {
54335         // fix randome scrolling
54336         this.el.on('scroll', function() {
54337             Roo.log('fix random scolling');
54338             this.scrollTo('top',0); 
54339         });
54340     }
54341     content = content || this.content;
54342     if(content){
54343         this.setContent(content);
54344     }
54345     if(config && config.url){
54346         this.setUrl(this.url, this.params, this.loadOnce);
54347     }
54348     
54349     
54350     
54351     Roo.ContentPanel.superclass.constructor.call(this);
54352     
54353     if (this.view && typeof(this.view.xtype) != 'undefined') {
54354         this.view.el = this.el.appendChild(document.createElement("div"));
54355         this.view = Roo.factory(this.view); 
54356         this.view.render  &&  this.view.render(false, '');  
54357     }
54358     
54359     
54360     this.fireEvent('render', this);
54361 };
54362
54363 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54364     tabTip:'',
54365     setRegion : function(region){
54366         this.region = region;
54367         if(region){
54368            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54369         }else{
54370            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54371         } 
54372     },
54373     
54374     /**
54375      * Returns the toolbar for this Panel if one was configured. 
54376      * @return {Roo.Toolbar} 
54377      */
54378     getToolbar : function(){
54379         return this.toolbar;
54380     },
54381     
54382     setActiveState : function(active){
54383         this.active = active;
54384         if(!active){
54385             this.fireEvent("deactivate", this);
54386         }else{
54387             this.fireEvent("activate", this);
54388         }
54389     },
54390     /**
54391      * Updates this panel's element
54392      * @param {String} content The new content
54393      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54394     */
54395     setContent : function(content, loadScripts){
54396         this.el.update(content, loadScripts);
54397     },
54398
54399     ignoreResize : function(w, h){
54400         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54401             return true;
54402         }else{
54403             this.lastSize = {width: w, height: h};
54404             return false;
54405         }
54406     },
54407     /**
54408      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54409      * @return {Roo.UpdateManager} The UpdateManager
54410      */
54411     getUpdateManager : function(){
54412         return this.el.getUpdateManager();
54413     },
54414      /**
54415      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54416      * @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:
54417 <pre><code>
54418 panel.load({
54419     url: "your-url.php",
54420     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54421     callback: yourFunction,
54422     scope: yourObject, //(optional scope)
54423     discardUrl: false,
54424     nocache: false,
54425     text: "Loading...",
54426     timeout: 30,
54427     scripts: false
54428 });
54429 </code></pre>
54430      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54431      * 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.
54432      * @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}
54433      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54434      * @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.
54435      * @return {Roo.ContentPanel} this
54436      */
54437     load : function(){
54438         var um = this.el.getUpdateManager();
54439         um.update.apply(um, arguments);
54440         return this;
54441     },
54442
54443
54444     /**
54445      * 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.
54446      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54447      * @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)
54448      * @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)
54449      * @return {Roo.UpdateManager} The UpdateManager
54450      */
54451     setUrl : function(url, params, loadOnce){
54452         if(this.refreshDelegate){
54453             this.removeListener("activate", this.refreshDelegate);
54454         }
54455         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54456         this.on("activate", this.refreshDelegate);
54457         return this.el.getUpdateManager();
54458     },
54459     
54460     _handleRefresh : function(url, params, loadOnce){
54461         if(!loadOnce || !this.loaded){
54462             var updater = this.el.getUpdateManager();
54463             updater.update(url, params, this._setLoaded.createDelegate(this));
54464         }
54465     },
54466     
54467     _setLoaded : function(){
54468         this.loaded = true;
54469     }, 
54470     
54471     /**
54472      * Returns this panel's id
54473      * @return {String} 
54474      */
54475     getId : function(){
54476         return this.el.id;
54477     },
54478     
54479     /** 
54480      * Returns this panel's element - used by regiosn to add.
54481      * @return {Roo.Element} 
54482      */
54483     getEl : function(){
54484         return this.wrapEl || this.el;
54485     },
54486     
54487     adjustForComponents : function(width, height)
54488     {
54489         //Roo.log('adjustForComponents ');
54490         if(this.resizeEl != this.el){
54491             width -= this.el.getFrameWidth('lr');
54492             height -= this.el.getFrameWidth('tb');
54493         }
54494         if(this.toolbar){
54495             var te = this.toolbar.getEl();
54496             height -= te.getHeight();
54497             te.setWidth(width);
54498         }
54499         if(this.footer){
54500             var te = this.footer.getEl();
54501             //Roo.log("footer:" + te.getHeight());
54502             
54503             height -= te.getHeight();
54504             te.setWidth(width);
54505         }
54506         
54507         
54508         if(this.adjustments){
54509             width += this.adjustments[0];
54510             height += this.adjustments[1];
54511         }
54512         return {"width": width, "height": height};
54513     },
54514     
54515     setSize : function(width, height){
54516         if(this.fitToFrame && !this.ignoreResize(width, height)){
54517             if(this.fitContainer && this.resizeEl != this.el){
54518                 this.el.setSize(width, height);
54519             }
54520             var size = this.adjustForComponents(width, height);
54521             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54522             this.fireEvent('resize', this, size.width, size.height);
54523         }
54524     },
54525     
54526     /**
54527      * Returns this panel's title
54528      * @return {String} 
54529      */
54530     getTitle : function(){
54531         return this.title;
54532     },
54533     
54534     /**
54535      * Set this panel's title
54536      * @param {String} title
54537      */
54538     setTitle : function(title){
54539         this.title = title;
54540         if(this.region){
54541             this.region.updatePanelTitle(this, title);
54542         }
54543     },
54544     
54545     /**
54546      * Returns true is this panel was configured to be closable
54547      * @return {Boolean} 
54548      */
54549     isClosable : function(){
54550         return this.closable;
54551     },
54552     
54553     beforeSlide : function(){
54554         this.el.clip();
54555         this.resizeEl.clip();
54556     },
54557     
54558     afterSlide : function(){
54559         this.el.unclip();
54560         this.resizeEl.unclip();
54561     },
54562     
54563     /**
54564      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54565      *   Will fail silently if the {@link #setUrl} method has not been called.
54566      *   This does not activate the panel, just updates its content.
54567      */
54568     refresh : function(){
54569         if(this.refreshDelegate){
54570            this.loaded = false;
54571            this.refreshDelegate();
54572         }
54573     },
54574     
54575     /**
54576      * Destroys this panel
54577      */
54578     destroy : function(){
54579         this.el.removeAllListeners();
54580         var tempEl = document.createElement("span");
54581         tempEl.appendChild(this.el.dom);
54582         tempEl.innerHTML = "";
54583         this.el.remove();
54584         this.el = null;
54585     },
54586     
54587     /**
54588      * form - if the content panel contains a form - this is a reference to it.
54589      * @type {Roo.form.Form}
54590      */
54591     form : false,
54592     /**
54593      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54594      *    This contains a reference to it.
54595      * @type {Roo.View}
54596      */
54597     view : false,
54598     
54599       /**
54600      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54601      * <pre><code>
54602
54603 layout.addxtype({
54604        xtype : 'Form',
54605        items: [ .... ]
54606    }
54607 );
54608
54609 </code></pre>
54610      * @param {Object} cfg Xtype definition of item to add.
54611      */
54612     
54613     addxtype : function(cfg) {
54614         // add form..
54615         if (cfg.xtype.match(/^Form$/)) {
54616             
54617             var el;
54618             //if (this.footer) {
54619             //    el = this.footer.container.insertSibling(false, 'before');
54620             //} else {
54621                 el = this.el.createChild();
54622             //}
54623
54624             this.form = new  Roo.form.Form(cfg);
54625             
54626             
54627             if ( this.form.allItems.length) {
54628                 this.form.render(el.dom);
54629             }
54630             return this.form;
54631         }
54632         // should only have one of theses..
54633         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54634             // views.. should not be just added - used named prop 'view''
54635             
54636             cfg.el = this.el.appendChild(document.createElement("div"));
54637             // factory?
54638             
54639             var ret = new Roo.factory(cfg);
54640              
54641              ret.render && ret.render(false, ''); // render blank..
54642             this.view = ret;
54643             return ret;
54644         }
54645         return false;
54646     }
54647 });
54648
54649 /**
54650  * @class Roo.GridPanel
54651  * @extends Roo.ContentPanel
54652  * @constructor
54653  * Create a new GridPanel.
54654  * @param {Roo.grid.Grid} grid The grid for this panel
54655  * @param {String/Object} config A string to set only the panel's title, or a config object
54656  */
54657 Roo.GridPanel = function(grid, config){
54658     
54659   
54660     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54661         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54662         
54663     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54664     
54665     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54666     
54667     if(this.toolbar){
54668         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54669     }
54670     // xtype created footer. - not sure if will work as we normally have to render first..
54671     if (this.footer && !this.footer.el && this.footer.xtype) {
54672         
54673         this.footer.container = this.grid.getView().getFooterPanel(true);
54674         this.footer.dataSource = this.grid.dataSource;
54675         this.footer = Roo.factory(this.footer, Roo);
54676         
54677     }
54678     
54679     grid.monitorWindowResize = false; // turn off autosizing
54680     grid.autoHeight = false;
54681     grid.autoWidth = false;
54682     this.grid = grid;
54683     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54684 };
54685
54686 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54687     getId : function(){
54688         return this.grid.id;
54689     },
54690     
54691     /**
54692      * Returns the grid for this panel
54693      * @return {Roo.grid.Grid} 
54694      */
54695     getGrid : function(){
54696         return this.grid;    
54697     },
54698     
54699     setSize : function(width, height){
54700         if(!this.ignoreResize(width, height)){
54701             var grid = this.grid;
54702             var size = this.adjustForComponents(width, height);
54703             grid.getGridEl().setSize(size.width, size.height);
54704             grid.autoSize();
54705         }
54706     },
54707     
54708     beforeSlide : function(){
54709         this.grid.getView().scroller.clip();
54710     },
54711     
54712     afterSlide : function(){
54713         this.grid.getView().scroller.unclip();
54714     },
54715     
54716     destroy : function(){
54717         this.grid.destroy();
54718         delete this.grid;
54719         Roo.GridPanel.superclass.destroy.call(this); 
54720     }
54721 });
54722
54723
54724 /**
54725  * @class Roo.NestedLayoutPanel
54726  * @extends Roo.ContentPanel
54727  * @constructor
54728  * Create a new NestedLayoutPanel.
54729  * 
54730  * 
54731  * @param {Roo.BorderLayout} layout The layout for this panel
54732  * @param {String/Object} config A string to set only the title or a config object
54733  */
54734 Roo.NestedLayoutPanel = function(layout, config)
54735 {
54736     // construct with only one argument..
54737     /* FIXME - implement nicer consturctors
54738     if (layout.layout) {
54739         config = layout;
54740         layout = config.layout;
54741         delete config.layout;
54742     }
54743     if (layout.xtype && !layout.getEl) {
54744         // then layout needs constructing..
54745         layout = Roo.factory(layout, Roo);
54746     }
54747     */
54748     
54749     
54750     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54751     
54752     layout.monitorWindowResize = false; // turn off autosizing
54753     this.layout = layout;
54754     this.layout.getEl().addClass("x-layout-nested-layout");
54755     
54756     
54757     
54758     
54759 };
54760
54761 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54762
54763     setSize : function(width, height){
54764         if(!this.ignoreResize(width, height)){
54765             var size = this.adjustForComponents(width, height);
54766             var el = this.layout.getEl();
54767             el.setSize(size.width, size.height);
54768             var touch = el.dom.offsetWidth;
54769             this.layout.layout();
54770             // ie requires a double layout on the first pass
54771             if(Roo.isIE && !this.initialized){
54772                 this.initialized = true;
54773                 this.layout.layout();
54774             }
54775         }
54776     },
54777     
54778     // activate all subpanels if not currently active..
54779     
54780     setActiveState : function(active){
54781         this.active = active;
54782         if(!active){
54783             this.fireEvent("deactivate", this);
54784             return;
54785         }
54786         
54787         this.fireEvent("activate", this);
54788         // not sure if this should happen before or after..
54789         if (!this.layout) {
54790             return; // should not happen..
54791         }
54792         var reg = false;
54793         for (var r in this.layout.regions) {
54794             reg = this.layout.getRegion(r);
54795             if (reg.getActivePanel()) {
54796                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54797                 reg.setActivePanel(reg.getActivePanel());
54798                 continue;
54799             }
54800             if (!reg.panels.length) {
54801                 continue;
54802             }
54803             reg.showPanel(reg.getPanel(0));
54804         }
54805         
54806         
54807         
54808         
54809     },
54810     
54811     /**
54812      * Returns the nested BorderLayout for this panel
54813      * @return {Roo.BorderLayout} 
54814      */
54815     getLayout : function(){
54816         return this.layout;
54817     },
54818     
54819      /**
54820      * Adds a xtype elements to the layout of the nested panel
54821      * <pre><code>
54822
54823 panel.addxtype({
54824        xtype : 'ContentPanel',
54825        region: 'west',
54826        items: [ .... ]
54827    }
54828 );
54829
54830 panel.addxtype({
54831         xtype : 'NestedLayoutPanel',
54832         region: 'west',
54833         layout: {
54834            center: { },
54835            west: { }   
54836         },
54837         items : [ ... list of content panels or nested layout panels.. ]
54838    }
54839 );
54840 </code></pre>
54841      * @param {Object} cfg Xtype definition of item to add.
54842      */
54843     addxtype : function(cfg) {
54844         return this.layout.addxtype(cfg);
54845     
54846     }
54847 });
54848
54849 Roo.ScrollPanel = function(el, config, content){
54850     config = config || {};
54851     config.fitToFrame = true;
54852     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54853     
54854     this.el.dom.style.overflow = "hidden";
54855     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54856     this.el.removeClass("x-layout-inactive-content");
54857     this.el.on("mousewheel", this.onWheel, this);
54858
54859     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54860     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54861     up.unselectable(); down.unselectable();
54862     up.on("click", this.scrollUp, this);
54863     down.on("click", this.scrollDown, this);
54864     up.addClassOnOver("x-scroller-btn-over");
54865     down.addClassOnOver("x-scroller-btn-over");
54866     up.addClassOnClick("x-scroller-btn-click");
54867     down.addClassOnClick("x-scroller-btn-click");
54868     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54869
54870     this.resizeEl = this.el;
54871     this.el = wrap; this.up = up; this.down = down;
54872 };
54873
54874 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54875     increment : 100,
54876     wheelIncrement : 5,
54877     scrollUp : function(){
54878         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54879     },
54880
54881     scrollDown : function(){
54882         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54883     },
54884
54885     afterScroll : function(){
54886         var el = this.resizeEl;
54887         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54888         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54889         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54890     },
54891
54892     setSize : function(){
54893         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54894         this.afterScroll();
54895     },
54896
54897     onWheel : function(e){
54898         var d = e.getWheelDelta();
54899         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54900         this.afterScroll();
54901         e.stopEvent();
54902     },
54903
54904     setContent : function(content, loadScripts){
54905         this.resizeEl.update(content, loadScripts);
54906     }
54907
54908 });
54909
54910
54911
54912
54913
54914
54915
54916
54917
54918 /**
54919  * @class Roo.TreePanel
54920  * @extends Roo.ContentPanel
54921  * @constructor
54922  * Create a new TreePanel. - defaults to fit/scoll contents.
54923  * @param {String/Object} config A string to set only the panel's title, or a config object
54924  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54925  */
54926 Roo.TreePanel = function(config){
54927     var el = config.el;
54928     var tree = config.tree;
54929     delete config.tree; 
54930     delete config.el; // hopefull!
54931     
54932     // wrapper for IE7 strict & safari scroll issue
54933     
54934     var treeEl = el.createChild();
54935     config.resizeEl = treeEl;
54936     
54937     
54938     
54939     Roo.TreePanel.superclass.constructor.call(this, el, config);
54940  
54941  
54942     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54943     //console.log(tree);
54944     this.on('activate', function()
54945     {
54946         if (this.tree.rendered) {
54947             return;
54948         }
54949         //console.log('render tree');
54950         this.tree.render();
54951     });
54952     // this should not be needed.. - it's actually the 'el' that resizes?
54953     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54954     
54955     //this.on('resize',  function (cp, w, h) {
54956     //        this.tree.innerCt.setWidth(w);
54957     //        this.tree.innerCt.setHeight(h);
54958     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54959     //});
54960
54961         
54962     
54963 };
54964
54965 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54966     fitToFrame : true,
54967     autoScroll : true
54968 });
54969
54970
54971
54972
54973
54974
54975
54976
54977
54978
54979
54980 /*
54981  * Based on:
54982  * Ext JS Library 1.1.1
54983  * Copyright(c) 2006-2007, Ext JS, LLC.
54984  *
54985  * Originally Released Under LGPL - original licence link has changed is not relivant.
54986  *
54987  * Fork - LGPL
54988  * <script type="text/javascript">
54989  */
54990  
54991
54992 /**
54993  * @class Roo.ReaderLayout
54994  * @extends Roo.BorderLayout
54995  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54996  * center region containing two nested regions (a top one for a list view and one for item preview below),
54997  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54998  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54999  * expedites the setup of the overall layout and regions for this common application style.
55000  * Example:
55001  <pre><code>
55002 var reader = new Roo.ReaderLayout();
55003 var CP = Roo.ContentPanel;  // shortcut for adding
55004
55005 reader.beginUpdate();
55006 reader.add("north", new CP("north", "North"));
55007 reader.add("west", new CP("west", {title: "West"}));
55008 reader.add("east", new CP("east", {title: "East"}));
55009
55010 reader.regions.listView.add(new CP("listView", "List"));
55011 reader.regions.preview.add(new CP("preview", "Preview"));
55012 reader.endUpdate();
55013 </code></pre>
55014 * @constructor
55015 * Create a new ReaderLayout
55016 * @param {Object} config Configuration options
55017 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55018 * document.body if omitted)
55019 */
55020 Roo.ReaderLayout = function(config, renderTo){
55021     var c = config || {size:{}};
55022     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55023         north: c.north !== false ? Roo.apply({
55024             split:false,
55025             initialSize: 32,
55026             titlebar: false
55027         }, c.north) : false,
55028         west: c.west !== false ? Roo.apply({
55029             split:true,
55030             initialSize: 200,
55031             minSize: 175,
55032             maxSize: 400,
55033             titlebar: true,
55034             collapsible: true,
55035             animate: true,
55036             margins:{left:5,right:0,bottom:5,top:5},
55037             cmargins:{left:5,right:5,bottom:5,top:5}
55038         }, c.west) : false,
55039         east: c.east !== false ? Roo.apply({
55040             split:true,
55041             initialSize: 200,
55042             minSize: 175,
55043             maxSize: 400,
55044             titlebar: true,
55045             collapsible: true,
55046             animate: true,
55047             margins:{left:0,right:5,bottom:5,top:5},
55048             cmargins:{left:5,right:5,bottom:5,top:5}
55049         }, c.east) : false,
55050         center: Roo.apply({
55051             tabPosition: 'top',
55052             autoScroll:false,
55053             closeOnTab: true,
55054             titlebar:false,
55055             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55056         }, c.center)
55057     });
55058
55059     this.el.addClass('x-reader');
55060
55061     this.beginUpdate();
55062
55063     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55064         south: c.preview !== false ? Roo.apply({
55065             split:true,
55066             initialSize: 200,
55067             minSize: 100,
55068             autoScroll:true,
55069             collapsible:true,
55070             titlebar: true,
55071             cmargins:{top:5,left:0, right:0, bottom:0}
55072         }, c.preview) : false,
55073         center: Roo.apply({
55074             autoScroll:false,
55075             titlebar:false,
55076             minHeight:200
55077         }, c.listView)
55078     });
55079     this.add('center', new Roo.NestedLayoutPanel(inner,
55080             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55081
55082     this.endUpdate();
55083
55084     this.regions.preview = inner.getRegion('south');
55085     this.regions.listView = inner.getRegion('center');
55086 };
55087
55088 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55089  * Based on:
55090  * Ext JS Library 1.1.1
55091  * Copyright(c) 2006-2007, Ext JS, LLC.
55092  *
55093  * Originally Released Under LGPL - original licence link has changed is not relivant.
55094  *
55095  * Fork - LGPL
55096  * <script type="text/javascript">
55097  */
55098  
55099 /**
55100  * @class Roo.grid.Grid
55101  * @extends Roo.util.Observable
55102  * This class represents the primary interface of a component based grid control.
55103  * <br><br>Usage:<pre><code>
55104  var grid = new Roo.grid.Grid("my-container-id", {
55105      ds: myDataStore,
55106      cm: myColModel,
55107      selModel: mySelectionModel,
55108      autoSizeColumns: true,
55109      monitorWindowResize: false,
55110      trackMouseOver: true
55111  });
55112  // set any options
55113  grid.render();
55114  * </code></pre>
55115  * <b>Common Problems:</b><br/>
55116  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55117  * element will correct this<br/>
55118  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55119  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55120  * are unpredictable.<br/>
55121  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55122  * grid to calculate dimensions/offsets.<br/>
55123   * @constructor
55124  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55125  * The container MUST have some type of size defined for the grid to fill. The container will be
55126  * automatically set to position relative if it isn't already.
55127  * @param {Object} config A config object that sets properties on this grid.
55128  */
55129 Roo.grid.Grid = function(container, config){
55130         // initialize the container
55131         this.container = Roo.get(container);
55132         this.container.update("");
55133         this.container.setStyle("overflow", "hidden");
55134     this.container.addClass('x-grid-container');
55135
55136     this.id = this.container.id;
55137
55138     Roo.apply(this, config);
55139     // check and correct shorthanded configs
55140     if(this.ds){
55141         this.dataSource = this.ds;
55142         delete this.ds;
55143     }
55144     if(this.cm){
55145         this.colModel = this.cm;
55146         delete this.cm;
55147     }
55148     if(this.sm){
55149         this.selModel = this.sm;
55150         delete this.sm;
55151     }
55152
55153     if (this.selModel) {
55154         this.selModel = Roo.factory(this.selModel, Roo.grid);
55155         this.sm = this.selModel;
55156         this.sm.xmodule = this.xmodule || false;
55157     }
55158     if (typeof(this.colModel.config) == 'undefined') {
55159         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55160         this.cm = this.colModel;
55161         this.cm.xmodule = this.xmodule || false;
55162     }
55163     if (this.dataSource) {
55164         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55165         this.ds = this.dataSource;
55166         this.ds.xmodule = this.xmodule || false;
55167          
55168     }
55169     
55170     
55171     
55172     if(this.width){
55173         this.container.setWidth(this.width);
55174     }
55175
55176     if(this.height){
55177         this.container.setHeight(this.height);
55178     }
55179     /** @private */
55180         this.addEvents({
55181         // raw events
55182         /**
55183          * @event click
55184          * The raw click event for the entire grid.
55185          * @param {Roo.EventObject} e
55186          */
55187         "click" : true,
55188         /**
55189          * @event dblclick
55190          * The raw dblclick event for the entire grid.
55191          * @param {Roo.EventObject} e
55192          */
55193         "dblclick" : true,
55194         /**
55195          * @event contextmenu
55196          * The raw contextmenu event for the entire grid.
55197          * @param {Roo.EventObject} e
55198          */
55199         "contextmenu" : true,
55200         /**
55201          * @event mousedown
55202          * The raw mousedown event for the entire grid.
55203          * @param {Roo.EventObject} e
55204          */
55205         "mousedown" : true,
55206         /**
55207          * @event mouseup
55208          * The raw mouseup event for the entire grid.
55209          * @param {Roo.EventObject} e
55210          */
55211         "mouseup" : true,
55212         /**
55213          * @event mouseover
55214          * The raw mouseover event for the entire grid.
55215          * @param {Roo.EventObject} e
55216          */
55217         "mouseover" : true,
55218         /**
55219          * @event mouseout
55220          * The raw mouseout event for the entire grid.
55221          * @param {Roo.EventObject} e
55222          */
55223         "mouseout" : true,
55224         /**
55225          * @event keypress
55226          * The raw keypress event for the entire grid.
55227          * @param {Roo.EventObject} e
55228          */
55229         "keypress" : true,
55230         /**
55231          * @event keydown
55232          * The raw keydown event for the entire grid.
55233          * @param {Roo.EventObject} e
55234          */
55235         "keydown" : true,
55236
55237         // custom events
55238
55239         /**
55240          * @event cellclick
55241          * Fires when a cell is clicked
55242          * @param {Grid} this
55243          * @param {Number} rowIndex
55244          * @param {Number} columnIndex
55245          * @param {Roo.EventObject} e
55246          */
55247         "cellclick" : true,
55248         /**
55249          * @event celldblclick
55250          * Fires when a cell is double clicked
55251          * @param {Grid} this
55252          * @param {Number} rowIndex
55253          * @param {Number} columnIndex
55254          * @param {Roo.EventObject} e
55255          */
55256         "celldblclick" : true,
55257         /**
55258          * @event rowclick
55259          * Fires when a row is clicked
55260          * @param {Grid} this
55261          * @param {Number} rowIndex
55262          * @param {Roo.EventObject} e
55263          */
55264         "rowclick" : true,
55265         /**
55266          * @event rowdblclick
55267          * Fires when a row is double clicked
55268          * @param {Grid} this
55269          * @param {Number} rowIndex
55270          * @param {Roo.EventObject} e
55271          */
55272         "rowdblclick" : true,
55273         /**
55274          * @event headerclick
55275          * Fires when a header is clicked
55276          * @param {Grid} this
55277          * @param {Number} columnIndex
55278          * @param {Roo.EventObject} e
55279          */
55280         "headerclick" : true,
55281         /**
55282          * @event headerdblclick
55283          * Fires when a header cell is double clicked
55284          * @param {Grid} this
55285          * @param {Number} columnIndex
55286          * @param {Roo.EventObject} e
55287          */
55288         "headerdblclick" : true,
55289         /**
55290          * @event rowcontextmenu
55291          * Fires when a row is right clicked
55292          * @param {Grid} this
55293          * @param {Number} rowIndex
55294          * @param {Roo.EventObject} e
55295          */
55296         "rowcontextmenu" : true,
55297         /**
55298          * @event cellcontextmenu
55299          * Fires when a cell is right clicked
55300          * @param {Grid} this
55301          * @param {Number} rowIndex
55302          * @param {Number} cellIndex
55303          * @param {Roo.EventObject} e
55304          */
55305          "cellcontextmenu" : true,
55306         /**
55307          * @event headercontextmenu
55308          * Fires when a header is right clicked
55309          * @param {Grid} this
55310          * @param {Number} columnIndex
55311          * @param {Roo.EventObject} e
55312          */
55313         "headercontextmenu" : true,
55314         /**
55315          * @event bodyscroll
55316          * Fires when the body element is scrolled
55317          * @param {Number} scrollLeft
55318          * @param {Number} scrollTop
55319          */
55320         "bodyscroll" : true,
55321         /**
55322          * @event columnresize
55323          * Fires when the user resizes a column
55324          * @param {Number} columnIndex
55325          * @param {Number} newSize
55326          */
55327         "columnresize" : true,
55328         /**
55329          * @event columnmove
55330          * Fires when the user moves a column
55331          * @param {Number} oldIndex
55332          * @param {Number} newIndex
55333          */
55334         "columnmove" : true,
55335         /**
55336          * @event startdrag
55337          * Fires when row(s) start being dragged
55338          * @param {Grid} this
55339          * @param {Roo.GridDD} dd The drag drop object
55340          * @param {event} e The raw browser event
55341          */
55342         "startdrag" : true,
55343         /**
55344          * @event enddrag
55345          * Fires when a drag operation is complete
55346          * @param {Grid} this
55347          * @param {Roo.GridDD} dd The drag drop object
55348          * @param {event} e The raw browser event
55349          */
55350         "enddrag" : true,
55351         /**
55352          * @event dragdrop
55353          * Fires when dragged row(s) are dropped on a valid DD target
55354          * @param {Grid} this
55355          * @param {Roo.GridDD} dd The drag drop object
55356          * @param {String} targetId The target drag drop object
55357          * @param {event} e The raw browser event
55358          */
55359         "dragdrop" : true,
55360         /**
55361          * @event dragover
55362          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55363          * @param {Grid} this
55364          * @param {Roo.GridDD} dd The drag drop object
55365          * @param {String} targetId The target drag drop object
55366          * @param {event} e The raw browser event
55367          */
55368         "dragover" : true,
55369         /**
55370          * @event dragenter
55371          *  Fires when the dragged row(s) first cross another DD target while being dragged
55372          * @param {Grid} this
55373          * @param {Roo.GridDD} dd The drag drop object
55374          * @param {String} targetId The target drag drop object
55375          * @param {event} e The raw browser event
55376          */
55377         "dragenter" : true,
55378         /**
55379          * @event dragout
55380          * Fires when the dragged row(s) leave another DD target while being dragged
55381          * @param {Grid} this
55382          * @param {Roo.GridDD} dd The drag drop object
55383          * @param {String} targetId The target drag drop object
55384          * @param {event} e The raw browser event
55385          */
55386         "dragout" : true,
55387         /**
55388          * @event rowclass
55389          * Fires when a row is rendered, so you can change add a style to it.
55390          * @param {GridView} gridview   The grid view
55391          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55392          */
55393         'rowclass' : true,
55394
55395         /**
55396          * @event render
55397          * Fires when the grid is rendered
55398          * @param {Grid} grid
55399          */
55400         'render' : true
55401     });
55402
55403     Roo.grid.Grid.superclass.constructor.call(this);
55404 };
55405 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55406     
55407     /**
55408      * @cfg {String} ddGroup - drag drop group.
55409      */
55410
55411     /**
55412      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55413      */
55414     minColumnWidth : 25,
55415
55416     /**
55417      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55418      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55419      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55420      */
55421     autoSizeColumns : false,
55422
55423     /**
55424      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55425      */
55426     autoSizeHeaders : true,
55427
55428     /**
55429      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55430      */
55431     monitorWindowResize : true,
55432
55433     /**
55434      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55435      * rows measured to get a columns size. Default is 0 (all rows).
55436      */
55437     maxRowsToMeasure : 0,
55438
55439     /**
55440      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55441      */
55442     trackMouseOver : true,
55443
55444     /**
55445     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55446     */
55447     
55448     /**
55449     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55450     */
55451     enableDragDrop : false,
55452     
55453     /**
55454     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55455     */
55456     enableColumnMove : true,
55457     
55458     /**
55459     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55460     */
55461     enableColumnHide : true,
55462     
55463     /**
55464     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55465     */
55466     enableRowHeightSync : false,
55467     
55468     /**
55469     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55470     */
55471     stripeRows : true,
55472     
55473     /**
55474     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55475     */
55476     autoHeight : false,
55477
55478     /**
55479      * @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.
55480      */
55481     autoExpandColumn : false,
55482
55483     /**
55484     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55485     * Default is 50.
55486     */
55487     autoExpandMin : 50,
55488
55489     /**
55490     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55491     */
55492     autoExpandMax : 1000,
55493
55494     /**
55495     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55496     */
55497     view : null,
55498
55499     /**
55500     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55501     */
55502     loadMask : false,
55503     /**
55504     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55505     */
55506     dropTarget: false,
55507     
55508    
55509     
55510     // private
55511     rendered : false,
55512
55513     /**
55514     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55515     * of a fixed width. Default is false.
55516     */
55517     /**
55518     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55519     */
55520     /**
55521      * Called once after all setup has been completed and the grid is ready to be rendered.
55522      * @return {Roo.grid.Grid} this
55523      */
55524     render : function()
55525     {
55526         var c = this.container;
55527         // try to detect autoHeight/width mode
55528         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55529             this.autoHeight = true;
55530         }
55531         var view = this.getView();
55532         view.init(this);
55533
55534         c.on("click", this.onClick, this);
55535         c.on("dblclick", this.onDblClick, this);
55536         c.on("contextmenu", this.onContextMenu, this);
55537         c.on("keydown", this.onKeyDown, this);
55538         if (Roo.isTouch) {
55539             c.on("touchstart", this.onTouchStart, this);
55540         }
55541
55542         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55543
55544         this.getSelectionModel().init(this);
55545
55546         view.render();
55547
55548         if(this.loadMask){
55549             this.loadMask = new Roo.LoadMask(this.container,
55550                     Roo.apply({store:this.dataSource}, this.loadMask));
55551         }
55552         
55553         
55554         if (this.toolbar && this.toolbar.xtype) {
55555             this.toolbar.container = this.getView().getHeaderPanel(true);
55556             this.toolbar = new Roo.Toolbar(this.toolbar);
55557         }
55558         if (this.footer && this.footer.xtype) {
55559             this.footer.dataSource = this.getDataSource();
55560             this.footer.container = this.getView().getFooterPanel(true);
55561             this.footer = Roo.factory(this.footer, Roo);
55562         }
55563         if (this.dropTarget && this.dropTarget.xtype) {
55564             delete this.dropTarget.xtype;
55565             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55566         }
55567         
55568         
55569         this.rendered = true;
55570         this.fireEvent('render', this);
55571         return this;
55572     },
55573
55574     /**
55575      * Reconfigures the grid to use a different Store and Column Model.
55576      * The View will be bound to the new objects and refreshed.
55577      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55578      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55579      */
55580     reconfigure : function(dataSource, colModel){
55581         if(this.loadMask){
55582             this.loadMask.destroy();
55583             this.loadMask = new Roo.LoadMask(this.container,
55584                     Roo.apply({store:dataSource}, this.loadMask));
55585         }
55586         this.view.bind(dataSource, colModel);
55587         this.dataSource = dataSource;
55588         this.colModel = colModel;
55589         this.view.refresh(true);
55590     },
55591     /**
55592      * addColumns
55593      * Add's a column, default at the end..
55594      
55595      * @param {int} position to add (default end)
55596      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55597      */
55598     addColumns : function(pos, ar)
55599     {
55600         
55601         for (var i =0;i< ar.length;i++) {
55602             var cfg = ar[i];
55603             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55604             this.cm.lookup[cfg.id] = cfg;
55605         }
55606         
55607         
55608         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55609             pos = this.cm.config.length; //this.cm.config.push(cfg);
55610         } 
55611         pos = Math.max(0,pos);
55612         ar.unshift(0);
55613         ar.unshift(pos);
55614         this.cm.config.splice.apply(this.cm.config, ar);
55615         
55616         
55617         
55618         this.view.generateRules(this.cm);
55619         this.view.refresh(true);
55620         
55621     },
55622     
55623     
55624     
55625     
55626     // private
55627     onKeyDown : function(e){
55628         this.fireEvent("keydown", e);
55629     },
55630
55631     /**
55632      * Destroy this grid.
55633      * @param {Boolean} removeEl True to remove the element
55634      */
55635     destroy : function(removeEl, keepListeners){
55636         if(this.loadMask){
55637             this.loadMask.destroy();
55638         }
55639         var c = this.container;
55640         c.removeAllListeners();
55641         this.view.destroy();
55642         this.colModel.purgeListeners();
55643         if(!keepListeners){
55644             this.purgeListeners();
55645         }
55646         c.update("");
55647         if(removeEl === true){
55648             c.remove();
55649         }
55650     },
55651
55652     // private
55653     processEvent : function(name, e){
55654         // does this fire select???
55655         //Roo.log('grid:processEvent '  + name);
55656         
55657         if (name != 'touchstart' ) {
55658             this.fireEvent(name, e);    
55659         }
55660         
55661         var t = e.getTarget();
55662         var v = this.view;
55663         var header = v.findHeaderIndex(t);
55664         if(header !== false){
55665             var ename = name == 'touchstart' ? 'click' : name;
55666              
55667             this.fireEvent("header" + ename, this, header, e);
55668         }else{
55669             var row = v.findRowIndex(t);
55670             var cell = v.findCellIndex(t);
55671             if (name == 'touchstart') {
55672                 // first touch is always a click.
55673                 // hopefull this happens after selection is updated.?
55674                 name = false;
55675                 
55676                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55677                     var cs = this.selModel.getSelectedCell();
55678                     if (row == cs[0] && cell == cs[1]){
55679                         name = 'dblclick';
55680                     }
55681                 }
55682                 if (typeof(this.selModel.getSelections) != 'undefined') {
55683                     var cs = this.selModel.getSelections();
55684                     var ds = this.dataSource;
55685                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55686                         name = 'dblclick';
55687                     }
55688                 }
55689                 if (!name) {
55690                     return;
55691                 }
55692             }
55693             
55694             
55695             if(row !== false){
55696                 this.fireEvent("row" + name, this, row, e);
55697                 if(cell !== false){
55698                     this.fireEvent("cell" + name, this, row, cell, e);
55699                 }
55700             }
55701         }
55702     },
55703
55704     // private
55705     onClick : function(e){
55706         this.processEvent("click", e);
55707     },
55708    // private
55709     onTouchStart : function(e){
55710         this.processEvent("touchstart", e);
55711     },
55712
55713     // private
55714     onContextMenu : function(e, t){
55715         this.processEvent("contextmenu", e);
55716     },
55717
55718     // private
55719     onDblClick : function(e){
55720         this.processEvent("dblclick", e);
55721     },
55722
55723     // private
55724     walkCells : function(row, col, step, fn, scope){
55725         var cm = this.colModel, clen = cm.getColumnCount();
55726         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55727         if(step < 0){
55728             if(col < 0){
55729                 row--;
55730                 first = false;
55731             }
55732             while(row >= 0){
55733                 if(!first){
55734                     col = clen-1;
55735                 }
55736                 first = false;
55737                 while(col >= 0){
55738                     if(fn.call(scope || this, row, col, cm) === true){
55739                         return [row, col];
55740                     }
55741                     col--;
55742                 }
55743                 row--;
55744             }
55745         } else {
55746             if(col >= clen){
55747                 row++;
55748                 first = false;
55749             }
55750             while(row < rlen){
55751                 if(!first){
55752                     col = 0;
55753                 }
55754                 first = false;
55755                 while(col < clen){
55756                     if(fn.call(scope || this, row, col, cm) === true){
55757                         return [row, col];
55758                     }
55759                     col++;
55760                 }
55761                 row++;
55762             }
55763         }
55764         return null;
55765     },
55766
55767     // private
55768     getSelections : function(){
55769         return this.selModel.getSelections();
55770     },
55771
55772     /**
55773      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55774      * but if manual update is required this method will initiate it.
55775      */
55776     autoSize : function(){
55777         if(this.rendered){
55778             this.view.layout();
55779             if(this.view.adjustForScroll){
55780                 this.view.adjustForScroll();
55781             }
55782         }
55783     },
55784
55785     /**
55786      * Returns the grid's underlying element.
55787      * @return {Element} The element
55788      */
55789     getGridEl : function(){
55790         return this.container;
55791     },
55792
55793     // private for compatibility, overridden by editor grid
55794     stopEditing : function(){},
55795
55796     /**
55797      * Returns the grid's SelectionModel.
55798      * @return {SelectionModel}
55799      */
55800     getSelectionModel : function(){
55801         if(!this.selModel){
55802             this.selModel = new Roo.grid.RowSelectionModel();
55803         }
55804         return this.selModel;
55805     },
55806
55807     /**
55808      * Returns the grid's DataSource.
55809      * @return {DataSource}
55810      */
55811     getDataSource : function(){
55812         return this.dataSource;
55813     },
55814
55815     /**
55816      * Returns the grid's ColumnModel.
55817      * @return {ColumnModel}
55818      */
55819     getColumnModel : function(){
55820         return this.colModel;
55821     },
55822
55823     /**
55824      * Returns the grid's GridView object.
55825      * @return {GridView}
55826      */
55827     getView : function(){
55828         if(!this.view){
55829             this.view = new Roo.grid.GridView(this.viewConfig);
55830         }
55831         return this.view;
55832     },
55833     /**
55834      * Called to get grid's drag proxy text, by default returns this.ddText.
55835      * @return {String}
55836      */
55837     getDragDropText : function(){
55838         var count = this.selModel.getCount();
55839         return String.format(this.ddText, count, count == 1 ? '' : 's');
55840     }
55841 });
55842 /**
55843  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55844  * %0 is replaced with the number of selected rows.
55845  * @type String
55846  */
55847 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55848  * Based on:
55849  * Ext JS Library 1.1.1
55850  * Copyright(c) 2006-2007, Ext JS, LLC.
55851  *
55852  * Originally Released Under LGPL - original licence link has changed is not relivant.
55853  *
55854  * Fork - LGPL
55855  * <script type="text/javascript">
55856  */
55857  
55858 Roo.grid.AbstractGridView = function(){
55859         this.grid = null;
55860         
55861         this.events = {
55862             "beforerowremoved" : true,
55863             "beforerowsinserted" : true,
55864             "beforerefresh" : true,
55865             "rowremoved" : true,
55866             "rowsinserted" : true,
55867             "rowupdated" : true,
55868             "refresh" : true
55869         };
55870     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55871 };
55872
55873 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55874     rowClass : "x-grid-row",
55875     cellClass : "x-grid-cell",
55876     tdClass : "x-grid-td",
55877     hdClass : "x-grid-hd",
55878     splitClass : "x-grid-hd-split",
55879     
55880     init: function(grid){
55881         this.grid = grid;
55882                 var cid = this.grid.getGridEl().id;
55883         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55884         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55885         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55886         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55887         },
55888         
55889     getColumnRenderers : function(){
55890         var renderers = [];
55891         var cm = this.grid.colModel;
55892         var colCount = cm.getColumnCount();
55893         for(var i = 0; i < colCount; i++){
55894             renderers[i] = cm.getRenderer(i);
55895         }
55896         return renderers;
55897     },
55898     
55899     getColumnIds : function(){
55900         var ids = [];
55901         var cm = this.grid.colModel;
55902         var colCount = cm.getColumnCount();
55903         for(var i = 0; i < colCount; i++){
55904             ids[i] = cm.getColumnId(i);
55905         }
55906         return ids;
55907     },
55908     
55909     getDataIndexes : function(){
55910         if(!this.indexMap){
55911             this.indexMap = this.buildIndexMap();
55912         }
55913         return this.indexMap.colToData;
55914     },
55915     
55916     getColumnIndexByDataIndex : function(dataIndex){
55917         if(!this.indexMap){
55918             this.indexMap = this.buildIndexMap();
55919         }
55920         return this.indexMap.dataToCol[dataIndex];
55921     },
55922     
55923     /**
55924      * Set a css style for a column dynamically. 
55925      * @param {Number} colIndex The index of the column
55926      * @param {String} name The css property name
55927      * @param {String} value The css value
55928      */
55929     setCSSStyle : function(colIndex, name, value){
55930         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55931         Roo.util.CSS.updateRule(selector, name, value);
55932     },
55933     
55934     generateRules : function(cm){
55935         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55936         Roo.util.CSS.removeStyleSheet(rulesId);
55937         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55938             var cid = cm.getColumnId(i);
55939             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55940                          this.tdSelector, cid, " {\n}\n",
55941                          this.hdSelector, cid, " {\n}\n",
55942                          this.splitSelector, cid, " {\n}\n");
55943         }
55944         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55945     }
55946 });/*
55947  * Based on:
55948  * Ext JS Library 1.1.1
55949  * Copyright(c) 2006-2007, Ext JS, LLC.
55950  *
55951  * Originally Released Under LGPL - original licence link has changed is not relivant.
55952  *
55953  * Fork - LGPL
55954  * <script type="text/javascript">
55955  */
55956
55957 // private
55958 // This is a support class used internally by the Grid components
55959 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55960     this.grid = grid;
55961     this.view = grid.getView();
55962     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55963     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55964     if(hd2){
55965         this.setHandleElId(Roo.id(hd));
55966         this.setOuterHandleElId(Roo.id(hd2));
55967     }
55968     this.scroll = false;
55969 };
55970 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55971     maxDragWidth: 120,
55972     getDragData : function(e){
55973         var t = Roo.lib.Event.getTarget(e);
55974         var h = this.view.findHeaderCell(t);
55975         if(h){
55976             return {ddel: h.firstChild, header:h};
55977         }
55978         return false;
55979     },
55980
55981     onInitDrag : function(e){
55982         this.view.headersDisabled = true;
55983         var clone = this.dragData.ddel.cloneNode(true);
55984         clone.id = Roo.id();
55985         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55986         this.proxy.update(clone);
55987         return true;
55988     },
55989
55990     afterValidDrop : function(){
55991         var v = this.view;
55992         setTimeout(function(){
55993             v.headersDisabled = false;
55994         }, 50);
55995     },
55996
55997     afterInvalidDrop : function(){
55998         var v = this.view;
55999         setTimeout(function(){
56000             v.headersDisabled = false;
56001         }, 50);
56002     }
56003 });
56004 /*
56005  * Based on:
56006  * Ext JS Library 1.1.1
56007  * Copyright(c) 2006-2007, Ext JS, LLC.
56008  *
56009  * Originally Released Under LGPL - original licence link has changed is not relivant.
56010  *
56011  * Fork - LGPL
56012  * <script type="text/javascript">
56013  */
56014 // private
56015 // This is a support class used internally by the Grid components
56016 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56017     this.grid = grid;
56018     this.view = grid.getView();
56019     // split the proxies so they don't interfere with mouse events
56020     this.proxyTop = Roo.DomHelper.append(document.body, {
56021         cls:"col-move-top", html:"&#160;"
56022     }, true);
56023     this.proxyBottom = Roo.DomHelper.append(document.body, {
56024         cls:"col-move-bottom", html:"&#160;"
56025     }, true);
56026     this.proxyTop.hide = this.proxyBottom.hide = function(){
56027         this.setLeftTop(-100,-100);
56028         this.setStyle("visibility", "hidden");
56029     };
56030     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56031     // temporarily disabled
56032     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56033     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56034 };
56035 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56036     proxyOffsets : [-4, -9],
56037     fly: Roo.Element.fly,
56038
56039     getTargetFromEvent : function(e){
56040         var t = Roo.lib.Event.getTarget(e);
56041         var cindex = this.view.findCellIndex(t);
56042         if(cindex !== false){
56043             return this.view.getHeaderCell(cindex);
56044         }
56045         return null;
56046     },
56047
56048     nextVisible : function(h){
56049         var v = this.view, cm = this.grid.colModel;
56050         h = h.nextSibling;
56051         while(h){
56052             if(!cm.isHidden(v.getCellIndex(h))){
56053                 return h;
56054             }
56055             h = h.nextSibling;
56056         }
56057         return null;
56058     },
56059
56060     prevVisible : function(h){
56061         var v = this.view, cm = this.grid.colModel;
56062         h = h.prevSibling;
56063         while(h){
56064             if(!cm.isHidden(v.getCellIndex(h))){
56065                 return h;
56066             }
56067             h = h.prevSibling;
56068         }
56069         return null;
56070     },
56071
56072     positionIndicator : function(h, n, e){
56073         var x = Roo.lib.Event.getPageX(e);
56074         var r = Roo.lib.Dom.getRegion(n.firstChild);
56075         var px, pt, py = r.top + this.proxyOffsets[1];
56076         if((r.right - x) <= (r.right-r.left)/2){
56077             px = r.right+this.view.borderWidth;
56078             pt = "after";
56079         }else{
56080             px = r.left;
56081             pt = "before";
56082         }
56083         var oldIndex = this.view.getCellIndex(h);
56084         var newIndex = this.view.getCellIndex(n);
56085
56086         if(this.grid.colModel.isFixed(newIndex)){
56087             return false;
56088         }
56089
56090         var locked = this.grid.colModel.isLocked(newIndex);
56091
56092         if(pt == "after"){
56093             newIndex++;
56094         }
56095         if(oldIndex < newIndex){
56096             newIndex--;
56097         }
56098         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56099             return false;
56100         }
56101         px +=  this.proxyOffsets[0];
56102         this.proxyTop.setLeftTop(px, py);
56103         this.proxyTop.show();
56104         if(!this.bottomOffset){
56105             this.bottomOffset = this.view.mainHd.getHeight();
56106         }
56107         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56108         this.proxyBottom.show();
56109         return pt;
56110     },
56111
56112     onNodeEnter : function(n, dd, e, data){
56113         if(data.header != n){
56114             this.positionIndicator(data.header, n, e);
56115         }
56116     },
56117
56118     onNodeOver : function(n, dd, e, data){
56119         var result = false;
56120         if(data.header != n){
56121             result = this.positionIndicator(data.header, n, e);
56122         }
56123         if(!result){
56124             this.proxyTop.hide();
56125             this.proxyBottom.hide();
56126         }
56127         return result ? this.dropAllowed : this.dropNotAllowed;
56128     },
56129
56130     onNodeOut : function(n, dd, e, data){
56131         this.proxyTop.hide();
56132         this.proxyBottom.hide();
56133     },
56134
56135     onNodeDrop : function(n, dd, e, data){
56136         var h = data.header;
56137         if(h != n){
56138             var cm = this.grid.colModel;
56139             var x = Roo.lib.Event.getPageX(e);
56140             var r = Roo.lib.Dom.getRegion(n.firstChild);
56141             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56142             var oldIndex = this.view.getCellIndex(h);
56143             var newIndex = this.view.getCellIndex(n);
56144             var locked = cm.isLocked(newIndex);
56145             if(pt == "after"){
56146                 newIndex++;
56147             }
56148             if(oldIndex < newIndex){
56149                 newIndex--;
56150             }
56151             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56152                 return false;
56153             }
56154             cm.setLocked(oldIndex, locked, true);
56155             cm.moveColumn(oldIndex, newIndex);
56156             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56157             return true;
56158         }
56159         return false;
56160     }
56161 });
56162 /*
56163  * Based on:
56164  * Ext JS Library 1.1.1
56165  * Copyright(c) 2006-2007, Ext JS, LLC.
56166  *
56167  * Originally Released Under LGPL - original licence link has changed is not relivant.
56168  *
56169  * Fork - LGPL
56170  * <script type="text/javascript">
56171  */
56172   
56173 /**
56174  * @class Roo.grid.GridView
56175  * @extends Roo.util.Observable
56176  *
56177  * @constructor
56178  * @param {Object} config
56179  */
56180 Roo.grid.GridView = function(config){
56181     Roo.grid.GridView.superclass.constructor.call(this);
56182     this.el = null;
56183
56184     Roo.apply(this, config);
56185 };
56186
56187 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56188
56189     unselectable :  'unselectable="on"',
56190     unselectableCls :  'x-unselectable',
56191     
56192     
56193     rowClass : "x-grid-row",
56194
56195     cellClass : "x-grid-col",
56196
56197     tdClass : "x-grid-td",
56198
56199     hdClass : "x-grid-hd",
56200
56201     splitClass : "x-grid-split",
56202
56203     sortClasses : ["sort-asc", "sort-desc"],
56204
56205     enableMoveAnim : false,
56206
56207     hlColor: "C3DAF9",
56208
56209     dh : Roo.DomHelper,
56210
56211     fly : Roo.Element.fly,
56212
56213     css : Roo.util.CSS,
56214
56215     borderWidth: 1,
56216
56217     splitOffset: 3,
56218
56219     scrollIncrement : 22,
56220
56221     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56222
56223     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56224
56225     bind : function(ds, cm){
56226         if(this.ds){
56227             this.ds.un("load", this.onLoad, this);
56228             this.ds.un("datachanged", this.onDataChange, this);
56229             this.ds.un("add", this.onAdd, this);
56230             this.ds.un("remove", this.onRemove, this);
56231             this.ds.un("update", this.onUpdate, this);
56232             this.ds.un("clear", this.onClear, this);
56233         }
56234         if(ds){
56235             ds.on("load", this.onLoad, this);
56236             ds.on("datachanged", this.onDataChange, this);
56237             ds.on("add", this.onAdd, this);
56238             ds.on("remove", this.onRemove, this);
56239             ds.on("update", this.onUpdate, this);
56240             ds.on("clear", this.onClear, this);
56241         }
56242         this.ds = ds;
56243
56244         if(this.cm){
56245             this.cm.un("widthchange", this.onColWidthChange, this);
56246             this.cm.un("headerchange", this.onHeaderChange, this);
56247             this.cm.un("hiddenchange", this.onHiddenChange, this);
56248             this.cm.un("columnmoved", this.onColumnMove, this);
56249             this.cm.un("columnlockchange", this.onColumnLock, this);
56250         }
56251         if(cm){
56252             this.generateRules(cm);
56253             cm.on("widthchange", this.onColWidthChange, this);
56254             cm.on("headerchange", this.onHeaderChange, this);
56255             cm.on("hiddenchange", this.onHiddenChange, this);
56256             cm.on("columnmoved", this.onColumnMove, this);
56257             cm.on("columnlockchange", this.onColumnLock, this);
56258         }
56259         this.cm = cm;
56260     },
56261
56262     init: function(grid){
56263         Roo.grid.GridView.superclass.init.call(this, grid);
56264
56265         this.bind(grid.dataSource, grid.colModel);
56266
56267         grid.on("headerclick", this.handleHeaderClick, this);
56268
56269         if(grid.trackMouseOver){
56270             grid.on("mouseover", this.onRowOver, this);
56271             grid.on("mouseout", this.onRowOut, this);
56272         }
56273         grid.cancelTextSelection = function(){};
56274         this.gridId = grid.id;
56275
56276         var tpls = this.templates || {};
56277
56278         if(!tpls.master){
56279             tpls.master = new Roo.Template(
56280                '<div class="x-grid" hidefocus="true">',
56281                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56282                   '<div class="x-grid-topbar"></div>',
56283                   '<div class="x-grid-scroller"><div></div></div>',
56284                   '<div class="x-grid-locked">',
56285                       '<div class="x-grid-header">{lockedHeader}</div>',
56286                       '<div class="x-grid-body">{lockedBody}</div>',
56287                   "</div>",
56288                   '<div class="x-grid-viewport">',
56289                       '<div class="x-grid-header">{header}</div>',
56290                       '<div class="x-grid-body">{body}</div>',
56291                   "</div>",
56292                   '<div class="x-grid-bottombar"></div>',
56293                  
56294                   '<div class="x-grid-resize-proxy">&#160;</div>',
56295                "</div>"
56296             );
56297             tpls.master.disableformats = true;
56298         }
56299
56300         if(!tpls.header){
56301             tpls.header = new Roo.Template(
56302                '<table border="0" cellspacing="0" cellpadding="0">',
56303                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56304                "</table>{splits}"
56305             );
56306             tpls.header.disableformats = true;
56307         }
56308         tpls.header.compile();
56309
56310         if(!tpls.hcell){
56311             tpls.hcell = new Roo.Template(
56312                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56313                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56314                 "</div></td>"
56315              );
56316              tpls.hcell.disableFormats = true;
56317         }
56318         tpls.hcell.compile();
56319
56320         if(!tpls.hsplit){
56321             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56322                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56323             tpls.hsplit.disableFormats = true;
56324         }
56325         tpls.hsplit.compile();
56326
56327         if(!tpls.body){
56328             tpls.body = new Roo.Template(
56329                '<table border="0" cellspacing="0" cellpadding="0">',
56330                "<tbody>{rows}</tbody>",
56331                "</table>"
56332             );
56333             tpls.body.disableFormats = true;
56334         }
56335         tpls.body.compile();
56336
56337         if(!tpls.row){
56338             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56339             tpls.row.disableFormats = true;
56340         }
56341         tpls.row.compile();
56342
56343         if(!tpls.cell){
56344             tpls.cell = new Roo.Template(
56345                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56346                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56347                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56348                 "</td>"
56349             );
56350             tpls.cell.disableFormats = true;
56351         }
56352         tpls.cell.compile();
56353
56354         this.templates = tpls;
56355     },
56356
56357     // remap these for backwards compat
56358     onColWidthChange : function(){
56359         this.updateColumns.apply(this, arguments);
56360     },
56361     onHeaderChange : function(){
56362         this.updateHeaders.apply(this, arguments);
56363     }, 
56364     onHiddenChange : function(){
56365         this.handleHiddenChange.apply(this, arguments);
56366     },
56367     onColumnMove : function(){
56368         this.handleColumnMove.apply(this, arguments);
56369     },
56370     onColumnLock : function(){
56371         this.handleLockChange.apply(this, arguments);
56372     },
56373
56374     onDataChange : function(){
56375         this.refresh();
56376         this.updateHeaderSortState();
56377     },
56378
56379     onClear : function(){
56380         this.refresh();
56381     },
56382
56383     onUpdate : function(ds, record){
56384         this.refreshRow(record);
56385     },
56386
56387     refreshRow : function(record){
56388         var ds = this.ds, index;
56389         if(typeof record == 'number'){
56390             index = record;
56391             record = ds.getAt(index);
56392         }else{
56393             index = ds.indexOf(record);
56394         }
56395         this.insertRows(ds, index, index, true);
56396         this.onRemove(ds, record, index+1, true);
56397         this.syncRowHeights(index, index);
56398         this.layout();
56399         this.fireEvent("rowupdated", this, index, record);
56400     },
56401
56402     onAdd : function(ds, records, index){
56403         this.insertRows(ds, index, index + (records.length-1));
56404     },
56405
56406     onRemove : function(ds, record, index, isUpdate){
56407         if(isUpdate !== true){
56408             this.fireEvent("beforerowremoved", this, index, record);
56409         }
56410         var bt = this.getBodyTable(), lt = this.getLockedTable();
56411         if(bt.rows[index]){
56412             bt.firstChild.removeChild(bt.rows[index]);
56413         }
56414         if(lt.rows[index]){
56415             lt.firstChild.removeChild(lt.rows[index]);
56416         }
56417         if(isUpdate !== true){
56418             this.stripeRows(index);
56419             this.syncRowHeights(index, index);
56420             this.layout();
56421             this.fireEvent("rowremoved", this, index, record);
56422         }
56423     },
56424
56425     onLoad : function(){
56426         this.scrollToTop();
56427     },
56428
56429     /**
56430      * Scrolls the grid to the top
56431      */
56432     scrollToTop : function(){
56433         if(this.scroller){
56434             this.scroller.dom.scrollTop = 0;
56435             this.syncScroll();
56436         }
56437     },
56438
56439     /**
56440      * Gets a panel in the header of the grid that can be used for toolbars etc.
56441      * After modifying the contents of this panel a call to grid.autoSize() may be
56442      * required to register any changes in size.
56443      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56444      * @return Roo.Element
56445      */
56446     getHeaderPanel : function(doShow){
56447         if(doShow){
56448             this.headerPanel.show();
56449         }
56450         return this.headerPanel;
56451     },
56452
56453     /**
56454      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56455      * After modifying the contents of this panel a call to grid.autoSize() may be
56456      * required to register any changes in size.
56457      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56458      * @return Roo.Element
56459      */
56460     getFooterPanel : function(doShow){
56461         if(doShow){
56462             this.footerPanel.show();
56463         }
56464         return this.footerPanel;
56465     },
56466
56467     initElements : function(){
56468         var E = Roo.Element;
56469         var el = this.grid.getGridEl().dom.firstChild;
56470         var cs = el.childNodes;
56471
56472         this.el = new E(el);
56473         
56474          this.focusEl = new E(el.firstChild);
56475         this.focusEl.swallowEvent("click", true);
56476         
56477         this.headerPanel = new E(cs[1]);
56478         this.headerPanel.enableDisplayMode("block");
56479
56480         this.scroller = new E(cs[2]);
56481         this.scrollSizer = new E(this.scroller.dom.firstChild);
56482
56483         this.lockedWrap = new E(cs[3]);
56484         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56485         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56486
56487         this.mainWrap = new E(cs[4]);
56488         this.mainHd = new E(this.mainWrap.dom.firstChild);
56489         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56490
56491         this.footerPanel = new E(cs[5]);
56492         this.footerPanel.enableDisplayMode("block");
56493
56494         this.resizeProxy = new E(cs[6]);
56495
56496         this.headerSelector = String.format(
56497            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56498            this.lockedHd.id, this.mainHd.id
56499         );
56500
56501         this.splitterSelector = String.format(
56502            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56503            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56504         );
56505     },
56506     idToCssName : function(s)
56507     {
56508         return s.replace(/[^a-z0-9]+/ig, '-');
56509     },
56510
56511     getHeaderCell : function(index){
56512         return Roo.DomQuery.select(this.headerSelector)[index];
56513     },
56514
56515     getHeaderCellMeasure : function(index){
56516         return this.getHeaderCell(index).firstChild;
56517     },
56518
56519     getHeaderCellText : function(index){
56520         return this.getHeaderCell(index).firstChild.firstChild;
56521     },
56522
56523     getLockedTable : function(){
56524         return this.lockedBody.dom.firstChild;
56525     },
56526
56527     getBodyTable : function(){
56528         return this.mainBody.dom.firstChild;
56529     },
56530
56531     getLockedRow : function(index){
56532         return this.getLockedTable().rows[index];
56533     },
56534
56535     getRow : function(index){
56536         return this.getBodyTable().rows[index];
56537     },
56538
56539     getRowComposite : function(index){
56540         if(!this.rowEl){
56541             this.rowEl = new Roo.CompositeElementLite();
56542         }
56543         var els = [], lrow, mrow;
56544         if(lrow = this.getLockedRow(index)){
56545             els.push(lrow);
56546         }
56547         if(mrow = this.getRow(index)){
56548             els.push(mrow);
56549         }
56550         this.rowEl.elements = els;
56551         return this.rowEl;
56552     },
56553     /**
56554      * Gets the 'td' of the cell
56555      * 
56556      * @param {Integer} rowIndex row to select
56557      * @param {Integer} colIndex column to select
56558      * 
56559      * @return {Object} 
56560      */
56561     getCell : function(rowIndex, colIndex){
56562         var locked = this.cm.getLockedCount();
56563         var source;
56564         if(colIndex < locked){
56565             source = this.lockedBody.dom.firstChild;
56566         }else{
56567             source = this.mainBody.dom.firstChild;
56568             colIndex -= locked;
56569         }
56570         return source.rows[rowIndex].childNodes[colIndex];
56571     },
56572
56573     getCellText : function(rowIndex, colIndex){
56574         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56575     },
56576
56577     getCellBox : function(cell){
56578         var b = this.fly(cell).getBox();
56579         if(Roo.isOpera){ // opera fails to report the Y
56580             b.y = cell.offsetTop + this.mainBody.getY();
56581         }
56582         return b;
56583     },
56584
56585     getCellIndex : function(cell){
56586         var id = String(cell.className).match(this.cellRE);
56587         if(id){
56588             return parseInt(id[1], 10);
56589         }
56590         return 0;
56591     },
56592
56593     findHeaderIndex : function(n){
56594         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56595         return r ? this.getCellIndex(r) : false;
56596     },
56597
56598     findHeaderCell : function(n){
56599         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56600         return r ? r : false;
56601     },
56602
56603     findRowIndex : function(n){
56604         if(!n){
56605             return false;
56606         }
56607         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56608         return r ? r.rowIndex : false;
56609     },
56610
56611     findCellIndex : function(node){
56612         var stop = this.el.dom;
56613         while(node && node != stop){
56614             if(this.findRE.test(node.className)){
56615                 return this.getCellIndex(node);
56616             }
56617             node = node.parentNode;
56618         }
56619         return false;
56620     },
56621
56622     getColumnId : function(index){
56623         return this.cm.getColumnId(index);
56624     },
56625
56626     getSplitters : function()
56627     {
56628         if(this.splitterSelector){
56629            return Roo.DomQuery.select(this.splitterSelector);
56630         }else{
56631             return null;
56632       }
56633     },
56634
56635     getSplitter : function(index){
56636         return this.getSplitters()[index];
56637     },
56638
56639     onRowOver : function(e, t){
56640         var row;
56641         if((row = this.findRowIndex(t)) !== false){
56642             this.getRowComposite(row).addClass("x-grid-row-over");
56643         }
56644     },
56645
56646     onRowOut : function(e, t){
56647         var row;
56648         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56649             this.getRowComposite(row).removeClass("x-grid-row-over");
56650         }
56651     },
56652
56653     renderHeaders : function(){
56654         var cm = this.cm;
56655         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56656         var cb = [], lb = [], sb = [], lsb = [], p = {};
56657         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56658             p.cellId = "x-grid-hd-0-" + i;
56659             p.splitId = "x-grid-csplit-0-" + i;
56660             p.id = cm.getColumnId(i);
56661             p.value = cm.getColumnHeader(i) || "";
56662             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56663             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56664             if(!cm.isLocked(i)){
56665                 cb[cb.length] = ct.apply(p);
56666                 sb[sb.length] = st.apply(p);
56667             }else{
56668                 lb[lb.length] = ct.apply(p);
56669                 lsb[lsb.length] = st.apply(p);
56670             }
56671         }
56672         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56673                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56674     },
56675
56676     updateHeaders : function(){
56677         var html = this.renderHeaders();
56678         this.lockedHd.update(html[0]);
56679         this.mainHd.update(html[1]);
56680     },
56681
56682     /**
56683      * Focuses the specified row.
56684      * @param {Number} row The row index
56685      */
56686     focusRow : function(row)
56687     {
56688         //Roo.log('GridView.focusRow');
56689         var x = this.scroller.dom.scrollLeft;
56690         this.focusCell(row, 0, false);
56691         this.scroller.dom.scrollLeft = x;
56692     },
56693
56694     /**
56695      * Focuses the specified cell.
56696      * @param {Number} row The row index
56697      * @param {Number} col The column index
56698      * @param {Boolean} hscroll false to disable horizontal scrolling
56699      */
56700     focusCell : function(row, col, hscroll)
56701     {
56702         //Roo.log('GridView.focusCell');
56703         var el = this.ensureVisible(row, col, hscroll);
56704         this.focusEl.alignTo(el, "tl-tl");
56705         if(Roo.isGecko){
56706             this.focusEl.focus();
56707         }else{
56708             this.focusEl.focus.defer(1, this.focusEl);
56709         }
56710     },
56711
56712     /**
56713      * Scrolls the specified cell into view
56714      * @param {Number} row The row index
56715      * @param {Number} col The column index
56716      * @param {Boolean} hscroll false to disable horizontal scrolling
56717      */
56718     ensureVisible : function(row, col, hscroll)
56719     {
56720         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56721         //return null; //disable for testing.
56722         if(typeof row != "number"){
56723             row = row.rowIndex;
56724         }
56725         if(row < 0 && row >= this.ds.getCount()){
56726             return  null;
56727         }
56728         col = (col !== undefined ? col : 0);
56729         var cm = this.grid.colModel;
56730         while(cm.isHidden(col)){
56731             col++;
56732         }
56733
56734         var el = this.getCell(row, col);
56735         if(!el){
56736             return null;
56737         }
56738         var c = this.scroller.dom;
56739
56740         var ctop = parseInt(el.offsetTop, 10);
56741         var cleft = parseInt(el.offsetLeft, 10);
56742         var cbot = ctop + el.offsetHeight;
56743         var cright = cleft + el.offsetWidth;
56744         
56745         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56746         var stop = parseInt(c.scrollTop, 10);
56747         var sleft = parseInt(c.scrollLeft, 10);
56748         var sbot = stop + ch;
56749         var sright = sleft + c.clientWidth;
56750         /*
56751         Roo.log('GridView.ensureVisible:' +
56752                 ' ctop:' + ctop +
56753                 ' c.clientHeight:' + c.clientHeight +
56754                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56755                 ' stop:' + stop +
56756                 ' cbot:' + cbot +
56757                 ' sbot:' + sbot +
56758                 ' ch:' + ch  
56759                 );
56760         */
56761         if(ctop < stop){
56762              c.scrollTop = ctop;
56763             //Roo.log("set scrolltop to ctop DISABLE?");
56764         }else if(cbot > sbot){
56765             //Roo.log("set scrolltop to cbot-ch");
56766             c.scrollTop = cbot-ch;
56767         }
56768         
56769         if(hscroll !== false){
56770             if(cleft < sleft){
56771                 c.scrollLeft = cleft;
56772             }else if(cright > sright){
56773                 c.scrollLeft = cright-c.clientWidth;
56774             }
56775         }
56776          
56777         return el;
56778     },
56779
56780     updateColumns : function(){
56781         this.grid.stopEditing();
56782         var cm = this.grid.colModel, colIds = this.getColumnIds();
56783         //var totalWidth = cm.getTotalWidth();
56784         var pos = 0;
56785         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56786             //if(cm.isHidden(i)) continue;
56787             var w = cm.getColumnWidth(i);
56788             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56789             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56790         }
56791         this.updateSplitters();
56792     },
56793
56794     generateRules : function(cm){
56795         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56796         Roo.util.CSS.removeStyleSheet(rulesId);
56797         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56798             var cid = cm.getColumnId(i);
56799             var align = '';
56800             if(cm.config[i].align){
56801                 align = 'text-align:'+cm.config[i].align+';';
56802             }
56803             var hidden = '';
56804             if(cm.isHidden(i)){
56805                 hidden = 'display:none;';
56806             }
56807             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56808             ruleBuf.push(
56809                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56810                     this.hdSelector, cid, " {\n", align, width, "}\n",
56811                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56812                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56813         }
56814         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56815     },
56816
56817     updateSplitters : function(){
56818         var cm = this.cm, s = this.getSplitters();
56819         if(s){ // splitters not created yet
56820             var pos = 0, locked = true;
56821             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56822                 if(cm.isHidden(i)) {
56823                     continue;
56824                 }
56825                 var w = cm.getColumnWidth(i); // make sure it's a number
56826                 if(!cm.isLocked(i) && locked){
56827                     pos = 0;
56828                     locked = false;
56829                 }
56830                 pos += w;
56831                 s[i].style.left = (pos-this.splitOffset) + "px";
56832             }
56833         }
56834     },
56835
56836     handleHiddenChange : function(colModel, colIndex, hidden){
56837         if(hidden){
56838             this.hideColumn(colIndex);
56839         }else{
56840             this.unhideColumn(colIndex);
56841         }
56842     },
56843
56844     hideColumn : function(colIndex){
56845         var cid = this.getColumnId(colIndex);
56846         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56847         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56848         if(Roo.isSafari){
56849             this.updateHeaders();
56850         }
56851         this.updateSplitters();
56852         this.layout();
56853     },
56854
56855     unhideColumn : function(colIndex){
56856         var cid = this.getColumnId(colIndex);
56857         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56858         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56859
56860         if(Roo.isSafari){
56861             this.updateHeaders();
56862         }
56863         this.updateSplitters();
56864         this.layout();
56865     },
56866
56867     insertRows : function(dm, firstRow, lastRow, isUpdate){
56868         if(firstRow == 0 && lastRow == dm.getCount()-1){
56869             this.refresh();
56870         }else{
56871             if(!isUpdate){
56872                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56873             }
56874             var s = this.getScrollState();
56875             var markup = this.renderRows(firstRow, lastRow);
56876             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56877             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56878             this.restoreScroll(s);
56879             if(!isUpdate){
56880                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56881                 this.syncRowHeights(firstRow, lastRow);
56882                 this.stripeRows(firstRow);
56883                 this.layout();
56884             }
56885         }
56886     },
56887
56888     bufferRows : function(markup, target, index){
56889         var before = null, trows = target.rows, tbody = target.tBodies[0];
56890         if(index < trows.length){
56891             before = trows[index];
56892         }
56893         var b = document.createElement("div");
56894         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56895         var rows = b.firstChild.rows;
56896         for(var i = 0, len = rows.length; i < len; i++){
56897             if(before){
56898                 tbody.insertBefore(rows[0], before);
56899             }else{
56900                 tbody.appendChild(rows[0]);
56901             }
56902         }
56903         b.innerHTML = "";
56904         b = null;
56905     },
56906
56907     deleteRows : function(dm, firstRow, lastRow){
56908         if(dm.getRowCount()<1){
56909             this.fireEvent("beforerefresh", this);
56910             this.mainBody.update("");
56911             this.lockedBody.update("");
56912             this.fireEvent("refresh", this);
56913         }else{
56914             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56915             var bt = this.getBodyTable();
56916             var tbody = bt.firstChild;
56917             var rows = bt.rows;
56918             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56919                 tbody.removeChild(rows[firstRow]);
56920             }
56921             this.stripeRows(firstRow);
56922             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56923         }
56924     },
56925
56926     updateRows : function(dataSource, firstRow, lastRow){
56927         var s = this.getScrollState();
56928         this.refresh();
56929         this.restoreScroll(s);
56930     },
56931
56932     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56933         if(!noRefresh){
56934            this.refresh();
56935         }
56936         this.updateHeaderSortState();
56937     },
56938
56939     getScrollState : function(){
56940         
56941         var sb = this.scroller.dom;
56942         return {left: sb.scrollLeft, top: sb.scrollTop};
56943     },
56944
56945     stripeRows : function(startRow){
56946         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56947             return;
56948         }
56949         startRow = startRow || 0;
56950         var rows = this.getBodyTable().rows;
56951         var lrows = this.getLockedTable().rows;
56952         var cls = ' x-grid-row-alt ';
56953         for(var i = startRow, len = rows.length; i < len; i++){
56954             var row = rows[i], lrow = lrows[i];
56955             var isAlt = ((i+1) % 2 == 0);
56956             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56957             if(isAlt == hasAlt){
56958                 continue;
56959             }
56960             if(isAlt){
56961                 row.className += " x-grid-row-alt";
56962             }else{
56963                 row.className = row.className.replace("x-grid-row-alt", "");
56964             }
56965             if(lrow){
56966                 lrow.className = row.className;
56967             }
56968         }
56969     },
56970
56971     restoreScroll : function(state){
56972         //Roo.log('GridView.restoreScroll');
56973         var sb = this.scroller.dom;
56974         sb.scrollLeft = state.left;
56975         sb.scrollTop = state.top;
56976         this.syncScroll();
56977     },
56978
56979     syncScroll : function(){
56980         //Roo.log('GridView.syncScroll');
56981         var sb = this.scroller.dom;
56982         var sh = this.mainHd.dom;
56983         var bs = this.mainBody.dom;
56984         var lv = this.lockedBody.dom;
56985         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56986         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56987     },
56988
56989     handleScroll : function(e){
56990         this.syncScroll();
56991         var sb = this.scroller.dom;
56992         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56993         e.stopEvent();
56994     },
56995
56996     handleWheel : function(e){
56997         var d = e.getWheelDelta();
56998         this.scroller.dom.scrollTop -= d*22;
56999         // set this here to prevent jumpy scrolling on large tables
57000         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57001         e.stopEvent();
57002     },
57003
57004     renderRows : function(startRow, endRow){
57005         // pull in all the crap needed to render rows
57006         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57007         var colCount = cm.getColumnCount();
57008
57009         if(ds.getCount() < 1){
57010             return ["", ""];
57011         }
57012
57013         // build a map for all the columns
57014         var cs = [];
57015         for(var i = 0; i < colCount; i++){
57016             var name = cm.getDataIndex(i);
57017             cs[i] = {
57018                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57019                 renderer : cm.getRenderer(i),
57020                 id : cm.getColumnId(i),
57021                 locked : cm.isLocked(i),
57022                 has_editor : cm.isCellEditable(i)
57023             };
57024         }
57025
57026         startRow = startRow || 0;
57027         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57028
57029         // records to render
57030         var rs = ds.getRange(startRow, endRow);
57031
57032         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57033     },
57034
57035     // As much as I hate to duplicate code, this was branched because FireFox really hates
57036     // [].join("") on strings. The performance difference was substantial enough to
57037     // branch this function
57038     doRender : Roo.isGecko ?
57039             function(cs, rs, ds, startRow, colCount, stripe){
57040                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57041                 // buffers
57042                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57043                 
57044                 var hasListener = this.grid.hasListener('rowclass');
57045                 var rowcfg = {};
57046                 for(var j = 0, len = rs.length; j < len; j++){
57047                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57048                     for(var i = 0; i < colCount; i++){
57049                         c = cs[i];
57050                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57051                         p.id = c.id;
57052                         p.css = p.attr = "";
57053                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57054                         if(p.value == undefined || p.value === "") {
57055                             p.value = "&#160;";
57056                         }
57057                         if(c.has_editor){
57058                             p.css += ' x-grid-editable-cell';
57059                         }
57060                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57061                             p.css +=  ' x-grid-dirty-cell';
57062                         }
57063                         var markup = ct.apply(p);
57064                         if(!c.locked){
57065                             cb+= markup;
57066                         }else{
57067                             lcb+= markup;
57068                         }
57069                     }
57070                     var alt = [];
57071                     if(stripe && ((rowIndex+1) % 2 == 0)){
57072                         alt.push("x-grid-row-alt")
57073                     }
57074                     if(r.dirty){
57075                         alt.push(  " x-grid-dirty-row");
57076                     }
57077                     rp.cells = lcb;
57078                     if(this.getRowClass){
57079                         alt.push(this.getRowClass(r, rowIndex));
57080                     }
57081                     if (hasListener) {
57082                         rowcfg = {
57083                              
57084                             record: r,
57085                             rowIndex : rowIndex,
57086                             rowClass : ''
57087                         };
57088                         this.grid.fireEvent('rowclass', this, rowcfg);
57089                         alt.push(rowcfg.rowClass);
57090                     }
57091                     rp.alt = alt.join(" ");
57092                     lbuf+= rt.apply(rp);
57093                     rp.cells = cb;
57094                     buf+=  rt.apply(rp);
57095                 }
57096                 return [lbuf, buf];
57097             } :
57098             function(cs, rs, ds, startRow, colCount, stripe){
57099                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57100                 // buffers
57101                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57102                 var hasListener = this.grid.hasListener('rowclass');
57103  
57104                 var rowcfg = {};
57105                 for(var j = 0, len = rs.length; j < len; j++){
57106                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57107                     for(var i = 0; i < colCount; i++){
57108                         c = cs[i];
57109                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57110                         p.id = c.id;
57111                         p.css = p.attr = "";
57112                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57113                         if(p.value == undefined || p.value === "") {
57114                             p.value = "&#160;";
57115                         }
57116                         //Roo.log(c);
57117                          if(c.has_editor){
57118                             p.css += ' x-grid-editable-cell';
57119                         }
57120                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57121                             p.css += ' x-grid-dirty-cell' 
57122                         }
57123                         
57124                         var markup = ct.apply(p);
57125                         if(!c.locked){
57126                             cb[cb.length] = markup;
57127                         }else{
57128                             lcb[lcb.length] = markup;
57129                         }
57130                     }
57131                     var alt = [];
57132                     if(stripe && ((rowIndex+1) % 2 == 0)){
57133                         alt.push( "x-grid-row-alt");
57134                     }
57135                     if(r.dirty){
57136                         alt.push(" x-grid-dirty-row");
57137                     }
57138                     rp.cells = lcb;
57139                     if(this.getRowClass){
57140                         alt.push( this.getRowClass(r, rowIndex));
57141                     }
57142                     if (hasListener) {
57143                         rowcfg = {
57144                              
57145                             record: r,
57146                             rowIndex : rowIndex,
57147                             rowClass : ''
57148                         };
57149                         this.grid.fireEvent('rowclass', this, rowcfg);
57150                         alt.push(rowcfg.rowClass);
57151                     }
57152                     
57153                     rp.alt = alt.join(" ");
57154                     rp.cells = lcb.join("");
57155                     lbuf[lbuf.length] = rt.apply(rp);
57156                     rp.cells = cb.join("");
57157                     buf[buf.length] =  rt.apply(rp);
57158                 }
57159                 return [lbuf.join(""), buf.join("")];
57160             },
57161
57162     renderBody : function(){
57163         var markup = this.renderRows();
57164         var bt = this.templates.body;
57165         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57166     },
57167
57168     /**
57169      * Refreshes the grid
57170      * @param {Boolean} headersToo
57171      */
57172     refresh : function(headersToo){
57173         this.fireEvent("beforerefresh", this);
57174         this.grid.stopEditing();
57175         var result = this.renderBody();
57176         this.lockedBody.update(result[0]);
57177         this.mainBody.update(result[1]);
57178         if(headersToo === true){
57179             this.updateHeaders();
57180             this.updateColumns();
57181             this.updateSplitters();
57182             this.updateHeaderSortState();
57183         }
57184         this.syncRowHeights();
57185         this.layout();
57186         this.fireEvent("refresh", this);
57187     },
57188
57189     handleColumnMove : function(cm, oldIndex, newIndex){
57190         this.indexMap = null;
57191         var s = this.getScrollState();
57192         this.refresh(true);
57193         this.restoreScroll(s);
57194         this.afterMove(newIndex);
57195     },
57196
57197     afterMove : function(colIndex){
57198         if(this.enableMoveAnim && Roo.enableFx){
57199             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57200         }
57201         // if multisort - fix sortOrder, and reload..
57202         if (this.grid.dataSource.multiSort) {
57203             // the we can call sort again..
57204             var dm = this.grid.dataSource;
57205             var cm = this.grid.colModel;
57206             var so = [];
57207             for(var i = 0; i < cm.config.length; i++ ) {
57208                 
57209                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57210                     continue; // dont' bother, it's not in sort list or being set.
57211                 }
57212                 
57213                 so.push(cm.config[i].dataIndex);
57214             };
57215             dm.sortOrder = so;
57216             dm.load(dm.lastOptions);
57217             
57218             
57219         }
57220         
57221     },
57222
57223     updateCell : function(dm, rowIndex, dataIndex){
57224         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57225         if(typeof colIndex == "undefined"){ // not present in grid
57226             return;
57227         }
57228         var cm = this.grid.colModel;
57229         var cell = this.getCell(rowIndex, colIndex);
57230         var cellText = this.getCellText(rowIndex, colIndex);
57231
57232         var p = {
57233             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57234             id : cm.getColumnId(colIndex),
57235             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57236         };
57237         var renderer = cm.getRenderer(colIndex);
57238         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57239         if(typeof val == "undefined" || val === "") {
57240             val = "&#160;";
57241         }
57242         cellText.innerHTML = val;
57243         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57244         this.syncRowHeights(rowIndex, rowIndex);
57245     },
57246
57247     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57248         var maxWidth = 0;
57249         if(this.grid.autoSizeHeaders){
57250             var h = this.getHeaderCellMeasure(colIndex);
57251             maxWidth = Math.max(maxWidth, h.scrollWidth);
57252         }
57253         var tb, index;
57254         if(this.cm.isLocked(colIndex)){
57255             tb = this.getLockedTable();
57256             index = colIndex;
57257         }else{
57258             tb = this.getBodyTable();
57259             index = colIndex - this.cm.getLockedCount();
57260         }
57261         if(tb && tb.rows){
57262             var rows = tb.rows;
57263             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57264             for(var i = 0; i < stopIndex; i++){
57265                 var cell = rows[i].childNodes[index].firstChild;
57266                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57267             }
57268         }
57269         return maxWidth + /*margin for error in IE*/ 5;
57270     },
57271     /**
57272      * Autofit a column to its content.
57273      * @param {Number} colIndex
57274      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57275      */
57276      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57277          if(this.cm.isHidden(colIndex)){
57278              return; // can't calc a hidden column
57279          }
57280         if(forceMinSize){
57281             var cid = this.cm.getColumnId(colIndex);
57282             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57283            if(this.grid.autoSizeHeaders){
57284                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57285            }
57286         }
57287         var newWidth = this.calcColumnWidth(colIndex);
57288         this.cm.setColumnWidth(colIndex,
57289             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57290         if(!suppressEvent){
57291             this.grid.fireEvent("columnresize", colIndex, newWidth);
57292         }
57293     },
57294
57295     /**
57296      * Autofits all columns to their content and then expands to fit any extra space in the grid
57297      */
57298      autoSizeColumns : function(){
57299         var cm = this.grid.colModel;
57300         var colCount = cm.getColumnCount();
57301         for(var i = 0; i < colCount; i++){
57302             this.autoSizeColumn(i, true, true);
57303         }
57304         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57305             this.fitColumns();
57306         }else{
57307             this.updateColumns();
57308             this.layout();
57309         }
57310     },
57311
57312     /**
57313      * Autofits all columns to the grid's width proportionate with their current size
57314      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57315      */
57316     fitColumns : function(reserveScrollSpace){
57317         var cm = this.grid.colModel;
57318         var colCount = cm.getColumnCount();
57319         var cols = [];
57320         var width = 0;
57321         var i, w;
57322         for (i = 0; i < colCount; i++){
57323             if(!cm.isHidden(i) && !cm.isFixed(i)){
57324                 w = cm.getColumnWidth(i);
57325                 cols.push(i);
57326                 cols.push(w);
57327                 width += w;
57328             }
57329         }
57330         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57331         if(reserveScrollSpace){
57332             avail -= 17;
57333         }
57334         var frac = (avail - cm.getTotalWidth())/width;
57335         while (cols.length){
57336             w = cols.pop();
57337             i = cols.pop();
57338             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57339         }
57340         this.updateColumns();
57341         this.layout();
57342     },
57343
57344     onRowSelect : function(rowIndex){
57345         var row = this.getRowComposite(rowIndex);
57346         row.addClass("x-grid-row-selected");
57347     },
57348
57349     onRowDeselect : function(rowIndex){
57350         var row = this.getRowComposite(rowIndex);
57351         row.removeClass("x-grid-row-selected");
57352     },
57353
57354     onCellSelect : function(row, col){
57355         var cell = this.getCell(row, col);
57356         if(cell){
57357             Roo.fly(cell).addClass("x-grid-cell-selected");
57358         }
57359     },
57360
57361     onCellDeselect : function(row, col){
57362         var cell = this.getCell(row, col);
57363         if(cell){
57364             Roo.fly(cell).removeClass("x-grid-cell-selected");
57365         }
57366     },
57367
57368     updateHeaderSortState : function(){
57369         
57370         // sort state can be single { field: xxx, direction : yyy}
57371         // or   { xxx=>ASC , yyy : DESC ..... }
57372         
57373         var mstate = {};
57374         if (!this.ds.multiSort) { 
57375             var state = this.ds.getSortState();
57376             if(!state){
57377                 return;
57378             }
57379             mstate[state.field] = state.direction;
57380             // FIXME... - this is not used here.. but might be elsewhere..
57381             this.sortState = state;
57382             
57383         } else {
57384             mstate = this.ds.sortToggle;
57385         }
57386         //remove existing sort classes..
57387         
57388         var sc = this.sortClasses;
57389         var hds = this.el.select(this.headerSelector).removeClass(sc);
57390         
57391         for(var f in mstate) {
57392         
57393             var sortColumn = this.cm.findColumnIndex(f);
57394             
57395             if(sortColumn != -1){
57396                 var sortDir = mstate[f];        
57397                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57398             }
57399         }
57400         
57401          
57402         
57403     },
57404
57405
57406     handleHeaderClick : function(g, index,e){
57407         
57408         Roo.log("header click");
57409         
57410         if (Roo.isTouch) {
57411             // touch events on header are handled by context
57412             this.handleHdCtx(g,index,e);
57413             return;
57414         }
57415         
57416         
57417         if(this.headersDisabled){
57418             return;
57419         }
57420         var dm = g.dataSource, cm = g.colModel;
57421         if(!cm.isSortable(index)){
57422             return;
57423         }
57424         g.stopEditing();
57425         
57426         if (dm.multiSort) {
57427             // update the sortOrder
57428             var so = [];
57429             for(var i = 0; i < cm.config.length; i++ ) {
57430                 
57431                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57432                     continue; // dont' bother, it's not in sort list or being set.
57433                 }
57434                 
57435                 so.push(cm.config[i].dataIndex);
57436             };
57437             dm.sortOrder = so;
57438         }
57439         
57440         
57441         dm.sort(cm.getDataIndex(index));
57442     },
57443
57444
57445     destroy : function(){
57446         if(this.colMenu){
57447             this.colMenu.removeAll();
57448             Roo.menu.MenuMgr.unregister(this.colMenu);
57449             this.colMenu.getEl().remove();
57450             delete this.colMenu;
57451         }
57452         if(this.hmenu){
57453             this.hmenu.removeAll();
57454             Roo.menu.MenuMgr.unregister(this.hmenu);
57455             this.hmenu.getEl().remove();
57456             delete this.hmenu;
57457         }
57458         if(this.grid.enableColumnMove){
57459             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57460             if(dds){
57461                 for(var dd in dds){
57462                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57463                         var elid = dds[dd].dragElId;
57464                         dds[dd].unreg();
57465                         Roo.get(elid).remove();
57466                     } else if(dds[dd].config.isTarget){
57467                         dds[dd].proxyTop.remove();
57468                         dds[dd].proxyBottom.remove();
57469                         dds[dd].unreg();
57470                     }
57471                     if(Roo.dd.DDM.locationCache[dd]){
57472                         delete Roo.dd.DDM.locationCache[dd];
57473                     }
57474                 }
57475                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57476             }
57477         }
57478         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57479         this.bind(null, null);
57480         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57481     },
57482
57483     handleLockChange : function(){
57484         this.refresh(true);
57485     },
57486
57487     onDenyColumnLock : function(){
57488
57489     },
57490
57491     onDenyColumnHide : function(){
57492
57493     },
57494
57495     handleHdMenuClick : function(item){
57496         var index = this.hdCtxIndex;
57497         var cm = this.cm, ds = this.ds;
57498         switch(item.id){
57499             case "asc":
57500                 ds.sort(cm.getDataIndex(index), "ASC");
57501                 break;
57502             case "desc":
57503                 ds.sort(cm.getDataIndex(index), "DESC");
57504                 break;
57505             case "lock":
57506                 var lc = cm.getLockedCount();
57507                 if(cm.getColumnCount(true) <= lc+1){
57508                     this.onDenyColumnLock();
57509                     return;
57510                 }
57511                 if(lc != index){
57512                     cm.setLocked(index, true, true);
57513                     cm.moveColumn(index, lc);
57514                     this.grid.fireEvent("columnmove", index, lc);
57515                 }else{
57516                     cm.setLocked(index, true);
57517                 }
57518             break;
57519             case "unlock":
57520                 var lc = cm.getLockedCount();
57521                 if((lc-1) != index){
57522                     cm.setLocked(index, false, true);
57523                     cm.moveColumn(index, lc-1);
57524                     this.grid.fireEvent("columnmove", index, lc-1);
57525                 }else{
57526                     cm.setLocked(index, false);
57527                 }
57528             break;
57529             case 'wider': // used to expand cols on touch..
57530             case 'narrow':
57531                 var cw = cm.getColumnWidth(index);
57532                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57533                 cw = Math.max(0, cw);
57534                 cw = Math.min(cw,4000);
57535                 cm.setColumnWidth(index, cw);
57536                 break;
57537                 
57538             default:
57539                 index = cm.getIndexById(item.id.substr(4));
57540                 if(index != -1){
57541                     if(item.checked && cm.getColumnCount(true) <= 1){
57542                         this.onDenyColumnHide();
57543                         return false;
57544                     }
57545                     cm.setHidden(index, item.checked);
57546                 }
57547         }
57548         return true;
57549     },
57550
57551     beforeColMenuShow : function(){
57552         var cm = this.cm,  colCount = cm.getColumnCount();
57553         this.colMenu.removeAll();
57554         for(var i = 0; i < colCount; i++){
57555             this.colMenu.add(new Roo.menu.CheckItem({
57556                 id: "col-"+cm.getColumnId(i),
57557                 text: cm.getColumnHeader(i),
57558                 checked: !cm.isHidden(i),
57559                 hideOnClick:false
57560             }));
57561         }
57562     },
57563
57564     handleHdCtx : function(g, index, e){
57565         e.stopEvent();
57566         var hd = this.getHeaderCell(index);
57567         this.hdCtxIndex = index;
57568         var ms = this.hmenu.items, cm = this.cm;
57569         ms.get("asc").setDisabled(!cm.isSortable(index));
57570         ms.get("desc").setDisabled(!cm.isSortable(index));
57571         if(this.grid.enableColLock !== false){
57572             ms.get("lock").setDisabled(cm.isLocked(index));
57573             ms.get("unlock").setDisabled(!cm.isLocked(index));
57574         }
57575         this.hmenu.show(hd, "tl-bl");
57576     },
57577
57578     handleHdOver : function(e){
57579         var hd = this.findHeaderCell(e.getTarget());
57580         if(hd && !this.headersDisabled){
57581             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57582                this.fly(hd).addClass("x-grid-hd-over");
57583             }
57584         }
57585     },
57586
57587     handleHdOut : function(e){
57588         var hd = this.findHeaderCell(e.getTarget());
57589         if(hd){
57590             this.fly(hd).removeClass("x-grid-hd-over");
57591         }
57592     },
57593
57594     handleSplitDblClick : function(e, t){
57595         var i = this.getCellIndex(t);
57596         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57597             this.autoSizeColumn(i, true);
57598             this.layout();
57599         }
57600     },
57601
57602     render : function(){
57603
57604         var cm = this.cm;
57605         var colCount = cm.getColumnCount();
57606
57607         if(this.grid.monitorWindowResize === true){
57608             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57609         }
57610         var header = this.renderHeaders();
57611         var body = this.templates.body.apply({rows:""});
57612         var html = this.templates.master.apply({
57613             lockedBody: body,
57614             body: body,
57615             lockedHeader: header[0],
57616             header: header[1]
57617         });
57618
57619         //this.updateColumns();
57620
57621         this.grid.getGridEl().dom.innerHTML = html;
57622
57623         this.initElements();
57624         
57625         // a kludge to fix the random scolling effect in webkit
57626         this.el.on("scroll", function() {
57627             this.el.dom.scrollTop=0; // hopefully not recursive..
57628         },this);
57629
57630         this.scroller.on("scroll", this.handleScroll, this);
57631         this.lockedBody.on("mousewheel", this.handleWheel, this);
57632         this.mainBody.on("mousewheel", this.handleWheel, this);
57633
57634         this.mainHd.on("mouseover", this.handleHdOver, this);
57635         this.mainHd.on("mouseout", this.handleHdOut, this);
57636         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57637                 {delegate: "."+this.splitClass});
57638
57639         this.lockedHd.on("mouseover", this.handleHdOver, this);
57640         this.lockedHd.on("mouseout", this.handleHdOut, this);
57641         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57642                 {delegate: "."+this.splitClass});
57643
57644         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57645             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57646         }
57647
57648         this.updateSplitters();
57649
57650         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57651             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57652             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57653         }
57654
57655         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57656             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57657             this.hmenu.add(
57658                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57659                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57660             );
57661             if(this.grid.enableColLock !== false){
57662                 this.hmenu.add('-',
57663                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57664                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57665                 );
57666             }
57667             if (Roo.isTouch) {
57668                  this.hmenu.add('-',
57669                     {id:"wider", text: this.columnsWiderText},
57670                     {id:"narrow", text: this.columnsNarrowText }
57671                 );
57672                 
57673                  
57674             }
57675             
57676             if(this.grid.enableColumnHide !== false){
57677
57678                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57679                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57680                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57681
57682                 this.hmenu.add('-',
57683                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57684                 );
57685             }
57686             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57687
57688             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57689         }
57690
57691         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57692             this.dd = new Roo.grid.GridDragZone(this.grid, {
57693                 ddGroup : this.grid.ddGroup || 'GridDD'
57694             });
57695             
57696         }
57697
57698         /*
57699         for(var i = 0; i < colCount; i++){
57700             if(cm.isHidden(i)){
57701                 this.hideColumn(i);
57702             }
57703             if(cm.config[i].align){
57704                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57705                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57706             }
57707         }*/
57708         
57709         this.updateHeaderSortState();
57710
57711         this.beforeInitialResize();
57712         this.layout(true);
57713
57714         // two part rendering gives faster view to the user
57715         this.renderPhase2.defer(1, this);
57716     },
57717
57718     renderPhase2 : function(){
57719         // render the rows now
57720         this.refresh();
57721         if(this.grid.autoSizeColumns){
57722             this.autoSizeColumns();
57723         }
57724     },
57725
57726     beforeInitialResize : function(){
57727
57728     },
57729
57730     onColumnSplitterMoved : function(i, w){
57731         this.userResized = true;
57732         var cm = this.grid.colModel;
57733         cm.setColumnWidth(i, w, true);
57734         var cid = cm.getColumnId(i);
57735         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57736         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57737         this.updateSplitters();
57738         this.layout();
57739         this.grid.fireEvent("columnresize", i, w);
57740     },
57741
57742     syncRowHeights : function(startIndex, endIndex){
57743         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57744             startIndex = startIndex || 0;
57745             var mrows = this.getBodyTable().rows;
57746             var lrows = this.getLockedTable().rows;
57747             var len = mrows.length-1;
57748             endIndex = Math.min(endIndex || len, len);
57749             for(var i = startIndex; i <= endIndex; i++){
57750                 var m = mrows[i], l = lrows[i];
57751                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57752                 m.style.height = l.style.height = h + "px";
57753             }
57754         }
57755     },
57756
57757     layout : function(initialRender, is2ndPass){
57758         var g = this.grid;
57759         var auto = g.autoHeight;
57760         var scrollOffset = 16;
57761         var c = g.getGridEl(), cm = this.cm,
57762                 expandCol = g.autoExpandColumn,
57763                 gv = this;
57764         //c.beginMeasure();
57765
57766         if(!c.dom.offsetWidth){ // display:none?
57767             if(initialRender){
57768                 this.lockedWrap.show();
57769                 this.mainWrap.show();
57770             }
57771             return;
57772         }
57773
57774         var hasLock = this.cm.isLocked(0);
57775
57776         var tbh = this.headerPanel.getHeight();
57777         var bbh = this.footerPanel.getHeight();
57778
57779         if(auto){
57780             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57781             var newHeight = ch + c.getBorderWidth("tb");
57782             if(g.maxHeight){
57783                 newHeight = Math.min(g.maxHeight, newHeight);
57784             }
57785             c.setHeight(newHeight);
57786         }
57787
57788         if(g.autoWidth){
57789             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57790         }
57791
57792         var s = this.scroller;
57793
57794         var csize = c.getSize(true);
57795
57796         this.el.setSize(csize.width, csize.height);
57797
57798         this.headerPanel.setWidth(csize.width);
57799         this.footerPanel.setWidth(csize.width);
57800
57801         var hdHeight = this.mainHd.getHeight();
57802         var vw = csize.width;
57803         var vh = csize.height - (tbh + bbh);
57804
57805         s.setSize(vw, vh);
57806
57807         var bt = this.getBodyTable();
57808         
57809         if(cm.getLockedCount() == cm.config.length){
57810             bt = this.getLockedTable();
57811         }
57812         
57813         var ltWidth = hasLock ?
57814                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57815
57816         var scrollHeight = bt.offsetHeight;
57817         var scrollWidth = ltWidth + bt.offsetWidth;
57818         var vscroll = false, hscroll = false;
57819
57820         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57821
57822         var lw = this.lockedWrap, mw = this.mainWrap;
57823         var lb = this.lockedBody, mb = this.mainBody;
57824
57825         setTimeout(function(){
57826             var t = s.dom.offsetTop;
57827             var w = s.dom.clientWidth,
57828                 h = s.dom.clientHeight;
57829
57830             lw.setTop(t);
57831             lw.setSize(ltWidth, h);
57832
57833             mw.setLeftTop(ltWidth, t);
57834             mw.setSize(w-ltWidth, h);
57835
57836             lb.setHeight(h-hdHeight);
57837             mb.setHeight(h-hdHeight);
57838
57839             if(is2ndPass !== true && !gv.userResized && expandCol){
57840                 // high speed resize without full column calculation
57841                 
57842                 var ci = cm.getIndexById(expandCol);
57843                 if (ci < 0) {
57844                     ci = cm.findColumnIndex(expandCol);
57845                 }
57846                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57847                 var expandId = cm.getColumnId(ci);
57848                 var  tw = cm.getTotalWidth(false);
57849                 var currentWidth = cm.getColumnWidth(ci);
57850                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57851                 if(currentWidth != cw){
57852                     cm.setColumnWidth(ci, cw, true);
57853                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57854                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57855                     gv.updateSplitters();
57856                     gv.layout(false, true);
57857                 }
57858             }
57859
57860             if(initialRender){
57861                 lw.show();
57862                 mw.show();
57863             }
57864             //c.endMeasure();
57865         }, 10);
57866     },
57867
57868     onWindowResize : function(){
57869         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57870             return;
57871         }
57872         this.layout();
57873     },
57874
57875     appendFooter : function(parentEl){
57876         return null;
57877     },
57878
57879     sortAscText : "Sort Ascending",
57880     sortDescText : "Sort Descending",
57881     lockText : "Lock Column",
57882     unlockText : "Unlock Column",
57883     columnsText : "Columns",
57884  
57885     columnsWiderText : "Wider",
57886     columnsNarrowText : "Thinner"
57887 });
57888
57889
57890 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57891     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57892     this.proxy.el.addClass('x-grid3-col-dd');
57893 };
57894
57895 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57896     handleMouseDown : function(e){
57897
57898     },
57899
57900     callHandleMouseDown : function(e){
57901         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57902     }
57903 });
57904 /*
57905  * Based on:
57906  * Ext JS Library 1.1.1
57907  * Copyright(c) 2006-2007, Ext JS, LLC.
57908  *
57909  * Originally Released Under LGPL - original licence link has changed is not relivant.
57910  *
57911  * Fork - LGPL
57912  * <script type="text/javascript">
57913  */
57914  
57915 // private
57916 // This is a support class used internally by the Grid components
57917 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57918     this.grid = grid;
57919     this.view = grid.getView();
57920     this.proxy = this.view.resizeProxy;
57921     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57922         "gridSplitters" + this.grid.getGridEl().id, {
57923         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57924     });
57925     this.setHandleElId(Roo.id(hd));
57926     this.setOuterHandleElId(Roo.id(hd2));
57927     this.scroll = false;
57928 };
57929 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57930     fly: Roo.Element.fly,
57931
57932     b4StartDrag : function(x, y){
57933         this.view.headersDisabled = true;
57934         this.proxy.setHeight(this.view.mainWrap.getHeight());
57935         var w = this.cm.getColumnWidth(this.cellIndex);
57936         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57937         this.resetConstraints();
57938         this.setXConstraint(minw, 1000);
57939         this.setYConstraint(0, 0);
57940         this.minX = x - minw;
57941         this.maxX = x + 1000;
57942         this.startPos = x;
57943         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57944     },
57945
57946
57947     handleMouseDown : function(e){
57948         ev = Roo.EventObject.setEvent(e);
57949         var t = this.fly(ev.getTarget());
57950         if(t.hasClass("x-grid-split")){
57951             this.cellIndex = this.view.getCellIndex(t.dom);
57952             this.split = t.dom;
57953             this.cm = this.grid.colModel;
57954             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57955                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57956             }
57957         }
57958     },
57959
57960     endDrag : function(e){
57961         this.view.headersDisabled = false;
57962         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57963         var diff = endX - this.startPos;
57964         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57965     },
57966
57967     autoOffset : function(){
57968         this.setDelta(0,0);
57969     }
57970 });/*
57971  * Based on:
57972  * Ext JS Library 1.1.1
57973  * Copyright(c) 2006-2007, Ext JS, LLC.
57974  *
57975  * Originally Released Under LGPL - original licence link has changed is not relivant.
57976  *
57977  * Fork - LGPL
57978  * <script type="text/javascript">
57979  */
57980  
57981 // private
57982 // This is a support class used internally by the Grid components
57983 Roo.grid.GridDragZone = function(grid, config){
57984     this.view = grid.getView();
57985     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57986     if(this.view.lockedBody){
57987         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57988         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57989     }
57990     this.scroll = false;
57991     this.grid = grid;
57992     this.ddel = document.createElement('div');
57993     this.ddel.className = 'x-grid-dd-wrap';
57994 };
57995
57996 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57997     ddGroup : "GridDD",
57998
57999     getDragData : function(e){
58000         var t = Roo.lib.Event.getTarget(e);
58001         var rowIndex = this.view.findRowIndex(t);
58002         var sm = this.grid.selModel;
58003             
58004         //Roo.log(rowIndex);
58005         
58006         if (sm.getSelectedCell) {
58007             // cell selection..
58008             if (!sm.getSelectedCell()) {
58009                 return false;
58010             }
58011             if (rowIndex != sm.getSelectedCell()[0]) {
58012                 return false;
58013             }
58014         
58015         }
58016         
58017         if(rowIndex !== false){
58018             
58019             // if editorgrid.. 
58020             
58021             
58022             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58023                
58024             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58025               //  
58026             //}
58027             if (e.hasModifier()){
58028                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58029             }
58030             
58031             Roo.log("getDragData");
58032             
58033             return {
58034                 grid: this.grid,
58035                 ddel: this.ddel,
58036                 rowIndex: rowIndex,
58037                 selections:sm.getSelections ? sm.getSelections() : (
58038                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
58039                 )
58040             };
58041         }
58042         return false;
58043     },
58044
58045     onInitDrag : function(e){
58046         var data = this.dragData;
58047         this.ddel.innerHTML = this.grid.getDragDropText();
58048         this.proxy.update(this.ddel);
58049         // fire start drag?
58050     },
58051
58052     afterRepair : function(){
58053         this.dragging = false;
58054     },
58055
58056     getRepairXY : function(e, data){
58057         return false;
58058     },
58059
58060     onEndDrag : function(data, e){
58061         // fire end drag?
58062     },
58063
58064     onValidDrop : function(dd, e, id){
58065         // fire drag drop?
58066         this.hideProxy();
58067     },
58068
58069     beforeInvalidDrop : function(e, id){
58070
58071     }
58072 });/*
58073  * Based on:
58074  * Ext JS Library 1.1.1
58075  * Copyright(c) 2006-2007, Ext JS, LLC.
58076  *
58077  * Originally Released Under LGPL - original licence link has changed is not relivant.
58078  *
58079  * Fork - LGPL
58080  * <script type="text/javascript">
58081  */
58082  
58083
58084 /**
58085  * @class Roo.grid.ColumnModel
58086  * @extends Roo.util.Observable
58087  * This is the default implementation of a ColumnModel used by the Grid. It defines
58088  * the columns in the grid.
58089  * <br>Usage:<br>
58090  <pre><code>
58091  var colModel = new Roo.grid.ColumnModel([
58092         {header: "Ticker", width: 60, sortable: true, locked: true},
58093         {header: "Company Name", width: 150, sortable: true},
58094         {header: "Market Cap.", width: 100, sortable: true},
58095         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58096         {header: "Employees", width: 100, sortable: true, resizable: false}
58097  ]);
58098  </code></pre>
58099  * <p>
58100  
58101  * The config options listed for this class are options which may appear in each
58102  * individual column definition.
58103  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58104  * @constructor
58105  * @param {Object} config An Array of column config objects. See this class's
58106  * config objects for details.
58107 */
58108 Roo.grid.ColumnModel = function(config){
58109         /**
58110      * The config passed into the constructor
58111      */
58112     this.config = config;
58113     this.lookup = {};
58114
58115     // if no id, create one
58116     // if the column does not have a dataIndex mapping,
58117     // map it to the order it is in the config
58118     for(var i = 0, len = config.length; i < len; i++){
58119         var c = config[i];
58120         if(typeof c.dataIndex == "undefined"){
58121             c.dataIndex = i;
58122         }
58123         if(typeof c.renderer == "string"){
58124             c.renderer = Roo.util.Format[c.renderer];
58125         }
58126         if(typeof c.id == "undefined"){
58127             c.id = Roo.id();
58128         }
58129         if(c.editor && c.editor.xtype){
58130             c.editor  = Roo.factory(c.editor, Roo.grid);
58131         }
58132         if(c.editor && c.editor.isFormField){
58133             c.editor = new Roo.grid.GridEditor(c.editor);
58134         }
58135         this.lookup[c.id] = c;
58136     }
58137
58138     /**
58139      * The width of columns which have no width specified (defaults to 100)
58140      * @type Number
58141      */
58142     this.defaultWidth = 100;
58143
58144     /**
58145      * Default sortable of columns which have no sortable specified (defaults to false)
58146      * @type Boolean
58147      */
58148     this.defaultSortable = false;
58149
58150     this.addEvents({
58151         /**
58152              * @event widthchange
58153              * Fires when the width of a column changes.
58154              * @param {ColumnModel} this
58155              * @param {Number} columnIndex The column index
58156              * @param {Number} newWidth The new width
58157              */
58158             "widthchange": true,
58159         /**
58160              * @event headerchange
58161              * Fires when the text of a header changes.
58162              * @param {ColumnModel} this
58163              * @param {Number} columnIndex The column index
58164              * @param {Number} newText The new header text
58165              */
58166             "headerchange": true,
58167         /**
58168              * @event hiddenchange
58169              * Fires when a column is hidden or "unhidden".
58170              * @param {ColumnModel} this
58171              * @param {Number} columnIndex The column index
58172              * @param {Boolean} hidden true if hidden, false otherwise
58173              */
58174             "hiddenchange": true,
58175             /**
58176          * @event columnmoved
58177          * Fires when a column is moved.
58178          * @param {ColumnModel} this
58179          * @param {Number} oldIndex
58180          * @param {Number} newIndex
58181          */
58182         "columnmoved" : true,
58183         /**
58184          * @event columlockchange
58185          * Fires when a column's locked state is changed
58186          * @param {ColumnModel} this
58187          * @param {Number} colIndex
58188          * @param {Boolean} locked true if locked
58189          */
58190         "columnlockchange" : true
58191     });
58192     Roo.grid.ColumnModel.superclass.constructor.call(this);
58193 };
58194 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58195     /**
58196      * @cfg {String} header The header text to display in the Grid view.
58197      */
58198     /**
58199      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58200      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58201      * specified, the column's index is used as an index into the Record's data Array.
58202      */
58203     /**
58204      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58205      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58206      */
58207     /**
58208      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58209      * Defaults to the value of the {@link #defaultSortable} property.
58210      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58211      */
58212     /**
58213      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58214      */
58215     /**
58216      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58217      */
58218     /**
58219      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58220      */
58221     /**
58222      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58223      */
58224     /**
58225      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58226      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58227      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58228      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58229      */
58230        /**
58231      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58232      */
58233     /**
58234      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58235      */
58236     /**
58237      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58238      */
58239     /**
58240      * @cfg {String} cursor (Optional)
58241      */
58242     /**
58243      * @cfg {String} tooltip (Optional)
58244      */
58245     /**
58246      * @cfg {Number} xs (Optional)
58247      */
58248     /**
58249      * @cfg {Number} sm (Optional)
58250      */
58251     /**
58252      * @cfg {Number} md (Optional)
58253      */
58254     /**
58255      * @cfg {Number} lg (Optional)
58256      */
58257     /**
58258      * Returns the id of the column at the specified index.
58259      * @param {Number} index The column index
58260      * @return {String} the id
58261      */
58262     getColumnId : function(index){
58263         return this.config[index].id;
58264     },
58265
58266     /**
58267      * Returns the column for a specified id.
58268      * @param {String} id The column id
58269      * @return {Object} the column
58270      */
58271     getColumnById : function(id){
58272         return this.lookup[id];
58273     },
58274
58275     
58276     /**
58277      * Returns the column for a specified dataIndex.
58278      * @param {String} dataIndex The column dataIndex
58279      * @return {Object|Boolean} the column or false if not found
58280      */
58281     getColumnByDataIndex: function(dataIndex){
58282         var index = this.findColumnIndex(dataIndex);
58283         return index > -1 ? this.config[index] : false;
58284     },
58285     
58286     /**
58287      * Returns the index for a specified column id.
58288      * @param {String} id The column id
58289      * @return {Number} the index, or -1 if not found
58290      */
58291     getIndexById : function(id){
58292         for(var i = 0, len = this.config.length; i < len; i++){
58293             if(this.config[i].id == id){
58294                 return i;
58295             }
58296         }
58297         return -1;
58298     },
58299     
58300     /**
58301      * Returns the index for a specified column dataIndex.
58302      * @param {String} dataIndex The column dataIndex
58303      * @return {Number} the index, or -1 if not found
58304      */
58305     
58306     findColumnIndex : function(dataIndex){
58307         for(var i = 0, len = this.config.length; i < len; i++){
58308             if(this.config[i].dataIndex == dataIndex){
58309                 return i;
58310             }
58311         }
58312         return -1;
58313     },
58314     
58315     
58316     moveColumn : function(oldIndex, newIndex){
58317         var c = this.config[oldIndex];
58318         this.config.splice(oldIndex, 1);
58319         this.config.splice(newIndex, 0, c);
58320         this.dataMap = null;
58321         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58322     },
58323
58324     isLocked : function(colIndex){
58325         return this.config[colIndex].locked === true;
58326     },
58327
58328     setLocked : function(colIndex, value, suppressEvent){
58329         if(this.isLocked(colIndex) == value){
58330             return;
58331         }
58332         this.config[colIndex].locked = value;
58333         if(!suppressEvent){
58334             this.fireEvent("columnlockchange", this, colIndex, value);
58335         }
58336     },
58337
58338     getTotalLockedWidth : function(){
58339         var totalWidth = 0;
58340         for(var i = 0; i < this.config.length; i++){
58341             if(this.isLocked(i) && !this.isHidden(i)){
58342                 this.totalWidth += this.getColumnWidth(i);
58343             }
58344         }
58345         return totalWidth;
58346     },
58347
58348     getLockedCount : function(){
58349         for(var i = 0, len = this.config.length; i < len; i++){
58350             if(!this.isLocked(i)){
58351                 return i;
58352             }
58353         }
58354         
58355         return this.config.length;
58356     },
58357
58358     /**
58359      * Returns the number of columns.
58360      * @return {Number}
58361      */
58362     getColumnCount : function(visibleOnly){
58363         if(visibleOnly === true){
58364             var c = 0;
58365             for(var i = 0, len = this.config.length; i < len; i++){
58366                 if(!this.isHidden(i)){
58367                     c++;
58368                 }
58369             }
58370             return c;
58371         }
58372         return this.config.length;
58373     },
58374
58375     /**
58376      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58377      * @param {Function} fn
58378      * @param {Object} scope (optional)
58379      * @return {Array} result
58380      */
58381     getColumnsBy : function(fn, scope){
58382         var r = [];
58383         for(var i = 0, len = this.config.length; i < len; i++){
58384             var c = this.config[i];
58385             if(fn.call(scope||this, c, i) === true){
58386                 r[r.length] = c;
58387             }
58388         }
58389         return r;
58390     },
58391
58392     /**
58393      * Returns true if the specified column is sortable.
58394      * @param {Number} col The column index
58395      * @return {Boolean}
58396      */
58397     isSortable : function(col){
58398         if(typeof this.config[col].sortable == "undefined"){
58399             return this.defaultSortable;
58400         }
58401         return this.config[col].sortable;
58402     },
58403
58404     /**
58405      * Returns the rendering (formatting) function defined for the column.
58406      * @param {Number} col The column index.
58407      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58408      */
58409     getRenderer : function(col){
58410         if(!this.config[col].renderer){
58411             return Roo.grid.ColumnModel.defaultRenderer;
58412         }
58413         return this.config[col].renderer;
58414     },
58415
58416     /**
58417      * Sets the rendering (formatting) function for a column.
58418      * @param {Number} col The column index
58419      * @param {Function} fn The function to use to process the cell's raw data
58420      * to return HTML markup for the grid view. The render function is called with
58421      * the following parameters:<ul>
58422      * <li>Data value.</li>
58423      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58424      * <li>css A CSS style string to apply to the table cell.</li>
58425      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58426      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58427      * <li>Row index</li>
58428      * <li>Column index</li>
58429      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58430      */
58431     setRenderer : function(col, fn){
58432         this.config[col].renderer = fn;
58433     },
58434
58435     /**
58436      * Returns the width for the specified column.
58437      * @param {Number} col The column index
58438      * @return {Number}
58439      */
58440     getColumnWidth : function(col){
58441         return this.config[col].width * 1 || this.defaultWidth;
58442     },
58443
58444     /**
58445      * Sets the width for a column.
58446      * @param {Number} col The column index
58447      * @param {Number} width The new width
58448      */
58449     setColumnWidth : function(col, width, suppressEvent){
58450         this.config[col].width = width;
58451         this.totalWidth = null;
58452         if(!suppressEvent){
58453              this.fireEvent("widthchange", this, col, width);
58454         }
58455     },
58456
58457     /**
58458      * Returns the total width of all columns.
58459      * @param {Boolean} includeHidden True to include hidden column widths
58460      * @return {Number}
58461      */
58462     getTotalWidth : function(includeHidden){
58463         if(!this.totalWidth){
58464             this.totalWidth = 0;
58465             for(var i = 0, len = this.config.length; i < len; i++){
58466                 if(includeHidden || !this.isHidden(i)){
58467                     this.totalWidth += this.getColumnWidth(i);
58468                 }
58469             }
58470         }
58471         return this.totalWidth;
58472     },
58473
58474     /**
58475      * Returns the header for the specified column.
58476      * @param {Number} col The column index
58477      * @return {String}
58478      */
58479     getColumnHeader : function(col){
58480         return this.config[col].header;
58481     },
58482
58483     /**
58484      * Sets the header for a column.
58485      * @param {Number} col The column index
58486      * @param {String} header The new header
58487      */
58488     setColumnHeader : function(col, header){
58489         this.config[col].header = header;
58490         this.fireEvent("headerchange", this, col, header);
58491     },
58492
58493     /**
58494      * Returns the tooltip for the specified column.
58495      * @param {Number} col The column index
58496      * @return {String}
58497      */
58498     getColumnTooltip : function(col){
58499             return this.config[col].tooltip;
58500     },
58501     /**
58502      * Sets the tooltip for a column.
58503      * @param {Number} col The column index
58504      * @param {String} tooltip The new tooltip
58505      */
58506     setColumnTooltip : function(col, tooltip){
58507             this.config[col].tooltip = tooltip;
58508     },
58509
58510     /**
58511      * Returns the dataIndex for the specified column.
58512      * @param {Number} col The column index
58513      * @return {Number}
58514      */
58515     getDataIndex : function(col){
58516         return this.config[col].dataIndex;
58517     },
58518
58519     /**
58520      * Sets the dataIndex for a column.
58521      * @param {Number} col The column index
58522      * @param {Number} dataIndex The new dataIndex
58523      */
58524     setDataIndex : function(col, dataIndex){
58525         this.config[col].dataIndex = dataIndex;
58526     },
58527
58528     
58529     
58530     /**
58531      * Returns true if the cell is editable.
58532      * @param {Number} colIndex The column index
58533      * @param {Number} rowIndex The row index - this is nto actually used..?
58534      * @return {Boolean}
58535      */
58536     isCellEditable : function(colIndex, rowIndex){
58537         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58538     },
58539
58540     /**
58541      * Returns the editor defined for the cell/column.
58542      * return false or null to disable editing.
58543      * @param {Number} colIndex The column index
58544      * @param {Number} rowIndex The row index
58545      * @return {Object}
58546      */
58547     getCellEditor : function(colIndex, rowIndex){
58548         return this.config[colIndex].editor;
58549     },
58550
58551     /**
58552      * Sets if a column is editable.
58553      * @param {Number} col The column index
58554      * @param {Boolean} editable True if the column is editable
58555      */
58556     setEditable : function(col, editable){
58557         this.config[col].editable = editable;
58558     },
58559
58560
58561     /**
58562      * Returns true if the column is hidden.
58563      * @param {Number} colIndex The column index
58564      * @return {Boolean}
58565      */
58566     isHidden : function(colIndex){
58567         return this.config[colIndex].hidden;
58568     },
58569
58570
58571     /**
58572      * Returns true if the column width cannot be changed
58573      */
58574     isFixed : function(colIndex){
58575         return this.config[colIndex].fixed;
58576     },
58577
58578     /**
58579      * Returns true if the column can be resized
58580      * @return {Boolean}
58581      */
58582     isResizable : function(colIndex){
58583         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58584     },
58585     /**
58586      * Sets if a column is hidden.
58587      * @param {Number} colIndex The column index
58588      * @param {Boolean} hidden True if the column is hidden
58589      */
58590     setHidden : function(colIndex, hidden){
58591         this.config[colIndex].hidden = hidden;
58592         this.totalWidth = null;
58593         this.fireEvent("hiddenchange", this, colIndex, hidden);
58594     },
58595
58596     /**
58597      * Sets the editor for a column.
58598      * @param {Number} col The column index
58599      * @param {Object} editor The editor object
58600      */
58601     setEditor : function(col, editor){
58602         this.config[col].editor = editor;
58603     }
58604 });
58605
58606 Roo.grid.ColumnModel.defaultRenderer = function(value)
58607 {
58608     if(typeof value == "object") {
58609         return value;
58610     }
58611         if(typeof value == "string" && value.length < 1){
58612             return "&#160;";
58613         }
58614     
58615         return String.format("{0}", value);
58616 };
58617
58618 // Alias for backwards compatibility
58619 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58620 /*
58621  * Based on:
58622  * Ext JS Library 1.1.1
58623  * Copyright(c) 2006-2007, Ext JS, LLC.
58624  *
58625  * Originally Released Under LGPL - original licence link has changed is not relivant.
58626  *
58627  * Fork - LGPL
58628  * <script type="text/javascript">
58629  */
58630
58631 /**
58632  * @class Roo.grid.AbstractSelectionModel
58633  * @extends Roo.util.Observable
58634  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58635  * implemented by descendant classes.  This class should not be directly instantiated.
58636  * @constructor
58637  */
58638 Roo.grid.AbstractSelectionModel = function(){
58639     this.locked = false;
58640     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58641 };
58642
58643 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58644     /** @ignore Called by the grid automatically. Do not call directly. */
58645     init : function(grid){
58646         this.grid = grid;
58647         this.initEvents();
58648     },
58649
58650     /**
58651      * Locks the selections.
58652      */
58653     lock : function(){
58654         this.locked = true;
58655     },
58656
58657     /**
58658      * Unlocks the selections.
58659      */
58660     unlock : function(){
58661         this.locked = false;
58662     },
58663
58664     /**
58665      * Returns true if the selections are locked.
58666      * @return {Boolean}
58667      */
58668     isLocked : function(){
58669         return this.locked;
58670     }
58671 });/*
58672  * Based on:
58673  * Ext JS Library 1.1.1
58674  * Copyright(c) 2006-2007, Ext JS, LLC.
58675  *
58676  * Originally Released Under LGPL - original licence link has changed is not relivant.
58677  *
58678  * Fork - LGPL
58679  * <script type="text/javascript">
58680  */
58681 /**
58682  * @extends Roo.grid.AbstractSelectionModel
58683  * @class Roo.grid.RowSelectionModel
58684  * The default SelectionModel used by {@link Roo.grid.Grid}.
58685  * It supports multiple selections and keyboard selection/navigation. 
58686  * @constructor
58687  * @param {Object} config
58688  */
58689 Roo.grid.RowSelectionModel = function(config){
58690     Roo.apply(this, config);
58691     this.selections = new Roo.util.MixedCollection(false, function(o){
58692         return o.id;
58693     });
58694
58695     this.last = false;
58696     this.lastActive = false;
58697
58698     this.addEvents({
58699         /**
58700              * @event selectionchange
58701              * Fires when the selection changes
58702              * @param {SelectionModel} this
58703              */
58704             "selectionchange" : true,
58705         /**
58706              * @event afterselectionchange
58707              * Fires after the selection changes (eg. by key press or clicking)
58708              * @param {SelectionModel} this
58709              */
58710             "afterselectionchange" : true,
58711         /**
58712              * @event beforerowselect
58713              * Fires when a row is selected being selected, return false to cancel.
58714              * @param {SelectionModel} this
58715              * @param {Number} rowIndex The selected index
58716              * @param {Boolean} keepExisting False if other selections will be cleared
58717              */
58718             "beforerowselect" : true,
58719         /**
58720              * @event rowselect
58721              * Fires when a row is selected.
58722              * @param {SelectionModel} this
58723              * @param {Number} rowIndex The selected index
58724              * @param {Roo.data.Record} r The record
58725              */
58726             "rowselect" : true,
58727         /**
58728              * @event rowdeselect
58729              * Fires when a row is deselected.
58730              * @param {SelectionModel} this
58731              * @param {Number} rowIndex The selected index
58732              */
58733         "rowdeselect" : true
58734     });
58735     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58736     this.locked = false;
58737 };
58738
58739 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58740     /**
58741      * @cfg {Boolean} singleSelect
58742      * True to allow selection of only one row at a time (defaults to false)
58743      */
58744     singleSelect : false,
58745
58746     // private
58747     initEvents : function(){
58748
58749         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58750             this.grid.on("mousedown", this.handleMouseDown, this);
58751         }else{ // allow click to work like normal
58752             this.grid.on("rowclick", this.handleDragableRowClick, this);
58753         }
58754
58755         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58756             "up" : function(e){
58757                 if(!e.shiftKey){
58758                     this.selectPrevious(e.shiftKey);
58759                 }else if(this.last !== false && this.lastActive !== false){
58760                     var last = this.last;
58761                     this.selectRange(this.last,  this.lastActive-1);
58762                     this.grid.getView().focusRow(this.lastActive);
58763                     if(last !== false){
58764                         this.last = last;
58765                     }
58766                 }else{
58767                     this.selectFirstRow();
58768                 }
58769                 this.fireEvent("afterselectionchange", this);
58770             },
58771             "down" : function(e){
58772                 if(!e.shiftKey){
58773                     this.selectNext(e.shiftKey);
58774                 }else if(this.last !== false && this.lastActive !== false){
58775                     var last = this.last;
58776                     this.selectRange(this.last,  this.lastActive+1);
58777                     this.grid.getView().focusRow(this.lastActive);
58778                     if(last !== false){
58779                         this.last = last;
58780                     }
58781                 }else{
58782                     this.selectFirstRow();
58783                 }
58784                 this.fireEvent("afterselectionchange", this);
58785             },
58786             scope: this
58787         });
58788
58789         var view = this.grid.view;
58790         view.on("refresh", this.onRefresh, this);
58791         view.on("rowupdated", this.onRowUpdated, this);
58792         view.on("rowremoved", this.onRemove, this);
58793     },
58794
58795     // private
58796     onRefresh : function(){
58797         var ds = this.grid.dataSource, i, v = this.grid.view;
58798         var s = this.selections;
58799         s.each(function(r){
58800             if((i = ds.indexOfId(r.id)) != -1){
58801                 v.onRowSelect(i);
58802                 s.add(ds.getAt(i)); // updating the selection relate data
58803             }else{
58804                 s.remove(r);
58805             }
58806         });
58807     },
58808
58809     // private
58810     onRemove : function(v, index, r){
58811         this.selections.remove(r);
58812     },
58813
58814     // private
58815     onRowUpdated : function(v, index, r){
58816         if(this.isSelected(r)){
58817             v.onRowSelect(index);
58818         }
58819     },
58820
58821     /**
58822      * Select records.
58823      * @param {Array} records The records to select
58824      * @param {Boolean} keepExisting (optional) True to keep existing selections
58825      */
58826     selectRecords : function(records, keepExisting){
58827         if(!keepExisting){
58828             this.clearSelections();
58829         }
58830         var ds = this.grid.dataSource;
58831         for(var i = 0, len = records.length; i < len; i++){
58832             this.selectRow(ds.indexOf(records[i]), true);
58833         }
58834     },
58835
58836     /**
58837      * Gets the number of selected rows.
58838      * @return {Number}
58839      */
58840     getCount : function(){
58841         return this.selections.length;
58842     },
58843
58844     /**
58845      * Selects the first row in the grid.
58846      */
58847     selectFirstRow : function(){
58848         this.selectRow(0);
58849     },
58850
58851     /**
58852      * Select the last row.
58853      * @param {Boolean} keepExisting (optional) True to keep existing selections
58854      */
58855     selectLastRow : function(keepExisting){
58856         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58857     },
58858
58859     /**
58860      * Selects the row immediately following the last selected row.
58861      * @param {Boolean} keepExisting (optional) True to keep existing selections
58862      */
58863     selectNext : function(keepExisting){
58864         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58865             this.selectRow(this.last+1, keepExisting);
58866             this.grid.getView().focusRow(this.last);
58867         }
58868     },
58869
58870     /**
58871      * Selects the row that precedes the last selected row.
58872      * @param {Boolean} keepExisting (optional) True to keep existing selections
58873      */
58874     selectPrevious : function(keepExisting){
58875         if(this.last){
58876             this.selectRow(this.last-1, keepExisting);
58877             this.grid.getView().focusRow(this.last);
58878         }
58879     },
58880
58881     /**
58882      * Returns the selected records
58883      * @return {Array} Array of selected records
58884      */
58885     getSelections : function(){
58886         return [].concat(this.selections.items);
58887     },
58888
58889     /**
58890      * Returns the first selected record.
58891      * @return {Record}
58892      */
58893     getSelected : function(){
58894         return this.selections.itemAt(0);
58895     },
58896
58897
58898     /**
58899      * Clears all selections.
58900      */
58901     clearSelections : function(fast){
58902         if(this.locked) {
58903             return;
58904         }
58905         if(fast !== true){
58906             var ds = this.grid.dataSource;
58907             var s = this.selections;
58908             s.each(function(r){
58909                 this.deselectRow(ds.indexOfId(r.id));
58910             }, this);
58911             s.clear();
58912         }else{
58913             this.selections.clear();
58914         }
58915         this.last = false;
58916     },
58917
58918
58919     /**
58920      * Selects all rows.
58921      */
58922     selectAll : function(){
58923         if(this.locked) {
58924             return;
58925         }
58926         this.selections.clear();
58927         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58928             this.selectRow(i, true);
58929         }
58930     },
58931
58932     /**
58933      * Returns True if there is a selection.
58934      * @return {Boolean}
58935      */
58936     hasSelection : function(){
58937         return this.selections.length > 0;
58938     },
58939
58940     /**
58941      * Returns True if the specified row is selected.
58942      * @param {Number/Record} record The record or index of the record to check
58943      * @return {Boolean}
58944      */
58945     isSelected : function(index){
58946         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58947         return (r && this.selections.key(r.id) ? true : false);
58948     },
58949
58950     /**
58951      * Returns True if the specified record id is selected.
58952      * @param {String} id The id of record to check
58953      * @return {Boolean}
58954      */
58955     isIdSelected : function(id){
58956         return (this.selections.key(id) ? true : false);
58957     },
58958
58959     // private
58960     handleMouseDown : function(e, t){
58961         var view = this.grid.getView(), rowIndex;
58962         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58963             return;
58964         };
58965         if(e.shiftKey && this.last !== false){
58966             var last = this.last;
58967             this.selectRange(last, rowIndex, e.ctrlKey);
58968             this.last = last; // reset the last
58969             view.focusRow(rowIndex);
58970         }else{
58971             var isSelected = this.isSelected(rowIndex);
58972             if(e.button !== 0 && isSelected){
58973                 view.focusRow(rowIndex);
58974             }else if(e.ctrlKey && isSelected){
58975                 this.deselectRow(rowIndex);
58976             }else if(!isSelected){
58977                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58978                 view.focusRow(rowIndex);
58979             }
58980         }
58981         this.fireEvent("afterselectionchange", this);
58982     },
58983     // private
58984     handleDragableRowClick :  function(grid, rowIndex, e) 
58985     {
58986         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58987             this.selectRow(rowIndex, false);
58988             grid.view.focusRow(rowIndex);
58989              this.fireEvent("afterselectionchange", this);
58990         }
58991     },
58992     
58993     /**
58994      * Selects multiple rows.
58995      * @param {Array} rows Array of the indexes of the row to select
58996      * @param {Boolean} keepExisting (optional) True to keep existing selections
58997      */
58998     selectRows : function(rows, keepExisting){
58999         if(!keepExisting){
59000             this.clearSelections();
59001         }
59002         for(var i = 0, len = rows.length; i < len; i++){
59003             this.selectRow(rows[i], true);
59004         }
59005     },
59006
59007     /**
59008      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59009      * @param {Number} startRow The index of the first row in the range
59010      * @param {Number} endRow The index of the last row in the range
59011      * @param {Boolean} keepExisting (optional) True to retain existing selections
59012      */
59013     selectRange : function(startRow, endRow, keepExisting){
59014         if(this.locked) {
59015             return;
59016         }
59017         if(!keepExisting){
59018             this.clearSelections();
59019         }
59020         if(startRow <= endRow){
59021             for(var i = startRow; i <= endRow; i++){
59022                 this.selectRow(i, true);
59023             }
59024         }else{
59025             for(var i = startRow; i >= endRow; i--){
59026                 this.selectRow(i, true);
59027             }
59028         }
59029     },
59030
59031     /**
59032      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59033      * @param {Number} startRow The index of the first row in the range
59034      * @param {Number} endRow The index of the last row in the range
59035      */
59036     deselectRange : function(startRow, endRow, preventViewNotify){
59037         if(this.locked) {
59038             return;
59039         }
59040         for(var i = startRow; i <= endRow; i++){
59041             this.deselectRow(i, preventViewNotify);
59042         }
59043     },
59044
59045     /**
59046      * Selects a row.
59047      * @param {Number} row The index of the row to select
59048      * @param {Boolean} keepExisting (optional) True to keep existing selections
59049      */
59050     selectRow : function(index, keepExisting, preventViewNotify){
59051         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59052             return;
59053         }
59054         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59055             if(!keepExisting || this.singleSelect){
59056                 this.clearSelections();
59057             }
59058             var r = this.grid.dataSource.getAt(index);
59059             this.selections.add(r);
59060             this.last = this.lastActive = index;
59061             if(!preventViewNotify){
59062                 this.grid.getView().onRowSelect(index);
59063             }
59064             this.fireEvent("rowselect", this, index, r);
59065             this.fireEvent("selectionchange", this);
59066         }
59067     },
59068
59069     /**
59070      * Deselects a row.
59071      * @param {Number} row The index of the row to deselect
59072      */
59073     deselectRow : function(index, preventViewNotify){
59074         if(this.locked) {
59075             return;
59076         }
59077         if(this.last == index){
59078             this.last = false;
59079         }
59080         if(this.lastActive == index){
59081             this.lastActive = false;
59082         }
59083         var r = this.grid.dataSource.getAt(index);
59084         this.selections.remove(r);
59085         if(!preventViewNotify){
59086             this.grid.getView().onRowDeselect(index);
59087         }
59088         this.fireEvent("rowdeselect", this, index);
59089         this.fireEvent("selectionchange", this);
59090     },
59091
59092     // private
59093     restoreLast : function(){
59094         if(this._last){
59095             this.last = this._last;
59096         }
59097     },
59098
59099     // private
59100     acceptsNav : function(row, col, cm){
59101         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59102     },
59103
59104     // private
59105     onEditorKey : function(field, e){
59106         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59107         if(k == e.TAB){
59108             e.stopEvent();
59109             ed.completeEdit();
59110             if(e.shiftKey){
59111                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59112             }else{
59113                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59114             }
59115         }else if(k == e.ENTER && !e.ctrlKey){
59116             e.stopEvent();
59117             ed.completeEdit();
59118             if(e.shiftKey){
59119                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59120             }else{
59121                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59122             }
59123         }else if(k == e.ESC){
59124             ed.cancelEdit();
59125         }
59126         if(newCell){
59127             g.startEditing(newCell[0], newCell[1]);
59128         }
59129     }
59130 });/*
59131  * Based on:
59132  * Ext JS Library 1.1.1
59133  * Copyright(c) 2006-2007, Ext JS, LLC.
59134  *
59135  * Originally Released Under LGPL - original licence link has changed is not relivant.
59136  *
59137  * Fork - LGPL
59138  * <script type="text/javascript">
59139  */
59140 /**
59141  * @class Roo.grid.CellSelectionModel
59142  * @extends Roo.grid.AbstractSelectionModel
59143  * This class provides the basic implementation for cell selection in a grid.
59144  * @constructor
59145  * @param {Object} config The object containing the configuration of this model.
59146  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59147  */
59148 Roo.grid.CellSelectionModel = function(config){
59149     Roo.apply(this, config);
59150
59151     this.selection = null;
59152
59153     this.addEvents({
59154         /**
59155              * @event beforerowselect
59156              * Fires before a cell is selected.
59157              * @param {SelectionModel} this
59158              * @param {Number} rowIndex The selected row index
59159              * @param {Number} colIndex The selected cell index
59160              */
59161             "beforecellselect" : true,
59162         /**
59163              * @event cellselect
59164              * Fires when a cell is selected.
59165              * @param {SelectionModel} this
59166              * @param {Number} rowIndex The selected row index
59167              * @param {Number} colIndex The selected cell index
59168              */
59169             "cellselect" : true,
59170         /**
59171              * @event selectionchange
59172              * Fires when the active selection changes.
59173              * @param {SelectionModel} this
59174              * @param {Object} selection null for no selection or an object (o) with two properties
59175                 <ul>
59176                 <li>o.record: the record object for the row the selection is in</li>
59177                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59178                 </ul>
59179              */
59180             "selectionchange" : true,
59181         /**
59182              * @event tabend
59183              * Fires when the tab (or enter) was pressed on the last editable cell
59184              * You can use this to trigger add new row.
59185              * @param {SelectionModel} this
59186              */
59187             "tabend" : true,
59188          /**
59189              * @event beforeeditnext
59190              * Fires before the next editable sell is made active
59191              * You can use this to skip to another cell or fire the tabend
59192              *    if you set cell to false
59193              * @param {Object} eventdata object : { cell : [ row, col ] } 
59194              */
59195             "beforeeditnext" : true
59196     });
59197     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59198 };
59199
59200 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59201     
59202     enter_is_tab: false,
59203
59204     /** @ignore */
59205     initEvents : function(){
59206         this.grid.on("mousedown", this.handleMouseDown, this);
59207         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59208         var view = this.grid.view;
59209         view.on("refresh", this.onViewChange, this);
59210         view.on("rowupdated", this.onRowUpdated, this);
59211         view.on("beforerowremoved", this.clearSelections, this);
59212         view.on("beforerowsinserted", this.clearSelections, this);
59213         if(this.grid.isEditor){
59214             this.grid.on("beforeedit", this.beforeEdit,  this);
59215         }
59216     },
59217
59218         //private
59219     beforeEdit : function(e){
59220         this.select(e.row, e.column, false, true, e.record);
59221     },
59222
59223         //private
59224     onRowUpdated : function(v, index, r){
59225         if(this.selection && this.selection.record == r){
59226             v.onCellSelect(index, this.selection.cell[1]);
59227         }
59228     },
59229
59230         //private
59231     onViewChange : function(){
59232         this.clearSelections(true);
59233     },
59234
59235         /**
59236          * Returns the currently selected cell,.
59237          * @return {Array} The selected cell (row, column) or null if none selected.
59238          */
59239     getSelectedCell : function(){
59240         return this.selection ? this.selection.cell : null;
59241     },
59242
59243     /**
59244      * Clears all selections.
59245      * @param {Boolean} true to prevent the gridview from being notified about the change.
59246      */
59247     clearSelections : function(preventNotify){
59248         var s = this.selection;
59249         if(s){
59250             if(preventNotify !== true){
59251                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59252             }
59253             this.selection = null;
59254             this.fireEvent("selectionchange", this, null);
59255         }
59256     },
59257
59258     /**
59259      * Returns true if there is a selection.
59260      * @return {Boolean}
59261      */
59262     hasSelection : function(){
59263         return this.selection ? true : false;
59264     },
59265
59266     /** @ignore */
59267     handleMouseDown : function(e, t){
59268         var v = this.grid.getView();
59269         if(this.isLocked()){
59270             return;
59271         };
59272         var row = v.findRowIndex(t);
59273         var cell = v.findCellIndex(t);
59274         if(row !== false && cell !== false){
59275             this.select(row, cell);
59276         }
59277     },
59278
59279     /**
59280      * Selects a cell.
59281      * @param {Number} rowIndex
59282      * @param {Number} collIndex
59283      */
59284     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59285         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59286             this.clearSelections();
59287             r = r || this.grid.dataSource.getAt(rowIndex);
59288             this.selection = {
59289                 record : r,
59290                 cell : [rowIndex, colIndex]
59291             };
59292             if(!preventViewNotify){
59293                 var v = this.grid.getView();
59294                 v.onCellSelect(rowIndex, colIndex);
59295                 if(preventFocus !== true){
59296                     v.focusCell(rowIndex, colIndex);
59297                 }
59298             }
59299             this.fireEvent("cellselect", this, rowIndex, colIndex);
59300             this.fireEvent("selectionchange", this, this.selection);
59301         }
59302     },
59303
59304         //private
59305     isSelectable : function(rowIndex, colIndex, cm){
59306         return !cm.isHidden(colIndex);
59307     },
59308
59309     /** @ignore */
59310     handleKeyDown : function(e){
59311         //Roo.log('Cell Sel Model handleKeyDown');
59312         if(!e.isNavKeyPress()){
59313             return;
59314         }
59315         var g = this.grid, s = this.selection;
59316         if(!s){
59317             e.stopEvent();
59318             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59319             if(cell){
59320                 this.select(cell[0], cell[1]);
59321             }
59322             return;
59323         }
59324         var sm = this;
59325         var walk = function(row, col, step){
59326             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59327         };
59328         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59329         var newCell;
59330
59331       
59332
59333         switch(k){
59334             case e.TAB:
59335                 // handled by onEditorKey
59336                 if (g.isEditor && g.editing) {
59337                     return;
59338                 }
59339                 if(e.shiftKey) {
59340                     newCell = walk(r, c-1, -1);
59341                 } else {
59342                     newCell = walk(r, c+1, 1);
59343                 }
59344                 break;
59345             
59346             case e.DOWN:
59347                newCell = walk(r+1, c, 1);
59348                 break;
59349             
59350             case e.UP:
59351                 newCell = walk(r-1, c, -1);
59352                 break;
59353             
59354             case e.RIGHT:
59355                 newCell = walk(r, c+1, 1);
59356                 break;
59357             
59358             case e.LEFT:
59359                 newCell = walk(r, c-1, -1);
59360                 break;
59361             
59362             case e.ENTER:
59363                 
59364                 if(g.isEditor && !g.editing){
59365                    g.startEditing(r, c);
59366                    e.stopEvent();
59367                    return;
59368                 }
59369                 
59370                 
59371              break;
59372         };
59373         if(newCell){
59374             this.select(newCell[0], newCell[1]);
59375             e.stopEvent();
59376             
59377         }
59378     },
59379
59380     acceptsNav : function(row, col, cm){
59381         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59382     },
59383     /**
59384      * Selects a cell.
59385      * @param {Number} field (not used) - as it's normally used as a listener
59386      * @param {Number} e - event - fake it by using
59387      *
59388      * var e = Roo.EventObjectImpl.prototype;
59389      * e.keyCode = e.TAB
59390      *
59391      * 
59392      */
59393     onEditorKey : function(field, e){
59394         
59395         var k = e.getKey(),
59396             newCell,
59397             g = this.grid,
59398             ed = g.activeEditor,
59399             forward = false;
59400         ///Roo.log('onEditorKey' + k);
59401         
59402         
59403         if (this.enter_is_tab && k == e.ENTER) {
59404             k = e.TAB;
59405         }
59406         
59407         if(k == e.TAB){
59408             if(e.shiftKey){
59409                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59410             }else{
59411                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59412                 forward = true;
59413             }
59414             
59415             e.stopEvent();
59416             
59417         } else if(k == e.ENTER &&  !e.ctrlKey){
59418             ed.completeEdit();
59419             e.stopEvent();
59420             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59421         
59422                 } else if(k == e.ESC){
59423             ed.cancelEdit();
59424         }
59425                 
59426         if (newCell) {
59427             var ecall = { cell : newCell, forward : forward };
59428             this.fireEvent('beforeeditnext', ecall );
59429             newCell = ecall.cell;
59430                         forward = ecall.forward;
59431         }
59432                 
59433         if(newCell){
59434             //Roo.log('next cell after edit');
59435             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59436         } else if (forward) {
59437             // tabbed past last
59438             this.fireEvent.defer(100, this, ['tabend',this]);
59439         }
59440     }
59441 });/*
59442  * Based on:
59443  * Ext JS Library 1.1.1
59444  * Copyright(c) 2006-2007, Ext JS, LLC.
59445  *
59446  * Originally Released Under LGPL - original licence link has changed is not relivant.
59447  *
59448  * Fork - LGPL
59449  * <script type="text/javascript">
59450  */
59451  
59452 /**
59453  * @class Roo.grid.EditorGrid
59454  * @extends Roo.grid.Grid
59455  * Class for creating and editable grid.
59456  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59457  * The container MUST have some type of size defined for the grid to fill. The container will be 
59458  * automatically set to position relative if it isn't already.
59459  * @param {Object} dataSource The data model to bind to
59460  * @param {Object} colModel The column model with info about this grid's columns
59461  */
59462 Roo.grid.EditorGrid = function(container, config){
59463     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59464     this.getGridEl().addClass("xedit-grid");
59465
59466     if(!this.selModel){
59467         this.selModel = new Roo.grid.CellSelectionModel();
59468     }
59469
59470     this.activeEditor = null;
59471
59472         this.addEvents({
59473             /**
59474              * @event beforeedit
59475              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59476              * <ul style="padding:5px;padding-left:16px;">
59477              * <li>grid - This grid</li>
59478              * <li>record - The record being edited</li>
59479              * <li>field - The field name being edited</li>
59480              * <li>value - The value for the field being edited.</li>
59481              * <li>row - The grid row index</li>
59482              * <li>column - The grid column index</li>
59483              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59484              * </ul>
59485              * @param {Object} e An edit event (see above for description)
59486              */
59487             "beforeedit" : true,
59488             /**
59489              * @event afteredit
59490              * Fires after a cell is edited. <br />
59491              * <ul style="padding:5px;padding-left:16px;">
59492              * <li>grid - This grid</li>
59493              * <li>record - The record being edited</li>
59494              * <li>field - The field name being edited</li>
59495              * <li>value - The value being set</li>
59496              * <li>originalValue - The original value for the field, before the edit.</li>
59497              * <li>row - The grid row index</li>
59498              * <li>column - The grid column index</li>
59499              * </ul>
59500              * @param {Object} e An edit event (see above for description)
59501              */
59502             "afteredit" : true,
59503             /**
59504              * @event validateedit
59505              * Fires after a cell is edited, but before the value is set in the record. 
59506          * You can use this to modify the value being set in the field, Return false
59507              * to cancel the change. The edit event object has the following properties <br />
59508              * <ul style="padding:5px;padding-left:16px;">
59509          * <li>editor - This editor</li>
59510              * <li>grid - This grid</li>
59511              * <li>record - The record being edited</li>
59512              * <li>field - The field name being edited</li>
59513              * <li>value - The value being set</li>
59514              * <li>originalValue - The original value for the field, before the edit.</li>
59515              * <li>row - The grid row index</li>
59516              * <li>column - The grid column index</li>
59517              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59518              * </ul>
59519              * @param {Object} e An edit event (see above for description)
59520              */
59521             "validateedit" : true
59522         });
59523     this.on("bodyscroll", this.stopEditing,  this);
59524     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59525 };
59526
59527 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59528     /**
59529      * @cfg {Number} clicksToEdit
59530      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59531      */
59532     clicksToEdit: 2,
59533
59534     // private
59535     isEditor : true,
59536     // private
59537     trackMouseOver: false, // causes very odd FF errors
59538
59539     onCellDblClick : function(g, row, col){
59540         this.startEditing(row, col);
59541     },
59542
59543     onEditComplete : function(ed, value, startValue){
59544         this.editing = false;
59545         this.activeEditor = null;
59546         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59547         var r = ed.record;
59548         var field = this.colModel.getDataIndex(ed.col);
59549         var e = {
59550             grid: this,
59551             record: r,
59552             field: field,
59553             originalValue: startValue,
59554             value: value,
59555             row: ed.row,
59556             column: ed.col,
59557             cancel:false,
59558             editor: ed
59559         };
59560         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59561         cell.show();
59562           
59563         if(String(value) !== String(startValue)){
59564             
59565             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59566                 r.set(field, e.value);
59567                 // if we are dealing with a combo box..
59568                 // then we also set the 'name' colum to be the displayField
59569                 if (ed.field.displayField && ed.field.name) {
59570                     r.set(ed.field.name, ed.field.el.dom.value);
59571                 }
59572                 
59573                 delete e.cancel; //?? why!!!
59574                 this.fireEvent("afteredit", e);
59575             }
59576         } else {
59577             this.fireEvent("afteredit", e); // always fire it!
59578         }
59579         this.view.focusCell(ed.row, ed.col);
59580     },
59581
59582     /**
59583      * Starts editing the specified for the specified row/column
59584      * @param {Number} rowIndex
59585      * @param {Number} colIndex
59586      */
59587     startEditing : function(row, col){
59588         this.stopEditing();
59589         if(this.colModel.isCellEditable(col, row)){
59590             this.view.ensureVisible(row, col, true);
59591           
59592             var r = this.dataSource.getAt(row);
59593             var field = this.colModel.getDataIndex(col);
59594             var cell = Roo.get(this.view.getCell(row,col));
59595             var e = {
59596                 grid: this,
59597                 record: r,
59598                 field: field,
59599                 value: r.data[field],
59600                 row: row,
59601                 column: col,
59602                 cancel:false 
59603             };
59604             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59605                 this.editing = true;
59606                 var ed = this.colModel.getCellEditor(col, row);
59607                 
59608                 if (!ed) {
59609                     return;
59610                 }
59611                 if(!ed.rendered){
59612                     ed.render(ed.parentEl || document.body);
59613                 }
59614                 ed.field.reset();
59615                
59616                 cell.hide();
59617                 
59618                 (function(){ // complex but required for focus issues in safari, ie and opera
59619                     ed.row = row;
59620                     ed.col = col;
59621                     ed.record = r;
59622                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59623                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59624                     this.activeEditor = ed;
59625                     var v = r.data[field];
59626                     ed.startEdit(this.view.getCell(row, col), v);
59627                     // combo's with 'displayField and name set
59628                     if (ed.field.displayField && ed.field.name) {
59629                         ed.field.el.dom.value = r.data[ed.field.name];
59630                     }
59631                     
59632                     
59633                 }).defer(50, this);
59634             }
59635         }
59636     },
59637         
59638     /**
59639      * Stops any active editing
59640      */
59641     stopEditing : function(){
59642         if(this.activeEditor){
59643             this.activeEditor.completeEdit();
59644         }
59645         this.activeEditor = null;
59646     },
59647         
59648          /**
59649      * Called to get grid's drag proxy text, by default returns this.ddText.
59650      * @return {String}
59651      */
59652     getDragDropText : function(){
59653         var count = this.selModel.getSelectedCell() ? 1 : 0;
59654         return String.format(this.ddText, count, count == 1 ? '' : 's');
59655     }
59656         
59657 });/*
59658  * Based on:
59659  * Ext JS Library 1.1.1
59660  * Copyright(c) 2006-2007, Ext JS, LLC.
59661  *
59662  * Originally Released Under LGPL - original licence link has changed is not relivant.
59663  *
59664  * Fork - LGPL
59665  * <script type="text/javascript">
59666  */
59667
59668 // private - not really -- you end up using it !
59669 // This is a support class used internally by the Grid components
59670
59671 /**
59672  * @class Roo.grid.GridEditor
59673  * @extends Roo.Editor
59674  * Class for creating and editable grid elements.
59675  * @param {Object} config any settings (must include field)
59676  */
59677 Roo.grid.GridEditor = function(field, config){
59678     if (!config && field.field) {
59679         config = field;
59680         field = Roo.factory(config.field, Roo.form);
59681     }
59682     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59683     field.monitorTab = false;
59684 };
59685
59686 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59687     
59688     /**
59689      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59690      */
59691     
59692     alignment: "tl-tl",
59693     autoSize: "width",
59694     hideEl : false,
59695     cls: "x-small-editor x-grid-editor",
59696     shim:false,
59697     shadow:"frame"
59698 });/*
59699  * Based on:
59700  * Ext JS Library 1.1.1
59701  * Copyright(c) 2006-2007, Ext JS, LLC.
59702  *
59703  * Originally Released Under LGPL - original licence link has changed is not relivant.
59704  *
59705  * Fork - LGPL
59706  * <script type="text/javascript">
59707  */
59708   
59709
59710   
59711 Roo.grid.PropertyRecord = Roo.data.Record.create([
59712     {name:'name',type:'string'},  'value'
59713 ]);
59714
59715
59716 Roo.grid.PropertyStore = function(grid, source){
59717     this.grid = grid;
59718     this.store = new Roo.data.Store({
59719         recordType : Roo.grid.PropertyRecord
59720     });
59721     this.store.on('update', this.onUpdate,  this);
59722     if(source){
59723         this.setSource(source);
59724     }
59725     Roo.grid.PropertyStore.superclass.constructor.call(this);
59726 };
59727
59728
59729
59730 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59731     setSource : function(o){
59732         this.source = o;
59733         this.store.removeAll();
59734         var data = [];
59735         for(var k in o){
59736             if(this.isEditableValue(o[k])){
59737                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59738             }
59739         }
59740         this.store.loadRecords({records: data}, {}, true);
59741     },
59742
59743     onUpdate : function(ds, record, type){
59744         if(type == Roo.data.Record.EDIT){
59745             var v = record.data['value'];
59746             var oldValue = record.modified['value'];
59747             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59748                 this.source[record.id] = v;
59749                 record.commit();
59750                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59751             }else{
59752                 record.reject();
59753             }
59754         }
59755     },
59756
59757     getProperty : function(row){
59758        return this.store.getAt(row);
59759     },
59760
59761     isEditableValue: function(val){
59762         if(val && val instanceof Date){
59763             return true;
59764         }else if(typeof val == 'object' || typeof val == 'function'){
59765             return false;
59766         }
59767         return true;
59768     },
59769
59770     setValue : function(prop, value){
59771         this.source[prop] = value;
59772         this.store.getById(prop).set('value', value);
59773     },
59774
59775     getSource : function(){
59776         return this.source;
59777     }
59778 });
59779
59780 Roo.grid.PropertyColumnModel = function(grid, store){
59781     this.grid = grid;
59782     var g = Roo.grid;
59783     g.PropertyColumnModel.superclass.constructor.call(this, [
59784         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59785         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59786     ]);
59787     this.store = store;
59788     this.bselect = Roo.DomHelper.append(document.body, {
59789         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59790             {tag: 'option', value: 'true', html: 'true'},
59791             {tag: 'option', value: 'false', html: 'false'}
59792         ]
59793     });
59794     Roo.id(this.bselect);
59795     var f = Roo.form;
59796     this.editors = {
59797         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59798         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59799         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59800         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59801         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59802     };
59803     this.renderCellDelegate = this.renderCell.createDelegate(this);
59804     this.renderPropDelegate = this.renderProp.createDelegate(this);
59805 };
59806
59807 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59808     
59809     
59810     nameText : 'Name',
59811     valueText : 'Value',
59812     
59813     dateFormat : 'm/j/Y',
59814     
59815     
59816     renderDate : function(dateVal){
59817         return dateVal.dateFormat(this.dateFormat);
59818     },
59819
59820     renderBool : function(bVal){
59821         return bVal ? 'true' : 'false';
59822     },
59823
59824     isCellEditable : function(colIndex, rowIndex){
59825         return colIndex == 1;
59826     },
59827
59828     getRenderer : function(col){
59829         return col == 1 ?
59830             this.renderCellDelegate : this.renderPropDelegate;
59831     },
59832
59833     renderProp : function(v){
59834         return this.getPropertyName(v);
59835     },
59836
59837     renderCell : function(val){
59838         var rv = val;
59839         if(val instanceof Date){
59840             rv = this.renderDate(val);
59841         }else if(typeof val == 'boolean'){
59842             rv = this.renderBool(val);
59843         }
59844         return Roo.util.Format.htmlEncode(rv);
59845     },
59846
59847     getPropertyName : function(name){
59848         var pn = this.grid.propertyNames;
59849         return pn && pn[name] ? pn[name] : name;
59850     },
59851
59852     getCellEditor : function(colIndex, rowIndex){
59853         var p = this.store.getProperty(rowIndex);
59854         var n = p.data['name'], val = p.data['value'];
59855         
59856         if(typeof(this.grid.customEditors[n]) == 'string'){
59857             return this.editors[this.grid.customEditors[n]];
59858         }
59859         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59860             return this.grid.customEditors[n];
59861         }
59862         if(val instanceof Date){
59863             return this.editors['date'];
59864         }else if(typeof val == 'number'){
59865             return this.editors['number'];
59866         }else if(typeof val == 'boolean'){
59867             return this.editors['boolean'];
59868         }else{
59869             return this.editors['string'];
59870         }
59871     }
59872 });
59873
59874 /**
59875  * @class Roo.grid.PropertyGrid
59876  * @extends Roo.grid.EditorGrid
59877  * This class represents the  interface of a component based property grid control.
59878  * <br><br>Usage:<pre><code>
59879  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59880       
59881  });
59882  // set any options
59883  grid.render();
59884  * </code></pre>
59885   
59886  * @constructor
59887  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59888  * The container MUST have some type of size defined for the grid to fill. The container will be
59889  * automatically set to position relative if it isn't already.
59890  * @param {Object} config A config object that sets properties on this grid.
59891  */
59892 Roo.grid.PropertyGrid = function(container, config){
59893     config = config || {};
59894     var store = new Roo.grid.PropertyStore(this);
59895     this.store = store;
59896     var cm = new Roo.grid.PropertyColumnModel(this, store);
59897     store.store.sort('name', 'ASC');
59898     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59899         ds: store.store,
59900         cm: cm,
59901         enableColLock:false,
59902         enableColumnMove:false,
59903         stripeRows:false,
59904         trackMouseOver: false,
59905         clicksToEdit:1
59906     }, config));
59907     this.getGridEl().addClass('x-props-grid');
59908     this.lastEditRow = null;
59909     this.on('columnresize', this.onColumnResize, this);
59910     this.addEvents({
59911          /**
59912              * @event beforepropertychange
59913              * Fires before a property changes (return false to stop?)
59914              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59915              * @param {String} id Record Id
59916              * @param {String} newval New Value
59917          * @param {String} oldval Old Value
59918              */
59919         "beforepropertychange": true,
59920         /**
59921              * @event propertychange
59922              * Fires after a property changes
59923              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59924              * @param {String} id Record Id
59925              * @param {String} newval New Value
59926          * @param {String} oldval Old Value
59927              */
59928         "propertychange": true
59929     });
59930     this.customEditors = this.customEditors || {};
59931 };
59932 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59933     
59934      /**
59935      * @cfg {Object} customEditors map of colnames=> custom editors.
59936      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59937      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59938      * false disables editing of the field.
59939          */
59940     
59941       /**
59942      * @cfg {Object} propertyNames map of property Names to their displayed value
59943          */
59944     
59945     render : function(){
59946         Roo.grid.PropertyGrid.superclass.render.call(this);
59947         this.autoSize.defer(100, this);
59948     },
59949
59950     autoSize : function(){
59951         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59952         if(this.view){
59953             this.view.fitColumns();
59954         }
59955     },
59956
59957     onColumnResize : function(){
59958         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59959         this.autoSize();
59960     },
59961     /**
59962      * Sets the data for the Grid
59963      * accepts a Key => Value object of all the elements avaiable.
59964      * @param {Object} data  to appear in grid.
59965      */
59966     setSource : function(source){
59967         this.store.setSource(source);
59968         //this.autoSize();
59969     },
59970     /**
59971      * Gets all the data from the grid.
59972      * @return {Object} data  data stored in grid
59973      */
59974     getSource : function(){
59975         return this.store.getSource();
59976     }
59977 });/*
59978   
59979  * Licence LGPL
59980  
59981  */
59982  
59983 /**
59984  * @class Roo.grid.Calendar
59985  * @extends Roo.util.Grid
59986  * This class extends the Grid to provide a calendar widget
59987  * <br><br>Usage:<pre><code>
59988  var grid = new Roo.grid.Calendar("my-container-id", {
59989      ds: myDataStore,
59990      cm: myColModel,
59991      selModel: mySelectionModel,
59992      autoSizeColumns: true,
59993      monitorWindowResize: false,
59994      trackMouseOver: true
59995      eventstore : real data store..
59996  });
59997  // set any options
59998  grid.render();
59999   
60000   * @constructor
60001  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60002  * The container MUST have some type of size defined for the grid to fill. The container will be
60003  * automatically set to position relative if it isn't already.
60004  * @param {Object} config A config object that sets properties on this grid.
60005  */
60006 Roo.grid.Calendar = function(container, config){
60007         // initialize the container
60008         this.container = Roo.get(container);
60009         this.container.update("");
60010         this.container.setStyle("overflow", "hidden");
60011     this.container.addClass('x-grid-container');
60012
60013     this.id = this.container.id;
60014
60015     Roo.apply(this, config);
60016     // check and correct shorthanded configs
60017     
60018     var rows = [];
60019     var d =1;
60020     for (var r = 0;r < 6;r++) {
60021         
60022         rows[r]=[];
60023         for (var c =0;c < 7;c++) {
60024             rows[r][c]= '';
60025         }
60026     }
60027     if (this.eventStore) {
60028         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60029         this.eventStore.on('load',this.onLoad, this);
60030         this.eventStore.on('beforeload',this.clearEvents, this);
60031          
60032     }
60033     
60034     this.dataSource = new Roo.data.Store({
60035             proxy: new Roo.data.MemoryProxy(rows),
60036             reader: new Roo.data.ArrayReader({}, [
60037                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60038     });
60039
60040     this.dataSource.load();
60041     this.ds = this.dataSource;
60042     this.ds.xmodule = this.xmodule || false;
60043     
60044     
60045     var cellRender = function(v,x,r)
60046     {
60047         return String.format(
60048             '<div class="fc-day  fc-widget-content"><div>' +
60049                 '<div class="fc-event-container"></div>' +
60050                 '<div class="fc-day-number">{0}</div>'+
60051                 
60052                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60053             '</div></div>', v);
60054     
60055     }
60056     
60057     
60058     this.colModel = new Roo.grid.ColumnModel( [
60059         {
60060             xtype: 'ColumnModel',
60061             xns: Roo.grid,
60062             dataIndex : 'weekday0',
60063             header : 'Sunday',
60064             renderer : cellRender
60065         },
60066         {
60067             xtype: 'ColumnModel',
60068             xns: Roo.grid,
60069             dataIndex : 'weekday1',
60070             header : 'Monday',
60071             renderer : cellRender
60072         },
60073         {
60074             xtype: 'ColumnModel',
60075             xns: Roo.grid,
60076             dataIndex : 'weekday2',
60077             header : 'Tuesday',
60078             renderer : cellRender
60079         },
60080         {
60081             xtype: 'ColumnModel',
60082             xns: Roo.grid,
60083             dataIndex : 'weekday3',
60084             header : 'Wednesday',
60085             renderer : cellRender
60086         },
60087         {
60088             xtype: 'ColumnModel',
60089             xns: Roo.grid,
60090             dataIndex : 'weekday4',
60091             header : 'Thursday',
60092             renderer : cellRender
60093         },
60094         {
60095             xtype: 'ColumnModel',
60096             xns: Roo.grid,
60097             dataIndex : 'weekday5',
60098             header : 'Friday',
60099             renderer : cellRender
60100         },
60101         {
60102             xtype: 'ColumnModel',
60103             xns: Roo.grid,
60104             dataIndex : 'weekday6',
60105             header : 'Saturday',
60106             renderer : cellRender
60107         }
60108     ]);
60109     this.cm = this.colModel;
60110     this.cm.xmodule = this.xmodule || false;
60111  
60112         
60113           
60114     //this.selModel = new Roo.grid.CellSelectionModel();
60115     //this.sm = this.selModel;
60116     //this.selModel.init(this);
60117     
60118     
60119     if(this.width){
60120         this.container.setWidth(this.width);
60121     }
60122
60123     if(this.height){
60124         this.container.setHeight(this.height);
60125     }
60126     /** @private */
60127         this.addEvents({
60128         // raw events
60129         /**
60130          * @event click
60131          * The raw click event for the entire grid.
60132          * @param {Roo.EventObject} e
60133          */
60134         "click" : true,
60135         /**
60136          * @event dblclick
60137          * The raw dblclick event for the entire grid.
60138          * @param {Roo.EventObject} e
60139          */
60140         "dblclick" : true,
60141         /**
60142          * @event contextmenu
60143          * The raw contextmenu event for the entire grid.
60144          * @param {Roo.EventObject} e
60145          */
60146         "contextmenu" : true,
60147         /**
60148          * @event mousedown
60149          * The raw mousedown event for the entire grid.
60150          * @param {Roo.EventObject} e
60151          */
60152         "mousedown" : true,
60153         /**
60154          * @event mouseup
60155          * The raw mouseup event for the entire grid.
60156          * @param {Roo.EventObject} e
60157          */
60158         "mouseup" : true,
60159         /**
60160          * @event mouseover
60161          * The raw mouseover event for the entire grid.
60162          * @param {Roo.EventObject} e
60163          */
60164         "mouseover" : true,
60165         /**
60166          * @event mouseout
60167          * The raw mouseout event for the entire grid.
60168          * @param {Roo.EventObject} e
60169          */
60170         "mouseout" : true,
60171         /**
60172          * @event keypress
60173          * The raw keypress event for the entire grid.
60174          * @param {Roo.EventObject} e
60175          */
60176         "keypress" : true,
60177         /**
60178          * @event keydown
60179          * The raw keydown event for the entire grid.
60180          * @param {Roo.EventObject} e
60181          */
60182         "keydown" : true,
60183
60184         // custom events
60185
60186         /**
60187          * @event cellclick
60188          * Fires when a cell is clicked
60189          * @param {Grid} this
60190          * @param {Number} rowIndex
60191          * @param {Number} columnIndex
60192          * @param {Roo.EventObject} e
60193          */
60194         "cellclick" : true,
60195         /**
60196          * @event celldblclick
60197          * Fires when a cell is double clicked
60198          * @param {Grid} this
60199          * @param {Number} rowIndex
60200          * @param {Number} columnIndex
60201          * @param {Roo.EventObject} e
60202          */
60203         "celldblclick" : true,
60204         /**
60205          * @event rowclick
60206          * Fires when a row is clicked
60207          * @param {Grid} this
60208          * @param {Number} rowIndex
60209          * @param {Roo.EventObject} e
60210          */
60211         "rowclick" : true,
60212         /**
60213          * @event rowdblclick
60214          * Fires when a row is double clicked
60215          * @param {Grid} this
60216          * @param {Number} rowIndex
60217          * @param {Roo.EventObject} e
60218          */
60219         "rowdblclick" : true,
60220         /**
60221          * @event headerclick
60222          * Fires when a header is clicked
60223          * @param {Grid} this
60224          * @param {Number} columnIndex
60225          * @param {Roo.EventObject} e
60226          */
60227         "headerclick" : true,
60228         /**
60229          * @event headerdblclick
60230          * Fires when a header cell is double clicked
60231          * @param {Grid} this
60232          * @param {Number} columnIndex
60233          * @param {Roo.EventObject} e
60234          */
60235         "headerdblclick" : true,
60236         /**
60237          * @event rowcontextmenu
60238          * Fires when a row is right clicked
60239          * @param {Grid} this
60240          * @param {Number} rowIndex
60241          * @param {Roo.EventObject} e
60242          */
60243         "rowcontextmenu" : true,
60244         /**
60245          * @event cellcontextmenu
60246          * Fires when a cell is right clicked
60247          * @param {Grid} this
60248          * @param {Number} rowIndex
60249          * @param {Number} cellIndex
60250          * @param {Roo.EventObject} e
60251          */
60252          "cellcontextmenu" : true,
60253         /**
60254          * @event headercontextmenu
60255          * Fires when a header is right clicked
60256          * @param {Grid} this
60257          * @param {Number} columnIndex
60258          * @param {Roo.EventObject} e
60259          */
60260         "headercontextmenu" : true,
60261         /**
60262          * @event bodyscroll
60263          * Fires when the body element is scrolled
60264          * @param {Number} scrollLeft
60265          * @param {Number} scrollTop
60266          */
60267         "bodyscroll" : true,
60268         /**
60269          * @event columnresize
60270          * Fires when the user resizes a column
60271          * @param {Number} columnIndex
60272          * @param {Number} newSize
60273          */
60274         "columnresize" : true,
60275         /**
60276          * @event columnmove
60277          * Fires when the user moves a column
60278          * @param {Number} oldIndex
60279          * @param {Number} newIndex
60280          */
60281         "columnmove" : true,
60282         /**
60283          * @event startdrag
60284          * Fires when row(s) start being dragged
60285          * @param {Grid} this
60286          * @param {Roo.GridDD} dd The drag drop object
60287          * @param {event} e The raw browser event
60288          */
60289         "startdrag" : true,
60290         /**
60291          * @event enddrag
60292          * Fires when a drag operation is complete
60293          * @param {Grid} this
60294          * @param {Roo.GridDD} dd The drag drop object
60295          * @param {event} e The raw browser event
60296          */
60297         "enddrag" : true,
60298         /**
60299          * @event dragdrop
60300          * Fires when dragged row(s) are dropped on a valid DD target
60301          * @param {Grid} this
60302          * @param {Roo.GridDD} dd The drag drop object
60303          * @param {String} targetId The target drag drop object
60304          * @param {event} e The raw browser event
60305          */
60306         "dragdrop" : true,
60307         /**
60308          * @event dragover
60309          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60310          * @param {Grid} this
60311          * @param {Roo.GridDD} dd The drag drop object
60312          * @param {String} targetId The target drag drop object
60313          * @param {event} e The raw browser event
60314          */
60315         "dragover" : true,
60316         /**
60317          * @event dragenter
60318          *  Fires when the dragged row(s) first cross another DD target while being dragged
60319          * @param {Grid} this
60320          * @param {Roo.GridDD} dd The drag drop object
60321          * @param {String} targetId The target drag drop object
60322          * @param {event} e The raw browser event
60323          */
60324         "dragenter" : true,
60325         /**
60326          * @event dragout
60327          * Fires when the dragged row(s) leave another DD target while being dragged
60328          * @param {Grid} this
60329          * @param {Roo.GridDD} dd The drag drop object
60330          * @param {String} targetId The target drag drop object
60331          * @param {event} e The raw browser event
60332          */
60333         "dragout" : true,
60334         /**
60335          * @event rowclass
60336          * Fires when a row is rendered, so you can change add a style to it.
60337          * @param {GridView} gridview   The grid view
60338          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60339          */
60340         'rowclass' : true,
60341
60342         /**
60343          * @event render
60344          * Fires when the grid is rendered
60345          * @param {Grid} grid
60346          */
60347         'render' : true,
60348             /**
60349              * @event select
60350              * Fires when a date is selected
60351              * @param {DatePicker} this
60352              * @param {Date} date The selected date
60353              */
60354         'select': true,
60355         /**
60356              * @event monthchange
60357              * Fires when the displayed month changes 
60358              * @param {DatePicker} this
60359              * @param {Date} date The selected month
60360              */
60361         'monthchange': true,
60362         /**
60363              * @event evententer
60364              * Fires when mouse over an event
60365              * @param {Calendar} this
60366              * @param {event} Event
60367              */
60368         'evententer': true,
60369         /**
60370              * @event eventleave
60371              * Fires when the mouse leaves an
60372              * @param {Calendar} this
60373              * @param {event}
60374              */
60375         'eventleave': true,
60376         /**
60377              * @event eventclick
60378              * Fires when the mouse click an
60379              * @param {Calendar} this
60380              * @param {event}
60381              */
60382         'eventclick': true,
60383         /**
60384              * @event eventrender
60385              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60386              * @param {Calendar} this
60387              * @param {data} data to be modified
60388              */
60389         'eventrender': true
60390         
60391     });
60392
60393     Roo.grid.Grid.superclass.constructor.call(this);
60394     this.on('render', function() {
60395         this.view.el.addClass('x-grid-cal'); 
60396         
60397         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60398
60399     },this);
60400     
60401     if (!Roo.grid.Calendar.style) {
60402         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60403             
60404             
60405             '.x-grid-cal .x-grid-col' :  {
60406                 height: 'auto !important',
60407                 'vertical-align': 'top'
60408             },
60409             '.x-grid-cal  .fc-event-hori' : {
60410                 height: '14px'
60411             }
60412              
60413             
60414         }, Roo.id());
60415     }
60416
60417     
60418     
60419 };
60420 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60421     /**
60422      * @cfg {Store} eventStore The store that loads events.
60423      */
60424     eventStore : 25,
60425
60426      
60427     activeDate : false,
60428     startDay : 0,
60429     autoWidth : true,
60430     monitorWindowResize : false,
60431
60432     
60433     resizeColumns : function() {
60434         var col = (this.view.el.getWidth() / 7) - 3;
60435         // loop through cols, and setWidth
60436         for(var i =0 ; i < 7 ; i++){
60437             this.cm.setColumnWidth(i, col);
60438         }
60439     },
60440      setDate :function(date) {
60441         
60442         Roo.log('setDate?');
60443         
60444         this.resizeColumns();
60445         var vd = this.activeDate;
60446         this.activeDate = date;
60447 //        if(vd && this.el){
60448 //            var t = date.getTime();
60449 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60450 //                Roo.log('using add remove');
60451 //                
60452 //                this.fireEvent('monthchange', this, date);
60453 //                
60454 //                this.cells.removeClass("fc-state-highlight");
60455 //                this.cells.each(function(c){
60456 //                   if(c.dateValue == t){
60457 //                       c.addClass("fc-state-highlight");
60458 //                       setTimeout(function(){
60459 //                            try{c.dom.firstChild.focus();}catch(e){}
60460 //                       }, 50);
60461 //                       return false;
60462 //                   }
60463 //                   return true;
60464 //                });
60465 //                return;
60466 //            }
60467 //        }
60468         
60469         var days = date.getDaysInMonth();
60470         
60471         var firstOfMonth = date.getFirstDateOfMonth();
60472         var startingPos = firstOfMonth.getDay()-this.startDay;
60473         
60474         if(startingPos < this.startDay){
60475             startingPos += 7;
60476         }
60477         
60478         var pm = date.add(Date.MONTH, -1);
60479         var prevStart = pm.getDaysInMonth()-startingPos;
60480 //        
60481         
60482         
60483         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60484         
60485         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60486         //this.cells.addClassOnOver('fc-state-hover');
60487         
60488         var cells = this.cells.elements;
60489         var textEls = this.textNodes;
60490         
60491         //Roo.each(cells, function(cell){
60492         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60493         //});
60494         
60495         days += startingPos;
60496
60497         // convert everything to numbers so it's fast
60498         var day = 86400000;
60499         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60500         //Roo.log(d);
60501         //Roo.log(pm);
60502         //Roo.log(prevStart);
60503         
60504         var today = new Date().clearTime().getTime();
60505         var sel = date.clearTime().getTime();
60506         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60507         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60508         var ddMatch = this.disabledDatesRE;
60509         var ddText = this.disabledDatesText;
60510         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60511         var ddaysText = this.disabledDaysText;
60512         var format = this.format;
60513         
60514         var setCellClass = function(cal, cell){
60515             
60516             //Roo.log('set Cell Class');
60517             cell.title = "";
60518             var t = d.getTime();
60519             
60520             //Roo.log(d);
60521             
60522             
60523             cell.dateValue = t;
60524             if(t == today){
60525                 cell.className += " fc-today";
60526                 cell.className += " fc-state-highlight";
60527                 cell.title = cal.todayText;
60528             }
60529             if(t == sel){
60530                 // disable highlight in other month..
60531                 cell.className += " fc-state-highlight";
60532                 
60533             }
60534             // disabling
60535             if(t < min) {
60536                 //cell.className = " fc-state-disabled";
60537                 cell.title = cal.minText;
60538                 return;
60539             }
60540             if(t > max) {
60541                 //cell.className = " fc-state-disabled";
60542                 cell.title = cal.maxText;
60543                 return;
60544             }
60545             if(ddays){
60546                 if(ddays.indexOf(d.getDay()) != -1){
60547                     // cell.title = ddaysText;
60548                    // cell.className = " fc-state-disabled";
60549                 }
60550             }
60551             if(ddMatch && format){
60552                 var fvalue = d.dateFormat(format);
60553                 if(ddMatch.test(fvalue)){
60554                     cell.title = ddText.replace("%0", fvalue);
60555                    cell.className = " fc-state-disabled";
60556                 }
60557             }
60558             
60559             if (!cell.initialClassName) {
60560                 cell.initialClassName = cell.dom.className;
60561             }
60562             
60563             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60564         };
60565
60566         var i = 0;
60567         
60568         for(; i < startingPos; i++) {
60569             cells[i].dayName =  (++prevStart);
60570             Roo.log(textEls[i]);
60571             d.setDate(d.getDate()+1);
60572             
60573             //cells[i].className = "fc-past fc-other-month";
60574             setCellClass(this, cells[i]);
60575         }
60576         
60577         var intDay = 0;
60578         
60579         for(; i < days; i++){
60580             intDay = i - startingPos + 1;
60581             cells[i].dayName =  (intDay);
60582             d.setDate(d.getDate()+1);
60583             
60584             cells[i].className = ''; // "x-date-active";
60585             setCellClass(this, cells[i]);
60586         }
60587         var extraDays = 0;
60588         
60589         for(; i < 42; i++) {
60590             //textEls[i].innerHTML = (++extraDays);
60591             
60592             d.setDate(d.getDate()+1);
60593             cells[i].dayName = (++extraDays);
60594             cells[i].className = "fc-future fc-other-month";
60595             setCellClass(this, cells[i]);
60596         }
60597         
60598         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60599         
60600         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60601         
60602         // this will cause all the cells to mis
60603         var rows= [];
60604         var i =0;
60605         for (var r = 0;r < 6;r++) {
60606             for (var c =0;c < 7;c++) {
60607                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60608             }    
60609         }
60610         
60611         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60612         for(i=0;i<cells.length;i++) {
60613             
60614             this.cells.elements[i].dayName = cells[i].dayName ;
60615             this.cells.elements[i].className = cells[i].className;
60616             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60617             this.cells.elements[i].title = cells[i].title ;
60618             this.cells.elements[i].dateValue = cells[i].dateValue ;
60619         }
60620         
60621         
60622         
60623         
60624         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60625         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60626         
60627         ////if(totalRows != 6){
60628             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60629            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60630        // }
60631         
60632         this.fireEvent('monthchange', this, date);
60633         
60634         
60635     },
60636  /**
60637      * Returns the grid's SelectionModel.
60638      * @return {SelectionModel}
60639      */
60640     getSelectionModel : function(){
60641         if(!this.selModel){
60642             this.selModel = new Roo.grid.CellSelectionModel();
60643         }
60644         return this.selModel;
60645     },
60646
60647     load: function() {
60648         this.eventStore.load()
60649         
60650         
60651         
60652     },
60653     
60654     findCell : function(dt) {
60655         dt = dt.clearTime().getTime();
60656         var ret = false;
60657         this.cells.each(function(c){
60658             //Roo.log("check " +c.dateValue + '?=' + dt);
60659             if(c.dateValue == dt){
60660                 ret = c;
60661                 return false;
60662             }
60663             return true;
60664         });
60665         
60666         return ret;
60667     },
60668     
60669     findCells : function(rec) {
60670         var s = rec.data.start_dt.clone().clearTime().getTime();
60671        // Roo.log(s);
60672         var e= rec.data.end_dt.clone().clearTime().getTime();
60673        // Roo.log(e);
60674         var ret = [];
60675         this.cells.each(function(c){
60676              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60677             
60678             if(c.dateValue > e){
60679                 return ;
60680             }
60681             if(c.dateValue < s){
60682                 return ;
60683             }
60684             ret.push(c);
60685         });
60686         
60687         return ret;    
60688     },
60689     
60690     findBestRow: function(cells)
60691     {
60692         var ret = 0;
60693         
60694         for (var i =0 ; i < cells.length;i++) {
60695             ret  = Math.max(cells[i].rows || 0,ret);
60696         }
60697         return ret;
60698         
60699     },
60700     
60701     
60702     addItem : function(rec)
60703     {
60704         // look for vertical location slot in
60705         var cells = this.findCells(rec);
60706         
60707         rec.row = this.findBestRow(cells);
60708         
60709         // work out the location.
60710         
60711         var crow = false;
60712         var rows = [];
60713         for(var i =0; i < cells.length; i++) {
60714             if (!crow) {
60715                 crow = {
60716                     start : cells[i],
60717                     end :  cells[i]
60718                 };
60719                 continue;
60720             }
60721             if (crow.start.getY() == cells[i].getY()) {
60722                 // on same row.
60723                 crow.end = cells[i];
60724                 continue;
60725             }
60726             // different row.
60727             rows.push(crow);
60728             crow = {
60729                 start: cells[i],
60730                 end : cells[i]
60731             };
60732             
60733         }
60734         
60735         rows.push(crow);
60736         rec.els = [];
60737         rec.rows = rows;
60738         rec.cells = cells;
60739         for (var i = 0; i < cells.length;i++) {
60740             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60741             
60742         }
60743         
60744         
60745     },
60746     
60747     clearEvents: function() {
60748         
60749         if (!this.eventStore.getCount()) {
60750             return;
60751         }
60752         // reset number of rows in cells.
60753         Roo.each(this.cells.elements, function(c){
60754             c.rows = 0;
60755         });
60756         
60757         this.eventStore.each(function(e) {
60758             this.clearEvent(e);
60759         },this);
60760         
60761     },
60762     
60763     clearEvent : function(ev)
60764     {
60765         if (ev.els) {
60766             Roo.each(ev.els, function(el) {
60767                 el.un('mouseenter' ,this.onEventEnter, this);
60768                 el.un('mouseleave' ,this.onEventLeave, this);
60769                 el.remove();
60770             },this);
60771             ev.els = [];
60772         }
60773     },
60774     
60775     
60776     renderEvent : function(ev,ctr) {
60777         if (!ctr) {
60778              ctr = this.view.el.select('.fc-event-container',true).first();
60779         }
60780         
60781          
60782         this.clearEvent(ev);
60783             //code
60784        
60785         
60786         
60787         ev.els = [];
60788         var cells = ev.cells;
60789         var rows = ev.rows;
60790         this.fireEvent('eventrender', this, ev);
60791         
60792         for(var i =0; i < rows.length; i++) {
60793             
60794             cls = '';
60795             if (i == 0) {
60796                 cls += ' fc-event-start';
60797             }
60798             if ((i+1) == rows.length) {
60799                 cls += ' fc-event-end';
60800             }
60801             
60802             //Roo.log(ev.data);
60803             // how many rows should it span..
60804             var cg = this.eventTmpl.append(ctr,Roo.apply({
60805                 fccls : cls
60806                 
60807             }, ev.data) , true);
60808             
60809             
60810             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60811             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60812             cg.on('click', this.onEventClick, this, ev);
60813             
60814             ev.els.push(cg);
60815             
60816             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60817             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60818             //Roo.log(cg);
60819              
60820             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60821             cg.setWidth(ebox.right - sbox.x -2);
60822         }
60823     },
60824     
60825     renderEvents: function()
60826     {   
60827         // first make sure there is enough space..
60828         
60829         if (!this.eventTmpl) {
60830             this.eventTmpl = new Roo.Template(
60831                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60832                     '<div class="fc-event-inner">' +
60833                         '<span class="fc-event-time">{time}</span>' +
60834                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60835                     '</div>' +
60836                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60837                 '</div>'
60838             );
60839                 
60840         }
60841                
60842         
60843         
60844         this.cells.each(function(c) {
60845             //Roo.log(c.select('.fc-day-content div',true).first());
60846             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60847         });
60848         
60849         var ctr = this.view.el.select('.fc-event-container',true).first();
60850         
60851         var cls;
60852         this.eventStore.each(function(ev){
60853             
60854             this.renderEvent(ev);
60855              
60856              
60857         }, this);
60858         this.view.layout();
60859         
60860     },
60861     
60862     onEventEnter: function (e, el,event,d) {
60863         this.fireEvent('evententer', this, el, event);
60864     },
60865     
60866     onEventLeave: function (e, el,event,d) {
60867         this.fireEvent('eventleave', this, el, event);
60868     },
60869     
60870     onEventClick: function (e, el,event,d) {
60871         this.fireEvent('eventclick', this, el, event);
60872     },
60873     
60874     onMonthChange: function () {
60875         this.store.load();
60876     },
60877     
60878     onLoad: function () {
60879         
60880         //Roo.log('calendar onload');
60881 //         
60882         if(this.eventStore.getCount() > 0){
60883             
60884            
60885             
60886             this.eventStore.each(function(d){
60887                 
60888                 
60889                 // FIXME..
60890                 var add =   d.data;
60891                 if (typeof(add.end_dt) == 'undefined')  {
60892                     Roo.log("Missing End time in calendar data: ");
60893                     Roo.log(d);
60894                     return;
60895                 }
60896                 if (typeof(add.start_dt) == 'undefined')  {
60897                     Roo.log("Missing Start time in calendar data: ");
60898                     Roo.log(d);
60899                     return;
60900                 }
60901                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60902                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60903                 add.id = add.id || d.id;
60904                 add.title = add.title || '??';
60905                 
60906                 this.addItem(d);
60907                 
60908              
60909             },this);
60910         }
60911         
60912         this.renderEvents();
60913     }
60914     
60915
60916 });
60917 /*
60918  grid : {
60919                 xtype: 'Grid',
60920                 xns: Roo.grid,
60921                 listeners : {
60922                     render : function ()
60923                     {
60924                         _this.grid = this;
60925                         
60926                         if (!this.view.el.hasClass('course-timesheet')) {
60927                             this.view.el.addClass('course-timesheet');
60928                         }
60929                         if (this.tsStyle) {
60930                             this.ds.load({});
60931                             return; 
60932                         }
60933                         Roo.log('width');
60934                         Roo.log(_this.grid.view.el.getWidth());
60935                         
60936                         
60937                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60938                             '.course-timesheet .x-grid-row' : {
60939                                 height: '80px'
60940                             },
60941                             '.x-grid-row td' : {
60942                                 'vertical-align' : 0
60943                             },
60944                             '.course-edit-link' : {
60945                                 'color' : 'blue',
60946                                 'text-overflow' : 'ellipsis',
60947                                 'overflow' : 'hidden',
60948                                 'white-space' : 'nowrap',
60949                                 'cursor' : 'pointer'
60950                             },
60951                             '.sub-link' : {
60952                                 'color' : 'green'
60953                             },
60954                             '.de-act-sup-link' : {
60955                                 'color' : 'purple',
60956                                 'text-decoration' : 'line-through'
60957                             },
60958                             '.de-act-link' : {
60959                                 'color' : 'red',
60960                                 'text-decoration' : 'line-through'
60961                             },
60962                             '.course-timesheet .course-highlight' : {
60963                                 'border-top-style': 'dashed !important',
60964                                 'border-bottom-bottom': 'dashed !important'
60965                             },
60966                             '.course-timesheet .course-item' : {
60967                                 'font-family'   : 'tahoma, arial, helvetica',
60968                                 'font-size'     : '11px',
60969                                 'overflow'      : 'hidden',
60970                                 'padding-left'  : '10px',
60971                                 'padding-right' : '10px',
60972                                 'padding-top' : '10px' 
60973                             }
60974                             
60975                         }, Roo.id());
60976                                 this.ds.load({});
60977                     }
60978                 },
60979                 autoWidth : true,
60980                 monitorWindowResize : false,
60981                 cellrenderer : function(v,x,r)
60982                 {
60983                     return v;
60984                 },
60985                 sm : {
60986                     xtype: 'CellSelectionModel',
60987                     xns: Roo.grid
60988                 },
60989                 dataSource : {
60990                     xtype: 'Store',
60991                     xns: Roo.data,
60992                     listeners : {
60993                         beforeload : function (_self, options)
60994                         {
60995                             options.params = options.params || {};
60996                             options.params._month = _this.monthField.getValue();
60997                             options.params.limit = 9999;
60998                             options.params['sort'] = 'when_dt';    
60999                             options.params['dir'] = 'ASC';    
61000                             this.proxy.loadResponse = this.loadResponse;
61001                             Roo.log("load?");
61002                             //this.addColumns();
61003                         },
61004                         load : function (_self, records, options)
61005                         {
61006                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61007                                 // if you click on the translation.. you can edit it...
61008                                 var el = Roo.get(this);
61009                                 var id = el.dom.getAttribute('data-id');
61010                                 var d = el.dom.getAttribute('data-date');
61011                                 var t = el.dom.getAttribute('data-time');
61012                                 //var id = this.child('span').dom.textContent;
61013                                 
61014                                 //Roo.log(this);
61015                                 Pman.Dialog.CourseCalendar.show({
61016                                     id : id,
61017                                     when_d : d,
61018                                     when_t : t,
61019                                     productitem_active : id ? 1 : 0
61020                                 }, function() {
61021                                     _this.grid.ds.load({});
61022                                 });
61023                            
61024                            });
61025                            
61026                            _this.panel.fireEvent('resize', [ '', '' ]);
61027                         }
61028                     },
61029                     loadResponse : function(o, success, response){
61030                             // this is overridden on before load..
61031                             
61032                             Roo.log("our code?");       
61033                             //Roo.log(success);
61034                             //Roo.log(response)
61035                             delete this.activeRequest;
61036                             if(!success){
61037                                 this.fireEvent("loadexception", this, o, response);
61038                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61039                                 return;
61040                             }
61041                             var result;
61042                             try {
61043                                 result = o.reader.read(response);
61044                             }catch(e){
61045                                 Roo.log("load exception?");
61046                                 this.fireEvent("loadexception", this, o, response, e);
61047                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61048                                 return;
61049                             }
61050                             Roo.log("ready...");        
61051                             // loop through result.records;
61052                             // and set this.tdate[date] = [] << array of records..
61053                             _this.tdata  = {};
61054                             Roo.each(result.records, function(r){
61055                                 //Roo.log(r.data);
61056                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61057                                     _this.tdata[r.data.when_dt.format('j')] = [];
61058                                 }
61059                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61060                             });
61061                             
61062                             //Roo.log(_this.tdata);
61063                             
61064                             result.records = [];
61065                             result.totalRecords = 6;
61066                     
61067                             // let's generate some duumy records for the rows.
61068                             //var st = _this.dateField.getValue();
61069                             
61070                             // work out monday..
61071                             //st = st.add(Date.DAY, -1 * st.format('w'));
61072                             
61073                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61074                             
61075                             var firstOfMonth = date.getFirstDayOfMonth();
61076                             var days = date.getDaysInMonth();
61077                             var d = 1;
61078                             var firstAdded = false;
61079                             for (var i = 0; i < result.totalRecords ; i++) {
61080                                 //var d= st.add(Date.DAY, i);
61081                                 var row = {};
61082                                 var added = 0;
61083                                 for(var w = 0 ; w < 7 ; w++){
61084                                     if(!firstAdded && firstOfMonth != w){
61085                                         continue;
61086                                     }
61087                                     if(d > days){
61088                                         continue;
61089                                     }
61090                                     firstAdded = true;
61091                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61092                                     row['weekday'+w] = String.format(
61093                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61094                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61095                                                     d,
61096                                                     date.format('Y-m-')+dd
61097                                                 );
61098                                     added++;
61099                                     if(typeof(_this.tdata[d]) != 'undefined'){
61100                                         Roo.each(_this.tdata[d], function(r){
61101                                             var is_sub = '';
61102                                             var deactive = '';
61103                                             var id = r.id;
61104                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61105                                             if(r.parent_id*1>0){
61106                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61107                                                 id = r.parent_id;
61108                                             }
61109                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61110                                                 deactive = 'de-act-link';
61111                                             }
61112                                             
61113                                             row['weekday'+w] += String.format(
61114                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61115                                                     id, //0
61116                                                     r.product_id_name, //1
61117                                                     r.when_dt.format('h:ia'), //2
61118                                                     is_sub, //3
61119                                                     deactive, //4
61120                                                     desc // 5
61121                                             );
61122                                         });
61123                                     }
61124                                     d++;
61125                                 }
61126                                 
61127                                 // only do this if something added..
61128                                 if(added > 0){ 
61129                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61130                                 }
61131                                 
61132                                 
61133                                 // push it twice. (second one with an hour..
61134                                 
61135                             }
61136                             //Roo.log(result);
61137                             this.fireEvent("load", this, o, o.request.arg);
61138                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61139                         },
61140                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61141                     proxy : {
61142                         xtype: 'HttpProxy',
61143                         xns: Roo.data,
61144                         method : 'GET',
61145                         url : baseURL + '/Roo/Shop_course.php'
61146                     },
61147                     reader : {
61148                         xtype: 'JsonReader',
61149                         xns: Roo.data,
61150                         id : 'id',
61151                         fields : [
61152                             {
61153                                 'name': 'id',
61154                                 'type': 'int'
61155                             },
61156                             {
61157                                 'name': 'when_dt',
61158                                 'type': 'string'
61159                             },
61160                             {
61161                                 'name': 'end_dt',
61162                                 'type': 'string'
61163                             },
61164                             {
61165                                 'name': 'parent_id',
61166                                 'type': 'int'
61167                             },
61168                             {
61169                                 'name': 'product_id',
61170                                 'type': 'int'
61171                             },
61172                             {
61173                                 'name': 'productitem_id',
61174                                 'type': 'int'
61175                             },
61176                             {
61177                                 'name': 'guid',
61178                                 'type': 'int'
61179                             }
61180                         ]
61181                     }
61182                 },
61183                 toolbar : {
61184                     xtype: 'Toolbar',
61185                     xns: Roo,
61186                     items : [
61187                         {
61188                             xtype: 'Button',
61189                             xns: Roo.Toolbar,
61190                             listeners : {
61191                                 click : function (_self, e)
61192                                 {
61193                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61194                                     sd.setMonth(sd.getMonth()-1);
61195                                     _this.monthField.setValue(sd.format('Y-m-d'));
61196                                     _this.grid.ds.load({});
61197                                 }
61198                             },
61199                             text : "Back"
61200                         },
61201                         {
61202                             xtype: 'Separator',
61203                             xns: Roo.Toolbar
61204                         },
61205                         {
61206                             xtype: 'MonthField',
61207                             xns: Roo.form,
61208                             listeners : {
61209                                 render : function (_self)
61210                                 {
61211                                     _this.monthField = _self;
61212                                    // _this.monthField.set  today
61213                                 },
61214                                 select : function (combo, date)
61215                                 {
61216                                     _this.grid.ds.load({});
61217                                 }
61218                             },
61219                             value : (function() { return new Date(); })()
61220                         },
61221                         {
61222                             xtype: 'Separator',
61223                             xns: Roo.Toolbar
61224                         },
61225                         {
61226                             xtype: 'TextItem',
61227                             xns: Roo.Toolbar,
61228                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61229                         },
61230                         {
61231                             xtype: 'Fill',
61232                             xns: Roo.Toolbar
61233                         },
61234                         {
61235                             xtype: 'Button',
61236                             xns: Roo.Toolbar,
61237                             listeners : {
61238                                 click : function (_self, e)
61239                                 {
61240                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61241                                     sd.setMonth(sd.getMonth()+1);
61242                                     _this.monthField.setValue(sd.format('Y-m-d'));
61243                                     _this.grid.ds.load({});
61244                                 }
61245                             },
61246                             text : "Next"
61247                         }
61248                     ]
61249                 },
61250                  
61251             }
61252         };
61253         
61254         *//*
61255  * Based on:
61256  * Ext JS Library 1.1.1
61257  * Copyright(c) 2006-2007, Ext JS, LLC.
61258  *
61259  * Originally Released Under LGPL - original licence link has changed is not relivant.
61260  *
61261  * Fork - LGPL
61262  * <script type="text/javascript">
61263  */
61264  
61265 /**
61266  * @class Roo.LoadMask
61267  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61268  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61269  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61270  * element's UpdateManager load indicator and will be destroyed after the initial load.
61271  * @constructor
61272  * Create a new LoadMask
61273  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61274  * @param {Object} config The config object
61275  */
61276 Roo.LoadMask = function(el, config){
61277     this.el = Roo.get(el);
61278     Roo.apply(this, config);
61279     if(this.store){
61280         this.store.on('beforeload', this.onBeforeLoad, this);
61281         this.store.on('load', this.onLoad, this);
61282         this.store.on('loadexception', this.onLoadException, this);
61283         this.removeMask = false;
61284     }else{
61285         var um = this.el.getUpdateManager();
61286         um.showLoadIndicator = false; // disable the default indicator
61287         um.on('beforeupdate', this.onBeforeLoad, this);
61288         um.on('update', this.onLoad, this);
61289         um.on('failure', this.onLoad, this);
61290         this.removeMask = true;
61291     }
61292 };
61293
61294 Roo.LoadMask.prototype = {
61295     /**
61296      * @cfg {Boolean} removeMask
61297      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61298      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61299      */
61300     /**
61301      * @cfg {String} msg
61302      * The text to display in a centered loading message box (defaults to 'Loading...')
61303      */
61304     msg : 'Loading...',
61305     /**
61306      * @cfg {String} msgCls
61307      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61308      */
61309     msgCls : 'x-mask-loading',
61310
61311     /**
61312      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61313      * @type Boolean
61314      */
61315     disabled: false,
61316
61317     /**
61318      * Disables the mask to prevent it from being displayed
61319      */
61320     disable : function(){
61321        this.disabled = true;
61322     },
61323
61324     /**
61325      * Enables the mask so that it can be displayed
61326      */
61327     enable : function(){
61328         this.disabled = false;
61329     },
61330     
61331     onLoadException : function()
61332     {
61333         Roo.log(arguments);
61334         
61335         if (typeof(arguments[3]) != 'undefined') {
61336             Roo.MessageBox.alert("Error loading",arguments[3]);
61337         } 
61338         /*
61339         try {
61340             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61341                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61342             }   
61343         } catch(e) {
61344             
61345         }
61346         */
61347     
61348         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61349     },
61350     // private
61351     onLoad : function()
61352     {
61353         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61354     },
61355
61356     // private
61357     onBeforeLoad : function(){
61358         if(!this.disabled){
61359             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61360         }
61361     },
61362
61363     // private
61364     destroy : function(){
61365         if(this.store){
61366             this.store.un('beforeload', this.onBeforeLoad, this);
61367             this.store.un('load', this.onLoad, this);
61368             this.store.un('loadexception', this.onLoadException, this);
61369         }else{
61370             var um = this.el.getUpdateManager();
61371             um.un('beforeupdate', this.onBeforeLoad, this);
61372             um.un('update', this.onLoad, this);
61373             um.un('failure', this.onLoad, this);
61374         }
61375     }
61376 };/*
61377  * Based on:
61378  * Ext JS Library 1.1.1
61379  * Copyright(c) 2006-2007, Ext JS, LLC.
61380  *
61381  * Originally Released Under LGPL - original licence link has changed is not relivant.
61382  *
61383  * Fork - LGPL
61384  * <script type="text/javascript">
61385  */
61386
61387
61388 /**
61389  * @class Roo.XTemplate
61390  * @extends Roo.Template
61391  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61392 <pre><code>
61393 var t = new Roo.XTemplate(
61394         '&lt;select name="{name}"&gt;',
61395                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61396         '&lt;/select&gt;'
61397 );
61398  
61399 // then append, applying the master template values
61400  </code></pre>
61401  *
61402  * Supported features:
61403  *
61404  *  Tags:
61405
61406 <pre><code>
61407       {a_variable} - output encoded.
61408       {a_variable.format:("Y-m-d")} - call a method on the variable
61409       {a_variable:raw} - unencoded output
61410       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61411       {a_variable:this.method_on_template(...)} - call a method on the template object.
61412  
61413 </code></pre>
61414  *  The tpl tag:
61415 <pre><code>
61416         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61417         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61418         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61419         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61420   
61421         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61422         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61423 </code></pre>
61424  *      
61425  */
61426 Roo.XTemplate = function()
61427 {
61428     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61429     if (this.html) {
61430         this.compile();
61431     }
61432 };
61433
61434
61435 Roo.extend(Roo.XTemplate, Roo.Template, {
61436
61437     /**
61438      * The various sub templates
61439      */
61440     tpls : false,
61441     /**
61442      *
61443      * basic tag replacing syntax
61444      * WORD:WORD()
61445      *
61446      * // you can fake an object call by doing this
61447      *  x.t:(test,tesT) 
61448      * 
61449      */
61450     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61451
61452     /**
61453      * compile the template
61454      *
61455      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61456      *
61457      */
61458     compile: function()
61459     {
61460         var s = this.html;
61461      
61462         s = ['<tpl>', s, '</tpl>'].join('');
61463     
61464         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61465             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61466             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61467             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61468             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61469             m,
61470             id     = 0,
61471             tpls   = [];
61472     
61473         while(true == !!(m = s.match(re))){
61474             var forMatch   = m[0].match(nameRe),
61475                 ifMatch   = m[0].match(ifRe),
61476                 execMatch   = m[0].match(execRe),
61477                 namedMatch   = m[0].match(namedRe),
61478                 
61479                 exp  = null, 
61480                 fn   = null,
61481                 exec = null,
61482                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61483                 
61484             if (ifMatch) {
61485                 // if - puts fn into test..
61486                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61487                 if(exp){
61488                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61489                 }
61490             }
61491             
61492             if (execMatch) {
61493                 // exec - calls a function... returns empty if true is  returned.
61494                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61495                 if(exp){
61496                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61497                 }
61498             }
61499             
61500             
61501             if (name) {
61502                 // for = 
61503                 switch(name){
61504                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61505                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61506                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61507                 }
61508             }
61509             var uid = namedMatch ? namedMatch[1] : id;
61510             
61511             
61512             tpls.push({
61513                 id:     namedMatch ? namedMatch[1] : id,
61514                 target: name,
61515                 exec:   exec,
61516                 test:   fn,
61517                 body:   m[1] || ''
61518             });
61519             if (namedMatch) {
61520                 s = s.replace(m[0], '');
61521             } else { 
61522                 s = s.replace(m[0], '{xtpl'+ id + '}');
61523             }
61524             ++id;
61525         }
61526         this.tpls = [];
61527         for(var i = tpls.length-1; i >= 0; --i){
61528             this.compileTpl(tpls[i]);
61529             this.tpls[tpls[i].id] = tpls[i];
61530         }
61531         this.master = tpls[tpls.length-1];
61532         return this;
61533     },
61534     /**
61535      * same as applyTemplate, except it's done to one of the subTemplates
61536      * when using named templates, you can do:
61537      *
61538      * var str = pl.applySubTemplate('your-name', values);
61539      *
61540      * 
61541      * @param {Number} id of the template
61542      * @param {Object} values to apply to template
61543      * @param {Object} parent (normaly the instance of this object)
61544      */
61545     applySubTemplate : function(id, values, parent)
61546     {
61547         
61548         
61549         var t = this.tpls[id];
61550         
61551         
61552         try { 
61553             if(t.test && !t.test.call(this, values, parent)){
61554                 return '';
61555             }
61556         } catch(e) {
61557             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61558             Roo.log(e.toString());
61559             Roo.log(t.test);
61560             return ''
61561         }
61562         try { 
61563             
61564             if(t.exec && t.exec.call(this, values, parent)){
61565                 return '';
61566             }
61567         } catch(e) {
61568             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61569             Roo.log(e.toString());
61570             Roo.log(t.exec);
61571             return ''
61572         }
61573         try {
61574             var vs = t.target ? t.target.call(this, values, parent) : values;
61575             parent = t.target ? values : parent;
61576             if(t.target && vs instanceof Array){
61577                 var buf = [];
61578                 for(var i = 0, len = vs.length; i < len; i++){
61579                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61580                 }
61581                 return buf.join('');
61582             }
61583             return t.compiled.call(this, vs, parent);
61584         } catch (e) {
61585             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61586             Roo.log(e.toString());
61587             Roo.log(t.compiled);
61588             return '';
61589         }
61590     },
61591
61592     compileTpl : function(tpl)
61593     {
61594         var fm = Roo.util.Format;
61595         var useF = this.disableFormats !== true;
61596         var sep = Roo.isGecko ? "+" : ",";
61597         var undef = function(str) {
61598             Roo.log("Property not found :"  + str);
61599             return '';
61600         };
61601         
61602         var fn = function(m, name, format, args)
61603         {
61604             //Roo.log(arguments);
61605             args = args ? args.replace(/\\'/g,"'") : args;
61606             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61607             if (typeof(format) == 'undefined') {
61608                 format= 'htmlEncode';
61609             }
61610             if (format == 'raw' ) {
61611                 format = false;
61612             }
61613             
61614             if(name.substr(0, 4) == 'xtpl'){
61615                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61616             }
61617             
61618             // build an array of options to determine if value is undefined..
61619             
61620             // basically get 'xxxx.yyyy' then do
61621             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61622             //    (function () { Roo.log("Property not found"); return ''; })() :
61623             //    ......
61624             
61625             var udef_ar = [];
61626             var lookfor = '';
61627             Roo.each(name.split('.'), function(st) {
61628                 lookfor += (lookfor.length ? '.': '') + st;
61629                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61630             });
61631             
61632             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61633             
61634             
61635             if(format && useF){
61636                 
61637                 args = args ? ',' + args : "";
61638                  
61639                 if(format.substr(0, 5) != "this."){
61640                     format = "fm." + format + '(';
61641                 }else{
61642                     format = 'this.call("'+ format.substr(5) + '", ';
61643                     args = ", values";
61644                 }
61645                 
61646                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61647             }
61648              
61649             if (args.length) {
61650                 // called with xxyx.yuu:(test,test)
61651                 // change to ()
61652                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61653             }
61654             // raw.. - :raw modifier..
61655             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61656             
61657         };
61658         var body;
61659         // branched to use + in gecko and [].join() in others
61660         if(Roo.isGecko){
61661             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61662                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61663                     "';};};";
61664         }else{
61665             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61666             body.push(tpl.body.replace(/(\r\n|\n)/g,
61667                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61668             body.push("'].join('');};};");
61669             body = body.join('');
61670         }
61671         
61672         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61673        
61674         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61675         eval(body);
61676         
61677         return this;
61678     },
61679
61680     applyTemplate : function(values){
61681         return this.master.compiled.call(this, values, {});
61682         //var s = this.subs;
61683     },
61684
61685     apply : function(){
61686         return this.applyTemplate.apply(this, arguments);
61687     }
61688
61689  });
61690
61691 Roo.XTemplate.from = function(el){
61692     el = Roo.getDom(el);
61693     return new Roo.XTemplate(el.value || el.innerHTML);
61694 };