roojs-all.js
[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         isGecko = !isSafari && ua.indexOf("gecko") > -1,
60         isBorderBox = isIE && !isStrict,
61         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
62         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
63         isLinux = (ua.indexOf("linux") != -1),
64         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
65         isTouch =  (function() {
66             try {  
67                 document.createEvent("TouchEvent");  
68                 return true;  
69             } catch (e) {  
70                 return false;  
71             } 
72             
73         })();
74     // remove css image flicker
75         if(isIE && !isIE7){
76         try{
77             document.execCommand("BackgroundImageCache", false, true);
78         }catch(e){}
79     }
80     
81     Roo.apply(Roo, {
82         /**
83          * True if the browser is in strict mode
84          * @type Boolean
85          */
86         isStrict : isStrict,
87         /**
88          * True if the page is running over SSL
89          * @type Boolean
90          */
91         isSecure : isSecure,
92         /**
93          * True when the document is fully initialized and ready for action
94          * @type Boolean
95          */
96         isReady : false,
97         /**
98          * Turn on debugging output (currently only the factory uses this)
99          * @type Boolean
100          */
101         
102         debug: false,
103
104         /**
105          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
106          * @type Boolean
107          */
108         enableGarbageCollector : true,
109
110         /**
111          * True to automatically purge event listeners after uncaching an element (defaults to false).
112          * Note: this only happens if enableGarbageCollector is true.
113          * @type Boolean
114          */
115         enableListenerCollection:false,
116
117         /**
118          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
119          * the IE insecure content warning (defaults to javascript:false).
120          * @type String
121          */
122         SSL_SECURE_URL : "javascript:false",
123
124         /**
125          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
126          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
127          * @type String
128          */
129         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
130
131         emptyFn : function(){},
132         
133         /**
134          * Copies all the properties of config to obj if they don't already exist.
135          * @param {Object} obj The receiver of the properties
136          * @param {Object} config The source of the properties
137          * @return {Object} returns obj
138          */
139         applyIf : function(o, c){
140             if(o && c){
141                 for(var p in c){
142                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
143                 }
144             }
145             return o;
146         },
147
148         /**
149          * Applies event listeners to elements by selectors when the document is ready.
150          * The event name is specified with an @ suffix.
151 <pre><code>
152 Roo.addBehaviors({
153    // add a listener for click on all anchors in element with id foo
154    '#foo a@click' : function(e, t){
155        // do something
156    },
157
158    // add the same listener to multiple selectors (separated by comma BEFORE the @)
159    '#foo a, #bar span.some-class@mouseover' : function(){
160        // do something
161    }
162 });
163 </code></pre>
164          * @param {Object} obj The list of behaviors to apply
165          */
166         addBehaviors : function(o){
167             if(!Roo.isReady){
168                 Roo.onReady(function(){
169                     Roo.addBehaviors(o);
170                 });
171                 return;
172             }
173             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
174             for(var b in o){
175                 var parts = b.split('@');
176                 if(parts[1]){ // for Object prototype breakers
177                     var s = parts[0];
178                     if(!cache[s]){
179                         cache[s] = Roo.select(s);
180                     }
181                     cache[s].on(parts[1], o[b]);
182                 }
183             }
184             cache = null;
185         },
186
187         /**
188          * Generates unique ids. If the element already has an id, it is unchanged
189          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
190          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
191          * @return {String} The generated Id.
192          */
193         id : function(el, prefix){
194             prefix = prefix || "roo-gen";
195             el = Roo.getDom(el);
196             var id = prefix + (++idSeed);
197             return el ? (el.id ? el.id : (el.id = id)) : id;
198         },
199          
200        
201         /**
202          * Extends one class with another class and optionally overrides members with the passed literal. This class
203          * also adds the function "override()" to the class that can be used to override
204          * members on an instance.
205          * @param {Object} subclass The class inheriting the functionality
206          * @param {Object} superclass The class being extended
207          * @param {Object} overrides (optional) A literal with members
208          * @method extend
209          */
210         extend : function(){
211             // inline overrides
212             var io = function(o){
213                 for(var m in o){
214                     this[m] = o[m];
215                 }
216             };
217             return function(sb, sp, overrides){
218                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
219                     overrides = sp;
220                     sp = sb;
221                     sb = function(){sp.apply(this, arguments);};
222                 }
223                 var F = function(){}, sbp, spp = sp.prototype;
224                 F.prototype = spp;
225                 sbp = sb.prototype = new F();
226                 sbp.constructor=sb;
227                 sb.superclass=spp;
228                 
229                 if(spp.constructor == Object.prototype.constructor){
230                     spp.constructor=sp;
231                    
232                 }
233                 
234                 sb.override = function(o){
235                     Roo.override(sb, o);
236                 };
237                 sbp.override = io;
238                 Roo.override(sb, overrides);
239                 return sb;
240             };
241         }(),
242
243         /**
244          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
245          * Usage:<pre><code>
246 Roo.override(MyClass, {
247     newMethod1: function(){
248         // etc.
249     },
250     newMethod2: function(foo){
251         // etc.
252     }
253 });
254  </code></pre>
255          * @param {Object} origclass The class to override
256          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
257          * containing one or more methods.
258          * @method override
259          */
260         override : function(origclass, overrides){
261             if(overrides){
262                 var p = origclass.prototype;
263                 for(var method in overrides){
264                     p[method] = overrides[method];
265                 }
266             }
267         },
268         /**
269          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
270          * <pre><code>
271 Roo.namespace('Company', 'Company.data');
272 Company.Widget = function() { ... }
273 Company.data.CustomStore = function(config) { ... }
274 </code></pre>
275          * @param {String} namespace1
276          * @param {String} namespace2
277          * @param {String} etc
278          * @method namespace
279          */
280         namespace : function(){
281             var a=arguments, o=null, i, j, d, rt;
282             for (i=0; i<a.length; ++i) {
283                 d=a[i].split(".");
284                 rt = d[0];
285                 /** eval:var:o */
286                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
287                 for (j=1; j<d.length; ++j) {
288                     o[d[j]]=o[d[j]] || {};
289                     o=o[d[j]];
290                 }
291             }
292         },
293         /**
294          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
295          * <pre><code>
296 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
297 Roo.factory(conf, Roo.data);
298 </code></pre>
299          * @param {String} classname
300          * @param {String} namespace (optional)
301          * @method factory
302          */
303          
304         factory : function(c, ns)
305         {
306             // no xtype, no ns or c.xns - or forced off by c.xns
307             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
308                 return c;
309             }
310             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
311             if (c.constructor == ns[c.xtype]) {// already created...
312                 return c;
313             }
314             if (ns[c.xtype]) {
315                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
316                 var ret = new ns[c.xtype](c);
317                 ret.xns = false;
318                 return ret;
319             }
320             c.xns = false; // prevent recursion..
321             return c;
322         },
323          /**
324          * Logs to console if it can.
325          *
326          * @param {String|Object} string
327          * @method log
328          */
329         log : function(s)
330         {
331             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
332                 return; // alerT?
333             }
334             console.log(s);
335             
336         },
337         /**
338          * 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.
339          * @param {Object} o
340          * @return {String}
341          */
342         urlEncode : function(o){
343             if(!o){
344                 return "";
345             }
346             var buf = [];
347             for(var key in o){
348                 var ov = o[key], k = Roo.encodeURIComponent(key);
349                 var type = typeof ov;
350                 if(type == 'undefined'){
351                     buf.push(k, "=&");
352                 }else if(type != "function" && type != "object"){
353                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
354                 }else if(ov instanceof Array){
355                     if (ov.length) {
356                             for(var i = 0, len = ov.length; i < len; i++) {
357                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
358                             }
359                         } else {
360                             buf.push(k, "=&");
361                         }
362                 }
363             }
364             buf.pop();
365             return buf.join("");
366         },
367          /**
368          * Safe version of encodeURIComponent
369          * @param {String} data 
370          * @return {String} 
371          */
372         
373         encodeURIComponent : function (data)
374         {
375             try {
376                 return encodeURIComponent(data);
377             } catch(e) {} // should be an uri encode error.
378             
379             if (data == '' || data == null){
380                return '';
381             }
382             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
383             function nibble_to_hex(nibble){
384                 var chars = '0123456789ABCDEF';
385                 return chars.charAt(nibble);
386             }
387             data = data.toString();
388             var buffer = '';
389             for(var i=0; i<data.length; i++){
390                 var c = data.charCodeAt(i);
391                 var bs = new Array();
392                 if (c > 0x10000){
393                         // 4 bytes
394                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
395                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
396                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
397                     bs[3] = 0x80 | (c & 0x3F);
398                 }else if (c > 0x800){
399                          // 3 bytes
400                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
401                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
402                     bs[2] = 0x80 | (c & 0x3F);
403                 }else if (c > 0x80){
404                        // 2 bytes
405                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
406                     bs[1] = 0x80 | (c & 0x3F);
407                 }else{
408                         // 1 byte
409                     bs[0] = c;
410                 }
411                 for(var j=0; j<bs.length; j++){
412                     var b = bs[j];
413                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
414                             + nibble_to_hex(b &0x0F);
415                     buffer += '%'+hex;
416                }
417             }
418             return buffer;    
419              
420         },
421
422         /**
423          * 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]}.
424          * @param {String} string
425          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
426          * @return {Object} A literal with members
427          */
428         urlDecode : function(string, overwrite){
429             if(!string || !string.length){
430                 return {};
431             }
432             var obj = {};
433             var pairs = string.split('&');
434             var pair, name, value;
435             for(var i = 0, len = pairs.length; i < len; i++){
436                 pair = pairs[i].split('=');
437                 name = decodeURIComponent(pair[0]);
438                 value = decodeURIComponent(pair[1]);
439                 if(overwrite !== true){
440                     if(typeof obj[name] == "undefined"){
441                         obj[name] = value;
442                     }else if(typeof obj[name] == "string"){
443                         obj[name] = [obj[name]];
444                         obj[name].push(value);
445                     }else{
446                         obj[name].push(value);
447                     }
448                 }else{
449                     obj[name] = value;
450                 }
451             }
452             return obj;
453         },
454
455         /**
456          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
457          * passed array is not really an array, your function is called once with it.
458          * The supplied function is called with (Object item, Number index, Array allItems).
459          * @param {Array/NodeList/Mixed} array
460          * @param {Function} fn
461          * @param {Object} scope
462          */
463         each : function(array, fn, scope){
464             if(typeof array.length == "undefined" || typeof array == "string"){
465                 array = [array];
466             }
467             for(var i = 0, len = array.length; i < len; i++){
468                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
469             }
470         },
471
472         // deprecated
473         combine : function(){
474             var as = arguments, l = as.length, r = [];
475             for(var i = 0; i < l; i++){
476                 var a = as[i];
477                 if(a instanceof Array){
478                     r = r.concat(a);
479                 }else if(a.length !== undefined && !a.substr){
480                     r = r.concat(Array.prototype.slice.call(a, 0));
481                 }else{
482                     r.push(a);
483                 }
484             }
485             return r;
486         },
487
488         /**
489          * Escapes the passed string for use in a regular expression
490          * @param {String} str
491          * @return {String}
492          */
493         escapeRe : function(s) {
494             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
495         },
496
497         // internal
498         callback : function(cb, scope, args, delay){
499             if(typeof cb == "function"){
500                 if(delay){
501                     cb.defer(delay, scope, args || []);
502                 }else{
503                     cb.apply(scope, args || []);
504                 }
505             }
506         },
507
508         /**
509          * Return the dom node for the passed string (id), dom node, or Roo.Element
510          * @param {String/HTMLElement/Roo.Element} el
511          * @return HTMLElement
512          */
513         getDom : function(el){
514             if(!el){
515                 return null;
516             }
517             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
518         },
519
520         /**
521         * Shorthand for {@link Roo.ComponentMgr#get}
522         * @param {String} id
523         * @return Roo.Component
524         */
525         getCmp : function(id){
526             return Roo.ComponentMgr.get(id);
527         },
528          
529         num : function(v, defaultValue){
530             if(typeof v != 'number'){
531                 return defaultValue;
532             }
533             return v;
534         },
535
536         destroy : function(){
537             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
538                 var as = a[i];
539                 if(as){
540                     if(as.dom){
541                         as.removeAllListeners();
542                         as.remove();
543                         continue;
544                     }
545                     if(typeof as.purgeListeners == 'function'){
546                         as.purgeListeners();
547                     }
548                     if(typeof as.destroy == 'function'){
549                         as.destroy();
550                     }
551                 }
552             }
553         },
554
555         // inpired by a similar function in mootools library
556         /**
557          * Returns the type of object that is passed in. If the object passed in is null or undefined it
558          * return false otherwise it returns one of the following values:<ul>
559          * <li><b>string</b>: If the object passed is a string</li>
560          * <li><b>number</b>: If the object passed is a number</li>
561          * <li><b>boolean</b>: If the object passed is a boolean value</li>
562          * <li><b>function</b>: If the object passed is a function reference</li>
563          * <li><b>object</b>: If the object passed is an object</li>
564          * <li><b>array</b>: If the object passed is an array</li>
565          * <li><b>regexp</b>: If the object passed is a regular expression</li>
566          * <li><b>element</b>: If the object passed is a DOM Element</li>
567          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
568          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
569          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
570          * @param {Mixed} object
571          * @return {String}
572          */
573         type : function(o){
574             if(o === undefined || o === null){
575                 return false;
576             }
577             if(o.htmlElement){
578                 return 'element';
579             }
580             var t = typeof o;
581             if(t == 'object' && o.nodeName) {
582                 switch(o.nodeType) {
583                     case 1: return 'element';
584                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
585                 }
586             }
587             if(t == 'object' || t == 'function') {
588                 switch(o.constructor) {
589                     case Array: return 'array';
590                     case RegExp: return 'regexp';
591                 }
592                 if(typeof o.length == 'number' && typeof o.item == 'function') {
593                     return 'nodelist';
594                 }
595             }
596             return t;
597         },
598
599         /**
600          * Returns true if the passed value is null, undefined or an empty string (optional).
601          * @param {Mixed} value The value to test
602          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
603          * @return {Boolean}
604          */
605         isEmpty : function(v, allowBlank){
606             return v === null || v === undefined || (!allowBlank ? v === '' : false);
607         },
608         
609         /** @type Boolean */
610         isOpera : isOpera,
611         /** @type Boolean */
612         isSafari : isSafari,
613         /** @type Boolean */
614         isFirefox : isFirefox,
615         /** @type Boolean */
616         isIE : isIE,
617         /** @type Boolean */
618         isIE7 : isIE7,
619         /** @type Boolean */
620         isGecko : isGecko,
621         /** @type Boolean */
622         isBorderBox : isBorderBox,
623         /** @type Boolean */
624         isWindows : isWindows,
625         /** @type Boolean */
626         isLinux : isLinux,
627         /** @type Boolean */
628         isMac : isMac,
629         /** @type Boolean */
630         isTouch : isTouch,
631
632         /**
633          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
634          * you may want to set this to true.
635          * @type Boolean
636          */
637         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
638         
639         
640                 
641         /**
642          * Selects a single element as a Roo Element
643          * This is about as close as you can get to jQuery's $('do crazy stuff')
644          * @param {String} selector The selector/xpath query
645          * @param {Node} root (optional) The start of the query (defaults to document).
646          * @return {Roo.Element}
647          */
648         selectNode : function(selector, root) 
649         {
650             var node = Roo.DomQuery.selectNode(selector,root);
651             return node ? Roo.get(node) : new Roo.Element(false);
652         }
653         
654     });
655
656
657 })();
658
659 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
660                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
661                 "Roo.app", "Roo.ux",
662                 "Roo.bootstrap",
663                 "Roo.bootstrap.dash");
664 /*
665  * Based on:
666  * Ext JS Library 1.1.1
667  * Copyright(c) 2006-2007, Ext JS, LLC.
668  *
669  * Originally Released Under LGPL - original licence link has changed is not relivant.
670  *
671  * Fork - LGPL
672  * <script type="text/javascript">
673  */
674
675 (function() {    
676     // wrappedn so fnCleanup is not in global scope...
677     if(Roo.isIE) {
678         function fnCleanUp() {
679             var p = Function.prototype;
680             delete p.createSequence;
681             delete p.defer;
682             delete p.createDelegate;
683             delete p.createCallback;
684             delete p.createInterceptor;
685
686             window.detachEvent("onunload", fnCleanUp);
687         }
688         window.attachEvent("onunload", fnCleanUp);
689     }
690 })();
691
692
693 /**
694  * @class Function
695  * These functions are available on every Function object (any JavaScript function).
696  */
697 Roo.apply(Function.prototype, {
698      /**
699      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
700      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
701      * Will create a function that is bound to those 2 args.
702      * @return {Function} The new function
703     */
704     createCallback : function(/*args...*/){
705         // make args available, in function below
706         var args = arguments;
707         var method = this;
708         return function() {
709             return method.apply(window, args);
710         };
711     },
712
713     /**
714      * Creates a delegate (callback) that sets the scope to obj.
715      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
716      * Will create a function that is automatically scoped to this.
717      * @param {Object} obj (optional) The object for which the scope is set
718      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
719      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
720      *                                             if a number the args are inserted at the specified position
721      * @return {Function} The new function
722      */
723     createDelegate : function(obj, args, appendArgs){
724         var method = this;
725         return function() {
726             var callArgs = args || arguments;
727             if(appendArgs === true){
728                 callArgs = Array.prototype.slice.call(arguments, 0);
729                 callArgs = callArgs.concat(args);
730             }else if(typeof appendArgs == "number"){
731                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
732                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
733                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
734             }
735             return method.apply(obj || window, callArgs);
736         };
737     },
738
739     /**
740      * Calls this function after the number of millseconds specified.
741      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
742      * @param {Object} obj (optional) The object for which the scope is set
743      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
744      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
745      *                                             if a number the args are inserted at the specified position
746      * @return {Number} The timeout id that can be used with clearTimeout
747      */
748     defer : function(millis, obj, args, appendArgs){
749         var fn = this.createDelegate(obj, args, appendArgs);
750         if(millis){
751             return setTimeout(fn, millis);
752         }
753         fn();
754         return 0;
755     },
756     /**
757      * Create a combined function call sequence of the original function + the passed function.
758      * The resulting function returns the results of the original function.
759      * The passed fcn is called with the parameters of the original function
760      * @param {Function} fcn The function to sequence
761      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
762      * @return {Function} The new function
763      */
764     createSequence : function(fcn, scope){
765         if(typeof fcn != "function"){
766             return this;
767         }
768         var method = this;
769         return function() {
770             var retval = method.apply(this || window, arguments);
771             fcn.apply(scope || this || window, arguments);
772             return retval;
773         };
774     },
775
776     /**
777      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
778      * The resulting function returns the results of the original function.
779      * The passed fcn is called with the parameters of the original function.
780      * @addon
781      * @param {Function} fcn The function to call before the original
782      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
783      * @return {Function} The new function
784      */
785     createInterceptor : function(fcn, scope){
786         if(typeof fcn != "function"){
787             return this;
788         }
789         var method = this;
790         return function() {
791             fcn.target = this;
792             fcn.method = method;
793             if(fcn.apply(scope || this || window, arguments) === false){
794                 return;
795             }
796             return method.apply(this || window, arguments);
797         };
798     }
799 });
800 /*
801  * Based on:
802  * Ext JS Library 1.1.1
803  * Copyright(c) 2006-2007, Ext JS, LLC.
804  *
805  * Originally Released Under LGPL - original licence link has changed is not relivant.
806  *
807  * Fork - LGPL
808  * <script type="text/javascript">
809  */
810
811 Roo.applyIf(String, {
812     
813     /** @scope String */
814     
815     /**
816      * Escapes the passed string for ' and \
817      * @param {String} string The string to escape
818      * @return {String} The escaped string
819      * @static
820      */
821     escape : function(string) {
822         return string.replace(/('|\\)/g, "\\$1");
823     },
824
825     /**
826      * Pads the left side of a string with a specified character.  This is especially useful
827      * for normalizing number and date strings.  Example usage:
828      * <pre><code>
829 var s = String.leftPad('123', 5, '0');
830 // s now contains the string: '00123'
831 </code></pre>
832      * @param {String} string The original string
833      * @param {Number} size The total length of the output string
834      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
835      * @return {String} The padded string
836      * @static
837      */
838     leftPad : function (val, size, ch) {
839         var result = new String(val);
840         if(ch === null || ch === undefined || ch === '') {
841             ch = " ";
842         }
843         while (result.length < size) {
844             result = ch + result;
845         }
846         return result;
847     },
848
849     /**
850      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
851      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
852      * <pre><code>
853 var cls = 'my-class', text = 'Some text';
854 var s = String.format('<div class="{0}">{1}</div>', cls, text);
855 // s now contains the string: '<div class="my-class">Some text</div>'
856 </code></pre>
857      * @param {String} string The tokenized string to be formatted
858      * @param {String} value1 The value to replace token {0}
859      * @param {String} value2 Etc...
860      * @return {String} The formatted string
861      * @static
862      */
863     format : function(format){
864         var args = Array.prototype.slice.call(arguments, 1);
865         return format.replace(/\{(\d+)\}/g, function(m, i){
866             return Roo.util.Format.htmlEncode(args[i]);
867         });
868     }
869 });
870
871 /**
872  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
873  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
874  * they are already different, the first value passed in is returned.  Note that this method returns the new value
875  * but does not change the current string.
876  * <pre><code>
877 // alternate sort directions
878 sort = sort.toggle('ASC', 'DESC');
879
880 // instead of conditional logic:
881 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
882 </code></pre>
883  * @param {String} value The value to compare to the current string
884  * @param {String} other The new value to use if the string already equals the first value passed in
885  * @return {String} The new value
886  */
887  
888 String.prototype.toggle = function(value, other){
889     return this == value ? other : value;
890 };/*
891  * Based on:
892  * Ext JS Library 1.1.1
893  * Copyright(c) 2006-2007, Ext JS, LLC.
894  *
895  * Originally Released Under LGPL - original licence link has changed is not relivant.
896  *
897  * Fork - LGPL
898  * <script type="text/javascript">
899  */
900
901  /**
902  * @class Number
903  */
904 Roo.applyIf(Number.prototype, {
905     /**
906      * Checks whether or not the current number is within a desired range.  If the number is already within the
907      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
908      * exceeded.  Note that this method returns the constrained value but does not change the current number.
909      * @param {Number} min The minimum number in the range
910      * @param {Number} max The maximum number in the range
911      * @return {Number} The constrained value if outside the range, otherwise the current value
912      */
913     constrain : function(min, max){
914         return Math.min(Math.max(this, min), max);
915     }
916 });/*
917  * Based on:
918  * Ext JS Library 1.1.1
919  * Copyright(c) 2006-2007, Ext JS, LLC.
920  *
921  * Originally Released Under LGPL - original licence link has changed is not relivant.
922  *
923  * Fork - LGPL
924  * <script type="text/javascript">
925  */
926  /**
927  * @class Array
928  */
929 Roo.applyIf(Array.prototype, {
930     /**
931      * 
932      * Checks whether or not the specified object exists in the array.
933      * @param {Object} o The object to check for
934      * @return {Number} The index of o in the array (or -1 if it is not found)
935      */
936     indexOf : function(o){
937        for (var i = 0, len = this.length; i < len; i++){
938               if(this[i] == o) return i;
939        }
940            return -1;
941     },
942
943     /**
944      * Removes the specified object from the array.  If the object is not found nothing happens.
945      * @param {Object} o The object to remove
946      */
947     remove : function(o){
948        var index = this.indexOf(o);
949        if(index != -1){
950            this.splice(index, 1);
951        }
952     },
953     /**
954      * Map (JS 1.6 compatibility)
955      * @param {Function} function  to call
956      */
957     map : function(fun )
958     {
959         var len = this.length >>> 0;
960         if (typeof fun != "function")
961             throw new TypeError();
962
963         var res = new Array(len);
964         var thisp = arguments[1];
965         for (var i = 0; i < len; i++)
966         {
967             if (i in this)
968                 res[i] = fun.call(thisp, this[i], i, this);
969         }
970
971         return res;
972     }
973     
974 });
975
976
977  /*
978  * Based on:
979  * Ext JS Library 1.1.1
980  * Copyright(c) 2006-2007, Ext JS, LLC.
981  *
982  * Originally Released Under LGPL - original licence link has changed is not relivant.
983  *
984  * Fork - LGPL
985  * <script type="text/javascript">
986  */
987
988 /**
989  * @class Date
990  *
991  * The date parsing and format syntax is a subset of
992  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
993  * supported will provide results equivalent to their PHP versions.
994  *
995  * Following is the list of all currently supported formats:
996  *<pre>
997 Sample date:
998 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
999
1000 Format  Output      Description
1001 ------  ----------  --------------------------------------------------------------
1002   d      10         Day of the month, 2 digits with leading zeros
1003   D      Wed        A textual representation of a day, three letters
1004   j      10         Day of the month without leading zeros
1005   l      Wednesday  A full textual representation of the day of the week
1006   S      th         English ordinal day of month suffix, 2 chars (use with j)
1007   w      3          Numeric representation of the day of the week
1008   z      9          The julian date, or day of the year (0-365)
1009   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1010   F      January    A full textual representation of the month
1011   m      01         Numeric representation of a month, with leading zeros
1012   M      Jan        Month name abbreviation, three letters
1013   n      1          Numeric representation of a month, without leading zeros
1014   t      31         Number of days in the given month
1015   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1016   Y      2007       A full numeric representation of a year, 4 digits
1017   y      07         A two digit representation of a year
1018   a      pm         Lowercase Ante meridiem and Post meridiem
1019   A      PM         Uppercase Ante meridiem and Post meridiem
1020   g      3          12-hour format of an hour without leading zeros
1021   G      15         24-hour format of an hour without leading zeros
1022   h      03         12-hour format of an hour with leading zeros
1023   H      15         24-hour format of an hour with leading zeros
1024   i      05         Minutes with leading zeros
1025   s      01         Seconds, with leading zeros
1026   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1027   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1028   T      CST        Timezone setting of the machine running the code
1029   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1030 </pre>
1031  *
1032  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1033  * <pre><code>
1034 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1035 document.write(dt.format('Y-m-d'));                         //2007-01-10
1036 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1037 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
1038  </code></pre>
1039  *
1040  * Here are some standard date/time patterns that you might find helpful.  They
1041  * are not part of the source of Date.js, but to use them you can simply copy this
1042  * block of code into any script that is included after Date.js and they will also become
1043  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1044  * <pre><code>
1045 Date.patterns = {
1046     ISO8601Long:"Y-m-d H:i:s",
1047     ISO8601Short:"Y-m-d",
1048     ShortDate: "n/j/Y",
1049     LongDate: "l, F d, Y",
1050     FullDateTime: "l, F d, Y g:i:s A",
1051     MonthDay: "F d",
1052     ShortTime: "g:i A",
1053     LongTime: "g:i:s A",
1054     SortableDateTime: "Y-m-d\\TH:i:s",
1055     UniversalSortableDateTime: "Y-m-d H:i:sO",
1056     YearMonth: "F, Y"
1057 };
1058 </code></pre>
1059  *
1060  * Example usage:
1061  * <pre><code>
1062 var dt = new Date();
1063 document.write(dt.format(Date.patterns.ShortDate));
1064  </code></pre>
1065  */
1066
1067 /*
1068  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1069  * They generate precompiled functions from date formats instead of parsing and
1070  * processing the pattern every time you format a date.  These functions are available
1071  * on every Date object (any javascript function).
1072  *
1073  * The original article and download are here:
1074  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1075  *
1076  */
1077  
1078  
1079  // was in core
1080 /**
1081  Returns the number of milliseconds between this date and date
1082  @param {Date} date (optional) Defaults to now
1083  @return {Number} The diff in milliseconds
1084  @member Date getElapsed
1085  */
1086 Date.prototype.getElapsed = function(date) {
1087         return Math.abs((date || new Date()).getTime()-this.getTime());
1088 };
1089 // was in date file..
1090
1091
1092 // private
1093 Date.parseFunctions = {count:0};
1094 // private
1095 Date.parseRegexes = [];
1096 // private
1097 Date.formatFunctions = {count:0};
1098
1099 // private
1100 Date.prototype.dateFormat = function(format) {
1101     if (Date.formatFunctions[format] == null) {
1102         Date.createNewFormat(format);
1103     }
1104     var func = Date.formatFunctions[format];
1105     return this[func]();
1106 };
1107
1108
1109 /**
1110  * Formats a date given the supplied format string
1111  * @param {String} format The format string
1112  * @return {String} The formatted date
1113  * @method
1114  */
1115 Date.prototype.format = Date.prototype.dateFormat;
1116
1117 // private
1118 Date.createNewFormat = function(format) {
1119     var funcName = "format" + Date.formatFunctions.count++;
1120     Date.formatFunctions[format] = funcName;
1121     var code = "Date.prototype." + funcName + " = function(){return ";
1122     var special = false;
1123     var ch = '';
1124     for (var i = 0; i < format.length; ++i) {
1125         ch = format.charAt(i);
1126         if (!special && ch == "\\") {
1127             special = true;
1128         }
1129         else if (special) {
1130             special = false;
1131             code += "'" + String.escape(ch) + "' + ";
1132         }
1133         else {
1134             code += Date.getFormatCode(ch);
1135         }
1136     }
1137     /** eval:var:zzzzzzzzzzzzz */
1138     eval(code.substring(0, code.length - 3) + ";}");
1139 };
1140
1141 // private
1142 Date.getFormatCode = function(character) {
1143     switch (character) {
1144     case "d":
1145         return "String.leftPad(this.getDate(), 2, '0') + ";
1146     case "D":
1147         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1148     case "j":
1149         return "this.getDate() + ";
1150     case "l":
1151         return "Date.dayNames[this.getDay()] + ";
1152     case "S":
1153         return "this.getSuffix() + ";
1154     case "w":
1155         return "this.getDay() + ";
1156     case "z":
1157         return "this.getDayOfYear() + ";
1158     case "W":
1159         return "this.getWeekOfYear() + ";
1160     case "F":
1161         return "Date.monthNames[this.getMonth()] + ";
1162     case "m":
1163         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1164     case "M":
1165         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1166     case "n":
1167         return "(this.getMonth() + 1) + ";
1168     case "t":
1169         return "this.getDaysInMonth() + ";
1170     case "L":
1171         return "(this.isLeapYear() ? 1 : 0) + ";
1172     case "Y":
1173         return "this.getFullYear() + ";
1174     case "y":
1175         return "('' + this.getFullYear()).substring(2, 4) + ";
1176     case "a":
1177         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1178     case "A":
1179         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1180     case "g":
1181         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1182     case "G":
1183         return "this.getHours() + ";
1184     case "h":
1185         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1186     case "H":
1187         return "String.leftPad(this.getHours(), 2, '0') + ";
1188     case "i":
1189         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1190     case "s":
1191         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1192     case "O":
1193         return "this.getGMTOffset() + ";
1194     case "P":
1195         return "this.getGMTColonOffset() + ";
1196     case "T":
1197         return "this.getTimezone() + ";
1198     case "Z":
1199         return "(this.getTimezoneOffset() * -60) + ";
1200     default:
1201         return "'" + String.escape(character) + "' + ";
1202     }
1203 };
1204
1205 /**
1206  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1207  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1208  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1209  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1210  * string or the parse operation will fail.
1211  * Example Usage:
1212 <pre><code>
1213 //dt = Fri May 25 2007 (current date)
1214 var dt = new Date();
1215
1216 //dt = Thu May 25 2006 (today's month/day in 2006)
1217 dt = Date.parseDate("2006", "Y");
1218
1219 //dt = Sun Jan 15 2006 (all date parts specified)
1220 dt = Date.parseDate("2006-1-15", "Y-m-d");
1221
1222 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1223 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1224 </code></pre>
1225  * @param {String} input The unparsed date as a string
1226  * @param {String} format The format the date is in
1227  * @return {Date} The parsed date
1228  * @static
1229  */
1230 Date.parseDate = function(input, format) {
1231     if (Date.parseFunctions[format] == null) {
1232         Date.createParser(format);
1233     }
1234     var func = Date.parseFunctions[format];
1235     return Date[func](input);
1236 };
1237 /**
1238  * @private
1239  */
1240
1241 Date.createParser = function(format) {
1242     var funcName = "parse" + Date.parseFunctions.count++;
1243     var regexNum = Date.parseRegexes.length;
1244     var currentGroup = 1;
1245     Date.parseFunctions[format] = funcName;
1246
1247     var code = "Date." + funcName + " = function(input){\n"
1248         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1249         + "var d = new Date();\n"
1250         + "y = d.getFullYear();\n"
1251         + "m = d.getMonth();\n"
1252         + "d = d.getDate();\n"
1253         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1254         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1255         + "if (results && results.length > 0) {";
1256     var regex = "";
1257
1258     var special = false;
1259     var ch = '';
1260     for (var i = 0; i < format.length; ++i) {
1261         ch = format.charAt(i);
1262         if (!special && ch == "\\") {
1263             special = true;
1264         }
1265         else if (special) {
1266             special = false;
1267             regex += String.escape(ch);
1268         }
1269         else {
1270             var obj = Date.formatCodeToRegex(ch, currentGroup);
1271             currentGroup += obj.g;
1272             regex += obj.s;
1273             if (obj.g && obj.c) {
1274                 code += obj.c;
1275             }
1276         }
1277     }
1278
1279     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1280         + "{v = new Date(y, m, d, h, i, s);}\n"
1281         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1282         + "{v = new Date(y, m, d, h, i);}\n"
1283         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1284         + "{v = new Date(y, m, d, h);}\n"
1285         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1286         + "{v = new Date(y, m, d);}\n"
1287         + "else if (y >= 0 && m >= 0)\n"
1288         + "{v = new Date(y, m);}\n"
1289         + "else if (y >= 0)\n"
1290         + "{v = new Date(y);}\n"
1291         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1292         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1293         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1294         + ";}";
1295
1296     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1297     /** eval:var:zzzzzzzzzzzzz */
1298     eval(code);
1299 };
1300
1301 // private
1302 Date.formatCodeToRegex = function(character, currentGroup) {
1303     switch (character) {
1304     case "D":
1305         return {g:0,
1306         c:null,
1307         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1308     case "j":
1309         return {g:1,
1310             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1311             s:"(\\d{1,2})"}; // day of month without leading zeroes
1312     case "d":
1313         return {g:1,
1314             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1315             s:"(\\d{2})"}; // day of month with leading zeroes
1316     case "l":
1317         return {g:0,
1318             c:null,
1319             s:"(?:" + Date.dayNames.join("|") + ")"};
1320     case "S":
1321         return {g:0,
1322             c:null,
1323             s:"(?:st|nd|rd|th)"};
1324     case "w":
1325         return {g:0,
1326             c:null,
1327             s:"\\d"};
1328     case "z":
1329         return {g:0,
1330             c:null,
1331             s:"(?:\\d{1,3})"};
1332     case "W":
1333         return {g:0,
1334             c:null,
1335             s:"(?:\\d{2})"};
1336     case "F":
1337         return {g:1,
1338             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1339             s:"(" + Date.monthNames.join("|") + ")"};
1340     case "M":
1341         return {g:1,
1342             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1343             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1344     case "n":
1345         return {g:1,
1346             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1347             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1348     case "m":
1349         return {g:1,
1350             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1351             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1352     case "t":
1353         return {g:0,
1354             c:null,
1355             s:"\\d{1,2}"};
1356     case "L":
1357         return {g:0,
1358             c:null,
1359             s:"(?:1|0)"};
1360     case "Y":
1361         return {g:1,
1362             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{4})"};
1364     case "y":
1365         return {g:1,
1366             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1367                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1368             s:"(\\d{1,2})"};
1369     case "a":
1370         return {g:1,
1371             c:"if (results[" + currentGroup + "] == 'am') {\n"
1372                 + "if (h == 12) { h = 0; }\n"
1373                 + "} else { if (h < 12) { h += 12; }}",
1374             s:"(am|pm)"};
1375     case "A":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(AM|PM)"};
1381     case "g":
1382     case "G":
1383         return {g:1,
1384             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1385             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1386     case "h":
1387     case "H":
1388         return {g:1,
1389             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1390             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1391     case "i":
1392         return {g:1,
1393             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1394             s:"(\\d{2})"};
1395     case "s":
1396         return {g:1,
1397             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1398             s:"(\\d{2})"};
1399     case "O":
1400         return {g:1,
1401             c:[
1402                 "o = results[", currentGroup, "];\n",
1403                 "var sn = o.substring(0,1);\n", // get + / - sign
1404                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1405                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1406                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1407                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1408             ].join(""),
1409             s:"([+\-]\\d{2,4})"};
1410     
1411     
1412     case "P":
1413         return {g:1,
1414                 c:[
1415                    "o = results[", currentGroup, "];\n",
1416                    "var sn = o.substring(0,1);\n",
1417                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1418                    "var mn = o.substring(4,6) % 60;\n",
1419                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1420                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1421             ].join(""),
1422             s:"([+\-]\\d{4})"};
1423     case "T":
1424         return {g:0,
1425             c:null,
1426             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1427     case "Z":
1428         return {g:1,
1429             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1430                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1431             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1432     default:
1433         return {g:0,
1434             c:null,
1435             s:String.escape(character)};
1436     }
1437 };
1438
1439 /**
1440  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1441  * @return {String} The abbreviated timezone name (e.g. 'CST')
1442  */
1443 Date.prototype.getTimezone = function() {
1444     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1445 };
1446
1447 /**
1448  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1449  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1450  */
1451 Date.prototype.getGMTOffset = function() {
1452     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1453         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1454         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1455 };
1456
1457 /**
1458  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1459  * @return {String} 2-characters representing hours and 2-characters representing minutes
1460  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1461  */
1462 Date.prototype.getGMTColonOffset = function() {
1463         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1464                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1465                 + ":"
1466                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1467 }
1468
1469 /**
1470  * Get the numeric day number of the year, adjusted for leap year.
1471  * @return {Number} 0 through 364 (365 in leap years)
1472  */
1473 Date.prototype.getDayOfYear = function() {
1474     var num = 0;
1475     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1476     for (var i = 0; i < this.getMonth(); ++i) {
1477         num += Date.daysInMonth[i];
1478     }
1479     return num + this.getDate() - 1;
1480 };
1481
1482 /**
1483  * Get the string representation of the numeric week number of the year
1484  * (equivalent to the format specifier 'W').
1485  * @return {String} '00' through '52'
1486  */
1487 Date.prototype.getWeekOfYear = function() {
1488     // Skip to Thursday of this week
1489     var now = this.getDayOfYear() + (4 - this.getDay());
1490     // Find the first Thursday of the year
1491     var jan1 = new Date(this.getFullYear(), 0, 1);
1492     var then = (7 - jan1.getDay() + 4);
1493     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1494 };
1495
1496 /**
1497  * Whether or not the current date is in a leap year.
1498  * @return {Boolean} True if the current date is in a leap year, else false
1499  */
1500 Date.prototype.isLeapYear = function() {
1501     var year = this.getFullYear();
1502     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1503 };
1504
1505 /**
1506  * Get the first day of the current month, adjusted for leap year.  The returned value
1507  * is the numeric day index within the week (0-6) which can be used in conjunction with
1508  * the {@link #monthNames} array to retrieve the textual day name.
1509  * Example:
1510  *<pre><code>
1511 var dt = new Date('1/10/2007');
1512 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1513 </code></pre>
1514  * @return {Number} The day number (0-6)
1515  */
1516 Date.prototype.getFirstDayOfMonth = function() {
1517     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1518     return (day < 0) ? (day + 7) : day;
1519 };
1520
1521 /**
1522  * Get the last day of the current month, adjusted for leap year.  The returned value
1523  * is the numeric day index within the week (0-6) which can be used in conjunction with
1524  * the {@link #monthNames} array to retrieve the textual day name.
1525  * Example:
1526  *<pre><code>
1527 var dt = new Date('1/10/2007');
1528 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1529 </code></pre>
1530  * @return {Number} The day number (0-6)
1531  */
1532 Date.prototype.getLastDayOfMonth = function() {
1533     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1534     return (day < 0) ? (day + 7) : day;
1535 };
1536
1537
1538 /**
1539  * Get the first date of this date's month
1540  * @return {Date}
1541  */
1542 Date.prototype.getFirstDateOfMonth = function() {
1543     return new Date(this.getFullYear(), this.getMonth(), 1);
1544 };
1545
1546 /**
1547  * Get the last date of this date's month
1548  * @return {Date}
1549  */
1550 Date.prototype.getLastDateOfMonth = function() {
1551     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1552 };
1553 /**
1554  * Get the number of days in the current month, adjusted for leap year.
1555  * @return {Number} The number of days in the month
1556  */
1557 Date.prototype.getDaysInMonth = function() {
1558     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1559     return Date.daysInMonth[this.getMonth()];
1560 };
1561
1562 /**
1563  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1564  * @return {String} 'st, 'nd', 'rd' or 'th'
1565  */
1566 Date.prototype.getSuffix = function() {
1567     switch (this.getDate()) {
1568         case 1:
1569         case 21:
1570         case 31:
1571             return "st";
1572         case 2:
1573         case 22:
1574             return "nd";
1575         case 3:
1576         case 23:
1577             return "rd";
1578         default:
1579             return "th";
1580     }
1581 };
1582
1583 // private
1584 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1585
1586 /**
1587  * An array of textual month names.
1588  * Override these values for international dates, for example...
1589  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.monthNames =
1594    ["January",
1595     "February",
1596     "March",
1597     "April",
1598     "May",
1599     "June",
1600     "July",
1601     "August",
1602     "September",
1603     "October",
1604     "November",
1605     "December"];
1606
1607 /**
1608  * An array of textual day names.
1609  * Override these values for international dates, for example...
1610  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1611  * @type Array
1612  * @static
1613  */
1614 Date.dayNames =
1615    ["Sunday",
1616     "Monday",
1617     "Tuesday",
1618     "Wednesday",
1619     "Thursday",
1620     "Friday",
1621     "Saturday"];
1622
1623 // private
1624 Date.y2kYear = 50;
1625 // private
1626 Date.monthNumbers = {
1627     Jan:0,
1628     Feb:1,
1629     Mar:2,
1630     Apr:3,
1631     May:4,
1632     Jun:5,
1633     Jul:6,
1634     Aug:7,
1635     Sep:8,
1636     Oct:9,
1637     Nov:10,
1638     Dec:11};
1639
1640 /**
1641  * Creates and returns a new Date instance with the exact same date value as the called instance.
1642  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1643  * variable will also be changed.  When the intention is to create a new variable that will not
1644  * modify the original instance, you should create a clone.
1645  *
1646  * Example of correctly cloning a date:
1647  * <pre><code>
1648 //wrong way:
1649 var orig = new Date('10/1/2006');
1650 var copy = orig;
1651 copy.setDate(5);
1652 document.write(orig);  //returns 'Thu Oct 05 2006'!
1653
1654 //correct way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig.clone();
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 01 2006'
1659 </code></pre>
1660  * @return {Date} The new Date instance
1661  */
1662 Date.prototype.clone = function() {
1663         return new Date(this.getTime());
1664 };
1665
1666 /**
1667  * Clears any time information from this date
1668  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1669  @return {Date} this or the clone
1670  */
1671 Date.prototype.clearTime = function(clone){
1672     if(clone){
1673         return this.clone().clearTime();
1674     }
1675     this.setHours(0);
1676     this.setMinutes(0);
1677     this.setSeconds(0);
1678     this.setMilliseconds(0);
1679     return this;
1680 };
1681
1682 // private
1683 // safari setMonth is broken
1684 if(Roo.isSafari){
1685     Date.brokenSetMonth = Date.prototype.setMonth;
1686         Date.prototype.setMonth = function(num){
1687                 if(num <= -1){
1688                         var n = Math.ceil(-num);
1689                         var back_year = Math.ceil(n/12);
1690                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1691                         this.setFullYear(this.getFullYear() - back_year);
1692                         return Date.brokenSetMonth.call(this, month);
1693                 } else {
1694                         return Date.brokenSetMonth.apply(this, arguments);
1695                 }
1696         };
1697 }
1698
1699 /** Date interval constant 
1700 * @static 
1701 * @type String */
1702 Date.MILLI = "ms";
1703 /** Date interval constant 
1704 * @static 
1705 * @type String */
1706 Date.SECOND = "s";
1707 /** Date interval constant 
1708 * @static 
1709 * @type String */
1710 Date.MINUTE = "mi";
1711 /** Date interval constant 
1712 * @static 
1713 * @type String */
1714 Date.HOUR = "h";
1715 /** Date interval constant 
1716 * @static 
1717 * @type String */
1718 Date.DAY = "d";
1719 /** Date interval constant 
1720 * @static 
1721 * @type String */
1722 Date.MONTH = "mo";
1723 /** Date interval constant 
1724 * @static 
1725 * @type String */
1726 Date.YEAR = "y";
1727
1728 /**
1729  * Provides a convenient method of performing basic date arithmetic.  This method
1730  * does not modify the Date instance being called - it creates and returns
1731  * a new Date instance containing the resulting date value.
1732  *
1733  * Examples:
1734  * <pre><code>
1735 //Basic usage:
1736 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1737 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1738
1739 //Negative values will subtract correctly:
1740 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1741 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1742
1743 //You can even chain several calls together in one line!
1744 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1745 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1746  </code></pre>
1747  *
1748  * @param {String} interval   A valid date interval enum value
1749  * @param {Number} value      The amount to add to the current date
1750  * @return {Date} The new Date instance
1751  */
1752 Date.prototype.add = function(interval, value){
1753   var d = this.clone();
1754   if (!interval || value === 0) return d;
1755   switch(interval.toLowerCase()){
1756     case Date.MILLI:
1757       d.setMilliseconds(this.getMilliseconds() + value);
1758       break;
1759     case Date.SECOND:
1760       d.setSeconds(this.getSeconds() + value);
1761       break;
1762     case Date.MINUTE:
1763       d.setMinutes(this.getMinutes() + value);
1764       break;
1765     case Date.HOUR:
1766       d.setHours(this.getHours() + value);
1767       break;
1768     case Date.DAY:
1769       d.setDate(this.getDate() + value);
1770       break;
1771     case Date.MONTH:
1772       var day = this.getDate();
1773       if(day > 28){
1774           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1775       }
1776       d.setDate(day);
1777       d.setMonth(this.getMonth() + value);
1778       break;
1779     case Date.YEAR:
1780       d.setFullYear(this.getFullYear() + value);
1781       break;
1782   }
1783   return d;
1784 };
1785 /*
1786  * Based on:
1787  * Ext JS Library 1.1.1
1788  * Copyright(c) 2006-2007, Ext JS, LLC.
1789  *
1790  * Originally Released Under LGPL - original licence link has changed is not relivant.
1791  *
1792  * Fork - LGPL
1793  * <script type="text/javascript">
1794  */
1795
1796 /**
1797  * @class Roo.lib.Dom
1798  * @static
1799  * 
1800  * Dom utils (from YIU afaik)
1801  * 
1802  **/
1803 Roo.lib.Dom = {
1804     /**
1805      * Get the view width
1806      * @param {Boolean} full True will get the full document, otherwise it's the view width
1807      * @return {Number} The width
1808      */
1809      
1810     getViewWidth : function(full) {
1811         return full ? this.getDocumentWidth() : this.getViewportWidth();
1812     },
1813     /**
1814      * Get the view height
1815      * @param {Boolean} full True will get the full document, otherwise it's the view height
1816      * @return {Number} The height
1817      */
1818     getViewHeight : function(full) {
1819         return full ? this.getDocumentHeight() : this.getViewportHeight();
1820     },
1821
1822     getDocumentHeight: function() {
1823         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1824         return Math.max(scrollHeight, this.getViewportHeight());
1825     },
1826
1827     getDocumentWidth: function() {
1828         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1829         return Math.max(scrollWidth, this.getViewportWidth());
1830     },
1831
1832     getViewportHeight: function() {
1833         var height = self.innerHeight;
1834         var mode = document.compatMode;
1835
1836         if ((mode || Roo.isIE) && !Roo.isOpera) {
1837             height = (mode == "CSS1Compat") ?
1838                      document.documentElement.clientHeight :
1839                      document.body.clientHeight;
1840         }
1841
1842         return height;
1843     },
1844
1845     getViewportWidth: function() {
1846         var width = self.innerWidth;
1847         var mode = document.compatMode;
1848
1849         if (mode || Roo.isIE) {
1850             width = (mode == "CSS1Compat") ?
1851                     document.documentElement.clientWidth :
1852                     document.body.clientWidth;
1853         }
1854         return width;
1855     },
1856
1857     isAncestor : function(p, c) {
1858         p = Roo.getDom(p);
1859         c = Roo.getDom(c);
1860         if (!p || !c) {
1861             return false;
1862         }
1863
1864         if (p.contains && !Roo.isSafari) {
1865             return p.contains(c);
1866         } else if (p.compareDocumentPosition) {
1867             return !!(p.compareDocumentPosition(c) & 16);
1868         } else {
1869             var parent = c.parentNode;
1870             while (parent) {
1871                 if (parent == p) {
1872                     return true;
1873                 }
1874                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1875                     return false;
1876                 }
1877                 parent = parent.parentNode;
1878             }
1879             return false;
1880         }
1881     },
1882
1883     getRegion : function(el) {
1884         return Roo.lib.Region.getRegion(el);
1885     },
1886
1887     getY : function(el) {
1888         return this.getXY(el)[1];
1889     },
1890
1891     getX : function(el) {
1892         return this.getXY(el)[0];
1893     },
1894
1895     getXY : function(el) {
1896         var p, pe, b, scroll, bd = document.body;
1897         el = Roo.getDom(el);
1898         var fly = Roo.lib.AnimBase.fly;
1899         if (el.getBoundingClientRect) {
1900             b = el.getBoundingClientRect();
1901             scroll = fly(document).getScroll();
1902             return [b.left + scroll.left, b.top + scroll.top];
1903         }
1904         var x = 0, y = 0;
1905
1906         p = el;
1907
1908         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1909
1910         while (p) {
1911
1912             x += p.offsetLeft;
1913             y += p.offsetTop;
1914
1915             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1916                 hasAbsolute = true;
1917             }
1918
1919             if (Roo.isGecko) {
1920                 pe = fly(p);
1921
1922                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1923                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1924
1925
1926                 x += bl;
1927                 y += bt;
1928
1929
1930                 if (p != el && pe.getStyle('overflow') != 'visible') {
1931                     x += bl;
1932                     y += bt;
1933                 }
1934             }
1935             p = p.offsetParent;
1936         }
1937
1938         if (Roo.isSafari && hasAbsolute) {
1939             x -= bd.offsetLeft;
1940             y -= bd.offsetTop;
1941         }
1942
1943         if (Roo.isGecko && !hasAbsolute) {
1944             var dbd = fly(bd);
1945             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1946             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1947         }
1948
1949         p = el.parentNode;
1950         while (p && p != bd) {
1951             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1952                 x -= p.scrollLeft;
1953                 y -= p.scrollTop;
1954             }
1955             p = p.parentNode;
1956         }
1957         return [x, y];
1958     },
1959  
1960   
1961
1962
1963     setXY : function(el, xy) {
1964         el = Roo.fly(el, '_setXY');
1965         el.position();
1966         var pts = el.translatePoints(xy);
1967         if (xy[0] !== false) {
1968             el.dom.style.left = pts.left + "px";
1969         }
1970         if (xy[1] !== false) {
1971             el.dom.style.top = pts.top + "px";
1972         }
1973     },
1974
1975     setX : function(el, x) {
1976         this.setXY(el, [x, false]);
1977     },
1978
1979     setY : function(el, y) {
1980         this.setXY(el, [false, y]);
1981     }
1982 };
1983 /*
1984  * Portions of this file are based on pieces of Yahoo User Interface Library
1985  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1986  * YUI licensed under the BSD License:
1987  * http://developer.yahoo.net/yui/license.txt
1988  * <script type="text/javascript">
1989  *
1990  */
1991
1992 Roo.lib.Event = function() {
1993     var loadComplete = false;
1994     var listeners = [];
1995     var unloadListeners = [];
1996     var retryCount = 0;
1997     var onAvailStack = [];
1998     var counter = 0;
1999     var lastError = null;
2000
2001     return {
2002         POLL_RETRYS: 200,
2003         POLL_INTERVAL: 20,
2004         EL: 0,
2005         TYPE: 1,
2006         FN: 2,
2007         WFN: 3,
2008         OBJ: 3,
2009         ADJ_SCOPE: 4,
2010         _interval: null,
2011
2012         startInterval: function() {
2013             if (!this._interval) {
2014                 var self = this;
2015                 var callback = function() {
2016                     self._tryPreloadAttach();
2017                 };
2018                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2019
2020             }
2021         },
2022
2023         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2024             onAvailStack.push({ id:         p_id,
2025                 fn:         p_fn,
2026                 obj:        p_obj,
2027                 override:   p_override,
2028                 checkReady: false    });
2029
2030             retryCount = this.POLL_RETRYS;
2031             this.startInterval();
2032         },
2033
2034
2035         addListener: function(el, eventName, fn) {
2036             el = Roo.getDom(el);
2037             if (!el || !fn) {
2038                 return false;
2039             }
2040
2041             if ("unload" == eventName) {
2042                 unloadListeners[unloadListeners.length] =
2043                 [el, eventName, fn];
2044                 return true;
2045             }
2046
2047             var wrappedFn = function(e) {
2048                 return fn(Roo.lib.Event.getEvent(e));
2049             };
2050
2051             var li = [el, eventName, fn, wrappedFn];
2052
2053             var index = listeners.length;
2054             listeners[index] = li;
2055
2056             this.doAdd(el, eventName, wrappedFn, false);
2057             return true;
2058
2059         },
2060
2061
2062         removeListener: function(el, eventName, fn) {
2063             var i, len;
2064
2065             el = Roo.getDom(el);
2066
2067             if(!fn) {
2068                 return this.purgeElement(el, false, eventName);
2069             }
2070
2071
2072             if ("unload" == eventName) {
2073
2074                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2075                     var li = unloadListeners[i];
2076                     if (li &&
2077                         li[0] == el &&
2078                         li[1] == eventName &&
2079                         li[2] == fn) {
2080                         unloadListeners.splice(i, 1);
2081                         return true;
2082                     }
2083                 }
2084
2085                 return false;
2086             }
2087
2088             var cacheItem = null;
2089
2090
2091             var index = arguments[3];
2092
2093             if ("undefined" == typeof index) {
2094                 index = this._getCacheIndex(el, eventName, fn);
2095             }
2096
2097             if (index >= 0) {
2098                 cacheItem = listeners[index];
2099             }
2100
2101             if (!el || !cacheItem) {
2102                 return false;
2103             }
2104
2105             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2106
2107             delete listeners[index][this.WFN];
2108             delete listeners[index][this.FN];
2109             listeners.splice(index, 1);
2110
2111             return true;
2112
2113         },
2114
2115
2116         getTarget: function(ev, resolveTextNode) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var t = ev.target || ev.srcElement;
2120             return this.resolveTextNode(t);
2121         },
2122
2123
2124         resolveTextNode: function(node) {
2125             if (Roo.isSafari && node && 3 == node.nodeType) {
2126                 return node.parentNode;
2127             } else {
2128                 return node;
2129             }
2130         },
2131
2132
2133         getPageX: function(ev) {
2134             ev = ev.browserEvent || ev;
2135             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2136             var x = ev.pageX;
2137             if (!x && 0 !== x) {
2138                 x = ev.clientX || 0;
2139
2140                 if (Roo.isIE) {
2141                     x += this.getScroll()[1];
2142                 }
2143             }
2144
2145             return x;
2146         },
2147
2148
2149         getPageY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             var y = ev.pageY;
2153             if (!y && 0 !== y) {
2154                 y = ev.clientY || 0;
2155
2156                 if (Roo.isIE) {
2157                     y += this.getScroll()[0];
2158                 }
2159             }
2160
2161
2162             return y;
2163         },
2164
2165
2166         getXY: function(ev) {
2167             ev = ev.browserEvent || ev;
2168             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2169             return [this.getPageX(ev), this.getPageY(ev)];
2170         },
2171
2172
2173         getRelatedTarget: function(ev) {
2174             ev = ev.browserEvent || ev;
2175             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2176             var t = ev.relatedTarget;
2177             if (!t) {
2178                 if (ev.type == "mouseout") {
2179                     t = ev.toElement;
2180                 } else if (ev.type == "mouseover") {
2181                     t = ev.fromElement;
2182                 }
2183             }
2184
2185             return this.resolveTextNode(t);
2186         },
2187
2188
2189         getTime: function(ev) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             if (!ev.time) {
2193                 var t = new Date().getTime();
2194                 try {
2195                     ev.time = t;
2196                 } catch(ex) {
2197                     this.lastError = ex;
2198                     return t;
2199                 }
2200             }
2201
2202             return ev.time;
2203         },
2204
2205
2206         stopEvent: function(ev) {
2207             this.stopPropagation(ev);
2208             this.preventDefault(ev);
2209         },
2210
2211
2212         stopPropagation: function(ev) {
2213             ev = ev.browserEvent || ev;
2214             if (ev.stopPropagation) {
2215                 ev.stopPropagation();
2216             } else {
2217                 ev.cancelBubble = true;
2218             }
2219         },
2220
2221
2222         preventDefault: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             if(ev.preventDefault) {
2225                 ev.preventDefault();
2226             } else {
2227                 ev.returnValue = false;
2228             }
2229         },
2230
2231
2232         getEvent: function(e) {
2233             var ev = e || window.event;
2234             if (!ev) {
2235                 var c = this.getEvent.caller;
2236                 while (c) {
2237                     ev = c.arguments[0];
2238                     if (ev && Event == ev.constructor) {
2239                         break;
2240                     }
2241                     c = c.caller;
2242                 }
2243             }
2244             return ev;
2245         },
2246
2247
2248         getCharCode: function(ev) {
2249             ev = ev.browserEvent || ev;
2250             return ev.charCode || ev.keyCode || 0;
2251         },
2252
2253
2254         _getCacheIndex: function(el, eventName, fn) {
2255             for (var i = 0,len = listeners.length; i < len; ++i) {
2256                 var li = listeners[i];
2257                 if (li &&
2258                     li[this.FN] == fn &&
2259                     li[this.EL] == el &&
2260                     li[this.TYPE] == eventName) {
2261                     return i;
2262                 }
2263             }
2264
2265             return -1;
2266         },
2267
2268
2269         elCache: {},
2270
2271
2272         getEl: function(id) {
2273             return document.getElementById(id);
2274         },
2275
2276
2277         clearCache: function() {
2278         },
2279
2280
2281         _load: function(e) {
2282             loadComplete = true;
2283             var EU = Roo.lib.Event;
2284
2285
2286             if (Roo.isIE) {
2287                 EU.doRemove(window, "load", EU._load);
2288             }
2289         },
2290
2291
2292         _tryPreloadAttach: function() {
2293
2294             if (this.locked) {
2295                 return false;
2296             }
2297
2298             this.locked = true;
2299
2300
2301             var tryAgain = !loadComplete;
2302             if (!tryAgain) {
2303                 tryAgain = (retryCount > 0);
2304             }
2305
2306
2307             var notAvail = [];
2308             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2309                 var item = onAvailStack[i];
2310                 if (item) {
2311                     var el = this.getEl(item.id);
2312
2313                     if (el) {
2314                         if (!item.checkReady ||
2315                             loadComplete ||
2316                             el.nextSibling ||
2317                             (document && document.body)) {
2318
2319                             var scope = el;
2320                             if (item.override) {
2321                                 if (item.override === true) {
2322                                     scope = item.obj;
2323                                 } else {
2324                                     scope = item.override;
2325                                 }
2326                             }
2327                             item.fn.call(scope, item.obj);
2328                             onAvailStack[i] = null;
2329                         }
2330                     } else {
2331                         notAvail.push(item);
2332                     }
2333                 }
2334             }
2335
2336             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2337
2338             if (tryAgain) {
2339
2340                 this.startInterval();
2341             } else {
2342                 clearInterval(this._interval);
2343                 this._interval = null;
2344             }
2345
2346             this.locked = false;
2347
2348             return true;
2349
2350         },
2351
2352
2353         purgeElement: function(el, recurse, eventName) {
2354             var elListeners = this.getListeners(el, eventName);
2355             if (elListeners) {
2356                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2357                     var l = elListeners[i];
2358                     this.removeListener(el, l.type, l.fn);
2359                 }
2360             }
2361
2362             if (recurse && el && el.childNodes) {
2363                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2364                     this.purgeElement(el.childNodes[i], recurse, eventName);
2365                 }
2366             }
2367         },
2368
2369
2370         getListeners: function(el, eventName) {
2371             var results = [], searchLists;
2372             if (!eventName) {
2373                 searchLists = [listeners, unloadListeners];
2374             } else if (eventName == "unload") {
2375                 searchLists = [unloadListeners];
2376             } else {
2377                 searchLists = [listeners];
2378             }
2379
2380             for (var j = 0; j < searchLists.length; ++j) {
2381                 var searchList = searchLists[j];
2382                 if (searchList && searchList.length > 0) {
2383                     for (var i = 0,len = searchList.length; i < len; ++i) {
2384                         var l = searchList[i];
2385                         if (l && l[this.EL] === el &&
2386                             (!eventName || eventName === l[this.TYPE])) {
2387                             results.push({
2388                                 type:   l[this.TYPE],
2389                                 fn:     l[this.FN],
2390                                 obj:    l[this.OBJ],
2391                                 adjust: l[this.ADJ_SCOPE],
2392                                 index:  i
2393                             });
2394                         }
2395                     }
2396                 }
2397             }
2398
2399             return (results.length) ? results : null;
2400         },
2401
2402
2403         _unload: function(e) {
2404
2405             var EU = Roo.lib.Event, i, j, l, len, index;
2406
2407             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2408                 l = unloadListeners[i];
2409                 if (l) {
2410                     var scope = window;
2411                     if (l[EU.ADJ_SCOPE]) {
2412                         if (l[EU.ADJ_SCOPE] === true) {
2413                             scope = l[EU.OBJ];
2414                         } else {
2415                             scope = l[EU.ADJ_SCOPE];
2416                         }
2417                     }
2418                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2419                     unloadListeners[i] = null;
2420                     l = null;
2421                     scope = null;
2422                 }
2423             }
2424
2425             unloadListeners = null;
2426
2427             if (listeners && listeners.length > 0) {
2428                 j = listeners.length;
2429                 while (j) {
2430                     index = j - 1;
2431                     l = listeners[index];
2432                     if (l) {
2433                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2434                                 l[EU.FN], index);
2435                     }
2436                     j = j - 1;
2437                 }
2438                 l = null;
2439
2440                 EU.clearCache();
2441             }
2442
2443             EU.doRemove(window, "unload", EU._unload);
2444
2445         },
2446
2447
2448         getScroll: function() {
2449             var dd = document.documentElement, db = document.body;
2450             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2451                 return [dd.scrollTop, dd.scrollLeft];
2452             } else if (db) {
2453                 return [db.scrollTop, db.scrollLeft];
2454             } else {
2455                 return [0, 0];
2456             }
2457         },
2458
2459
2460         doAdd: function () {
2461             if (window.addEventListener) {
2462                 return function(el, eventName, fn, capture) {
2463                     el.addEventListener(eventName, fn, (capture));
2464                 };
2465             } else if (window.attachEvent) {
2466                 return function(el, eventName, fn, capture) {
2467                     el.attachEvent("on" + eventName, fn);
2468                 };
2469             } else {
2470                 return function() {
2471                 };
2472             }
2473         }(),
2474
2475
2476         doRemove: function() {
2477             if (window.removeEventListener) {
2478                 return function (el, eventName, fn, capture) {
2479                     el.removeEventListener(eventName, fn, (capture));
2480                 };
2481             } else if (window.detachEvent) {
2482                 return function (el, eventName, fn) {
2483                     el.detachEvent("on" + eventName, fn);
2484                 };
2485             } else {
2486                 return function() {
2487                 };
2488             }
2489         }()
2490     };
2491     
2492 }();
2493 (function() {     
2494    
2495     var E = Roo.lib.Event;
2496     E.on = E.addListener;
2497     E.un = E.removeListener;
2498
2499     if (document && document.body) {
2500         E._load();
2501     } else {
2502         E.doAdd(window, "load", E._load);
2503     }
2504     E.doAdd(window, "unload", E._unload);
2505     E._tryPreloadAttach();
2506 })();
2507
2508 /*
2509  * Portions of this file are based on pieces of Yahoo User Interface Library
2510  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2511  * YUI licensed under the BSD License:
2512  * http://developer.yahoo.net/yui/license.txt
2513  * <script type="text/javascript">
2514  *
2515  */
2516
2517 (function() {
2518     /**
2519      * @class Roo.lib.Ajax
2520      *
2521      */
2522     Roo.lib.Ajax = {
2523         /**
2524          * @static 
2525          */
2526         request : function(method, uri, cb, data, options) {
2527             if(options){
2528                 var hs = options.headers;
2529                 if(hs){
2530                     for(var h in hs){
2531                         if(hs.hasOwnProperty(h)){
2532                             this.initHeader(h, hs[h], false);
2533                         }
2534                     }
2535                 }
2536                 if(options.xmlData){
2537                     this.initHeader('Content-Type', 'text/xml', false);
2538                     method = 'POST';
2539                     data = options.xmlData;
2540                 }
2541             }
2542
2543             return this.asyncRequest(method, uri, cb, data);
2544         },
2545
2546         serializeForm : function(form) {
2547             if(typeof form == 'string') {
2548                 form = (document.getElementById(form) || document.forms[form]);
2549             }
2550
2551             var el, name, val, disabled, data = '', hasSubmit = false;
2552             for (var i = 0; i < form.elements.length; i++) {
2553                 el = form.elements[i];
2554                 disabled = form.elements[i].disabled;
2555                 name = form.elements[i].name;
2556                 val = form.elements[i].value;
2557
2558                 if (!disabled && name){
2559                     switch (el.type)
2560                             {
2561                         case 'select-one':
2562                         case 'select-multiple':
2563                             for (var j = 0; j < el.options.length; j++) {
2564                                 if (el.options[j].selected) {
2565                                     if (Roo.isIE) {
2566                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2567                                     }
2568                                     else {
2569                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2570                                     }
2571                                 }
2572                             }
2573                             break;
2574                         case 'radio':
2575                         case 'checkbox':
2576                             if (el.checked) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                             }
2579                             break;
2580                         case 'file':
2581
2582                         case undefined:
2583
2584                         case 'reset':
2585
2586                         case 'button':
2587
2588                             break;
2589                         case 'submit':
2590                             if(hasSubmit == false) {
2591                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2592                                 hasSubmit = true;
2593                             }
2594                             break;
2595                         default:
2596                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2597                             break;
2598                     }
2599                 }
2600             }
2601             data = data.substr(0, data.length - 1);
2602             return data;
2603         },
2604
2605         headers:{},
2606
2607         hasHeaders:false,
2608
2609         useDefaultHeader:true,
2610
2611         defaultPostHeader:'application/x-www-form-urlencoded',
2612
2613         useDefaultXhrHeader:true,
2614
2615         defaultXhrHeader:'XMLHttpRequest',
2616
2617         hasDefaultHeaders:true,
2618
2619         defaultHeaders:{},
2620
2621         poll:{},
2622
2623         timeout:{},
2624
2625         pollInterval:50,
2626
2627         transactionId:0,
2628
2629         setProgId:function(id)
2630         {
2631             this.activeX.unshift(id);
2632         },
2633
2634         setDefaultPostHeader:function(b)
2635         {
2636             this.useDefaultHeader = b;
2637         },
2638
2639         setDefaultXhrHeader:function(b)
2640         {
2641             this.useDefaultXhrHeader = b;
2642         },
2643
2644         setPollingInterval:function(i)
2645         {
2646             if (typeof i == 'number' && isFinite(i)) {
2647                 this.pollInterval = i;
2648             }
2649         },
2650
2651         createXhrObject:function(transactionId)
2652         {
2653             var obj,http;
2654             try
2655             {
2656
2657                 http = new XMLHttpRequest();
2658
2659                 obj = { conn:http, tId:transactionId };
2660             }
2661             catch(e)
2662             {
2663                 for (var i = 0; i < this.activeX.length; ++i) {
2664                     try
2665                     {
2666
2667                         http = new ActiveXObject(this.activeX[i]);
2668
2669                         obj = { conn:http, tId:transactionId };
2670                         break;
2671                     }
2672                     catch(e) {
2673                     }
2674                 }
2675             }
2676             finally
2677             {
2678                 return obj;
2679             }
2680         },
2681
2682         getConnectionObject:function()
2683         {
2684             var o;
2685             var tId = this.transactionId;
2686
2687             try
2688             {
2689                 o = this.createXhrObject(tId);
2690                 if (o) {
2691                     this.transactionId++;
2692                 }
2693             }
2694             catch(e) {
2695             }
2696             finally
2697             {
2698                 return o;
2699             }
2700         },
2701
2702         asyncRequest:function(method, uri, callback, postData)
2703         {
2704             var o = this.getConnectionObject();
2705
2706             if (!o) {
2707                 return null;
2708             }
2709             else {
2710                 o.conn.open(method, uri, true);
2711
2712                 if (this.useDefaultXhrHeader) {
2713                     if (!this.defaultHeaders['X-Requested-With']) {
2714                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2715                     }
2716                 }
2717
2718                 if(postData && this.useDefaultHeader){
2719                     this.initHeader('Content-Type', this.defaultPostHeader);
2720                 }
2721
2722                  if (this.hasDefaultHeaders || this.hasHeaders) {
2723                     this.setHeader(o);
2724                 }
2725
2726                 this.handleReadyState(o, callback);
2727                 o.conn.send(postData || null);
2728
2729                 return o;
2730             }
2731         },
2732
2733         handleReadyState:function(o, callback)
2734         {
2735             var oConn = this;
2736
2737             if (callback && callback.timeout) {
2738                 
2739                 this.timeout[o.tId] = window.setTimeout(function() {
2740                     oConn.abort(o, callback, true);
2741                 }, callback.timeout);
2742             }
2743
2744             this.poll[o.tId] = window.setInterval(
2745                     function() {
2746                         if (o.conn && o.conn.readyState == 4) {
2747                             window.clearInterval(oConn.poll[o.tId]);
2748                             delete oConn.poll[o.tId];
2749
2750                             if(callback && callback.timeout) {
2751                                 window.clearTimeout(oConn.timeout[o.tId]);
2752                                 delete oConn.timeout[o.tId];
2753                             }
2754
2755                             oConn.handleTransactionResponse(o, callback);
2756                         }
2757                     }
2758                     , this.pollInterval);
2759         },
2760
2761         handleTransactionResponse:function(o, callback, isAbort)
2762         {
2763
2764             if (!callback) {
2765                 this.releaseObject(o);
2766                 return;
2767             }
2768
2769             var httpStatus, responseObject;
2770
2771             try
2772             {
2773                 if (o.conn.status !== undefined && o.conn.status != 0) {
2774                     httpStatus = o.conn.status;
2775                 }
2776                 else {
2777                     httpStatus = 13030;
2778                 }
2779             }
2780             catch(e) {
2781
2782
2783                 httpStatus = 13030;
2784             }
2785
2786             if (httpStatus >= 200 && httpStatus < 300) {
2787                 responseObject = this.createResponseObject(o, callback.argument);
2788                 if (callback.success) {
2789                     if (!callback.scope) {
2790                         callback.success(responseObject);
2791                     }
2792                     else {
2793
2794
2795                         callback.success.apply(callback.scope, [responseObject]);
2796                     }
2797                 }
2798             }
2799             else {
2800                 switch (httpStatus) {
2801
2802                     case 12002:
2803                     case 12029:
2804                     case 12030:
2805                     case 12031:
2806                     case 12152:
2807                     case 13030:
2808                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2809                         if (callback.failure) {
2810                             if (!callback.scope) {
2811                                 callback.failure(responseObject);
2812                             }
2813                             else {
2814                                 callback.failure.apply(callback.scope, [responseObject]);
2815                             }
2816                         }
2817                         break;
2818                     default:
2819                         responseObject = this.createResponseObject(o, callback.argument);
2820                         if (callback.failure) {
2821                             if (!callback.scope) {
2822                                 callback.failure(responseObject);
2823                             }
2824                             else {
2825                                 callback.failure.apply(callback.scope, [responseObject]);
2826                             }
2827                         }
2828                 }
2829             }
2830
2831             this.releaseObject(o);
2832             responseObject = null;
2833         },
2834
2835         createResponseObject:function(o, callbackArg)
2836         {
2837             var obj = {};
2838             var headerObj = {};
2839
2840             try
2841             {
2842                 var headerStr = o.conn.getAllResponseHeaders();
2843                 var header = headerStr.split('\n');
2844                 for (var i = 0; i < header.length; i++) {
2845                     var delimitPos = header[i].indexOf(':');
2846                     if (delimitPos != -1) {
2847                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2848                     }
2849                 }
2850             }
2851             catch(e) {
2852             }
2853
2854             obj.tId = o.tId;
2855             obj.status = o.conn.status;
2856             obj.statusText = o.conn.statusText;
2857             obj.getResponseHeader = headerObj;
2858             obj.getAllResponseHeaders = headerStr;
2859             obj.responseText = o.conn.responseText;
2860             obj.responseXML = o.conn.responseXML;
2861
2862             if (typeof callbackArg !== undefined) {
2863                 obj.argument = callbackArg;
2864             }
2865
2866             return obj;
2867         },
2868
2869         createExceptionObject:function(tId, callbackArg, isAbort)
2870         {
2871             var COMM_CODE = 0;
2872             var COMM_ERROR = 'communication failure';
2873             var ABORT_CODE = -1;
2874             var ABORT_ERROR = 'transaction aborted';
2875
2876             var obj = {};
2877
2878             obj.tId = tId;
2879             if (isAbort) {
2880                 obj.status = ABORT_CODE;
2881                 obj.statusText = ABORT_ERROR;
2882             }
2883             else {
2884                 obj.status = COMM_CODE;
2885                 obj.statusText = COMM_ERROR;
2886             }
2887
2888             if (callbackArg) {
2889                 obj.argument = callbackArg;
2890             }
2891
2892             return obj;
2893         },
2894
2895         initHeader:function(label, value, isDefault)
2896         {
2897             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2898
2899             if (headerObj[label] === undefined) {
2900                 headerObj[label] = value;
2901             }
2902             else {
2903
2904
2905                 headerObj[label] = value + "," + headerObj[label];
2906             }
2907
2908             if (isDefault) {
2909                 this.hasDefaultHeaders = true;
2910             }
2911             else {
2912                 this.hasHeaders = true;
2913             }
2914         },
2915
2916
2917         setHeader:function(o)
2918         {
2919             if (this.hasDefaultHeaders) {
2920                 for (var prop in this.defaultHeaders) {
2921                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2922                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2923                     }
2924                 }
2925             }
2926
2927             if (this.hasHeaders) {
2928                 for (var prop in this.headers) {
2929                     if (this.headers.hasOwnProperty(prop)) {
2930                         o.conn.setRequestHeader(prop, this.headers[prop]);
2931                     }
2932                 }
2933                 this.headers = {};
2934                 this.hasHeaders = false;
2935             }
2936         },
2937
2938         resetDefaultHeaders:function() {
2939             delete this.defaultHeaders;
2940             this.defaultHeaders = {};
2941             this.hasDefaultHeaders = false;
2942         },
2943
2944         abort:function(o, callback, isTimeout)
2945         {
2946             if(this.isCallInProgress(o)) {
2947                 o.conn.abort();
2948                 window.clearInterval(this.poll[o.tId]);
2949                 delete this.poll[o.tId];
2950                 if (isTimeout) {
2951                     delete this.timeout[o.tId];
2952                 }
2953
2954                 this.handleTransactionResponse(o, callback, true);
2955
2956                 return true;
2957             }
2958             else {
2959                 return false;
2960             }
2961         },
2962
2963
2964         isCallInProgress:function(o)
2965         {
2966             if (o && o.conn) {
2967                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2968             }
2969             else {
2970
2971                 return false;
2972             }
2973         },
2974
2975
2976         releaseObject:function(o)
2977         {
2978
2979             o.conn = null;
2980
2981             o = null;
2982         },
2983
2984         activeX:[
2985         'MSXML2.XMLHTTP.3.0',
2986         'MSXML2.XMLHTTP',
2987         'Microsoft.XMLHTTP'
2988         ]
2989
2990
2991     };
2992 })();/*
2993  * Portions of this file are based on pieces of Yahoo User Interface Library
2994  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2995  * YUI licensed under the BSD License:
2996  * http://developer.yahoo.net/yui/license.txt
2997  * <script type="text/javascript">
2998  *
2999  */
3000
3001 Roo.lib.Region = function(t, r, b, l) {
3002     this.top = t;
3003     this[1] = t;
3004     this.right = r;
3005     this.bottom = b;
3006     this.left = l;
3007     this[0] = l;
3008 };
3009
3010
3011 Roo.lib.Region.prototype = {
3012     contains : function(region) {
3013         return ( region.left >= this.left &&
3014                  region.right <= this.right &&
3015                  region.top >= this.top &&
3016                  region.bottom <= this.bottom    );
3017
3018     },
3019
3020     getArea : function() {
3021         return ( (this.bottom - this.top) * (this.right - this.left) );
3022     },
3023
3024     intersect : function(region) {
3025         var t = Math.max(this.top, region.top);
3026         var r = Math.min(this.right, region.right);
3027         var b = Math.min(this.bottom, region.bottom);
3028         var l = Math.max(this.left, region.left);
3029
3030         if (b >= t && r >= l) {
3031             return new Roo.lib.Region(t, r, b, l);
3032         } else {
3033             return null;
3034         }
3035     },
3036     union : function(region) {
3037         var t = Math.min(this.top, region.top);
3038         var r = Math.max(this.right, region.right);
3039         var b = Math.max(this.bottom, region.bottom);
3040         var l = Math.min(this.left, region.left);
3041
3042         return new Roo.lib.Region(t, r, b, l);
3043     },
3044
3045     adjust : function(t, l, b, r) {
3046         this.top += t;
3047         this.left += l;
3048         this.right += r;
3049         this.bottom += b;
3050         return this;
3051     }
3052 };
3053
3054 Roo.lib.Region.getRegion = function(el) {
3055     var p = Roo.lib.Dom.getXY(el);
3056
3057     var t = p[1];
3058     var r = p[0] + el.offsetWidth;
3059     var b = p[1] + el.offsetHeight;
3060     var l = p[0];
3061
3062     return new Roo.lib.Region(t, r, b, l);
3063 };
3064 /*
3065  * Portions of this file are based on pieces of Yahoo User Interface Library
3066  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3067  * YUI licensed under the BSD License:
3068  * http://developer.yahoo.net/yui/license.txt
3069  * <script type="text/javascript">
3070  *
3071  */
3072 //@@dep Roo.lib.Region
3073
3074
3075 Roo.lib.Point = function(x, y) {
3076     if (x instanceof Array) {
3077         y = x[1];
3078         x = x[0];
3079     }
3080     this.x = this.right = this.left = this[0] = x;
3081     this.y = this.top = this.bottom = this[1] = y;
3082 };
3083
3084 Roo.lib.Point.prototype = new Roo.lib.Region();
3085 /*
3086  * Portions of this file are based on pieces of Yahoo User Interface Library
3087  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3088  * YUI licensed under the BSD License:
3089  * http://developer.yahoo.net/yui/license.txt
3090  * <script type="text/javascript">
3091  *
3092  */
3093  
3094 (function() {   
3095
3096     Roo.lib.Anim = {
3097         scroll : function(el, args, duration, easing, cb, scope) {
3098             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3099         },
3100
3101         motion : function(el, args, duration, easing, cb, scope) {
3102             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3103         },
3104
3105         color : function(el, args, duration, easing, cb, scope) {
3106             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3107         },
3108
3109         run : function(el, args, duration, easing, cb, scope, type) {
3110             type = type || Roo.lib.AnimBase;
3111             if (typeof easing == "string") {
3112                 easing = Roo.lib.Easing[easing];
3113             }
3114             var anim = new type(el, args, duration, easing);
3115             anim.animateX(function() {
3116                 Roo.callback(cb, scope);
3117             });
3118             return anim;
3119         }
3120     };
3121 })();/*
3122  * Portions of this file are based on pieces of Yahoo User Interface Library
3123  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3124  * YUI licensed under the BSD License:
3125  * http://developer.yahoo.net/yui/license.txt
3126  * <script type="text/javascript">
3127  *
3128  */
3129
3130 (function() {    
3131     var libFlyweight;
3132     
3133     function fly(el) {
3134         if (!libFlyweight) {
3135             libFlyweight = new Roo.Element.Flyweight();
3136         }
3137         libFlyweight.dom = el;
3138         return libFlyweight;
3139     }
3140
3141     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3142     
3143    
3144     
3145     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3146         if (el) {
3147             this.init(el, attributes, duration, method);
3148         }
3149     };
3150
3151     Roo.lib.AnimBase.fly = fly;
3152     
3153     
3154     
3155     Roo.lib.AnimBase.prototype = {
3156
3157         toString: function() {
3158             var el = this.getEl();
3159             var id = el.id || el.tagName;
3160             return ("Anim " + id);
3161         },
3162
3163         patterns: {
3164             noNegatives:        /width|height|opacity|padding/i,
3165             offsetAttribute:  /^((width|height)|(top|left))$/,
3166             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3167             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3168         },
3169
3170
3171         doMethod: function(attr, start, end) {
3172             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3173         },
3174
3175
3176         setAttribute: function(attr, val, unit) {
3177             if (this.patterns.noNegatives.test(attr)) {
3178                 val = (val > 0) ? val : 0;
3179             }
3180
3181             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3182         },
3183
3184
3185         getAttribute: function(attr) {
3186             var el = this.getEl();
3187             var val = fly(el).getStyle(attr);
3188
3189             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3190                 return parseFloat(val);
3191             }
3192
3193             var a = this.patterns.offsetAttribute.exec(attr) || [];
3194             var pos = !!( a[3] );
3195             var box = !!( a[2] );
3196
3197
3198             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3199                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3200             } else {
3201                 val = 0;
3202             }
3203
3204             return val;
3205         },
3206
3207
3208         getDefaultUnit: function(attr) {
3209             if (this.patterns.defaultUnit.test(attr)) {
3210                 return 'px';
3211             }
3212
3213             return '';
3214         },
3215
3216         animateX : function(callback, scope) {
3217             var f = function() {
3218                 this.onComplete.removeListener(f);
3219                 if (typeof callback == "function") {
3220                     callback.call(scope || this, this);
3221                 }
3222             };
3223             this.onComplete.addListener(f, this);
3224             this.animate();
3225         },
3226
3227
3228         setRuntimeAttribute: function(attr) {
3229             var start;
3230             var end;
3231             var attributes = this.attributes;
3232
3233             this.runtimeAttributes[attr] = {};
3234
3235             var isset = function(prop) {
3236                 return (typeof prop !== 'undefined');
3237             };
3238
3239             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3240                 return false;
3241             }
3242
3243             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3244
3245
3246             if (isset(attributes[attr]['to'])) {
3247                 end = attributes[attr]['to'];
3248             } else if (isset(attributes[attr]['by'])) {
3249                 if (start.constructor == Array) {
3250                     end = [];
3251                     for (var i = 0, len = start.length; i < len; ++i) {
3252                         end[i] = start[i] + attributes[attr]['by'][i];
3253                     }
3254                 } else {
3255                     end = start + attributes[attr]['by'];
3256                 }
3257             }
3258
3259             this.runtimeAttributes[attr].start = start;
3260             this.runtimeAttributes[attr].end = end;
3261
3262
3263             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3264         },
3265
3266
3267         init: function(el, attributes, duration, method) {
3268
3269             var isAnimated = false;
3270
3271
3272             var startTime = null;
3273
3274
3275             var actualFrames = 0;
3276
3277
3278             el = Roo.getDom(el);
3279
3280
3281             this.attributes = attributes || {};
3282
3283
3284             this.duration = duration || 1;
3285
3286
3287             this.method = method || Roo.lib.Easing.easeNone;
3288
3289
3290             this.useSeconds = true;
3291
3292
3293             this.currentFrame = 0;
3294
3295
3296             this.totalFrames = Roo.lib.AnimMgr.fps;
3297
3298
3299             this.getEl = function() {
3300                 return el;
3301             };
3302
3303
3304             this.isAnimated = function() {
3305                 return isAnimated;
3306             };
3307
3308
3309             this.getStartTime = function() {
3310                 return startTime;
3311             };
3312
3313             this.runtimeAttributes = {};
3314
3315
3316             this.animate = function() {
3317                 if (this.isAnimated()) {
3318                     return false;
3319                 }
3320
3321                 this.currentFrame = 0;
3322
3323                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3324
3325                 Roo.lib.AnimMgr.registerElement(this);
3326             };
3327
3328
3329             this.stop = function(finish) {
3330                 if (finish) {
3331                     this.currentFrame = this.totalFrames;
3332                     this._onTween.fire();
3333                 }
3334                 Roo.lib.AnimMgr.stop(this);
3335             };
3336
3337             var onStart = function() {
3338                 this.onStart.fire();
3339
3340                 this.runtimeAttributes = {};
3341                 for (var attr in this.attributes) {
3342                     this.setRuntimeAttribute(attr);
3343                 }
3344
3345                 isAnimated = true;
3346                 actualFrames = 0;
3347                 startTime = new Date();
3348             };
3349
3350
3351             var onTween = function() {
3352                 var data = {
3353                     duration: new Date() - this.getStartTime(),
3354                     currentFrame: this.currentFrame
3355                 };
3356
3357                 data.toString = function() {
3358                     return (
3359                             'duration: ' + data.duration +
3360                             ', currentFrame: ' + data.currentFrame
3361                             );
3362                 };
3363
3364                 this.onTween.fire(data);
3365
3366                 var runtimeAttributes = this.runtimeAttributes;
3367
3368                 for (var attr in runtimeAttributes) {
3369                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3370                 }
3371
3372                 actualFrames += 1;
3373             };
3374
3375             var onComplete = function() {
3376                 var actual_duration = (new Date() - startTime) / 1000 ;
3377
3378                 var data = {
3379                     duration: actual_duration,
3380                     frames: actualFrames,
3381                     fps: actualFrames / actual_duration
3382                 };
3383
3384                 data.toString = function() {
3385                     return (
3386                             'duration: ' + data.duration +
3387                             ', frames: ' + data.frames +
3388                             ', fps: ' + data.fps
3389                             );
3390                 };
3391
3392                 isAnimated = false;
3393                 actualFrames = 0;
3394                 this.onComplete.fire(data);
3395             };
3396
3397
3398             this._onStart = new Roo.util.Event(this);
3399             this.onStart = new Roo.util.Event(this);
3400             this.onTween = new Roo.util.Event(this);
3401             this._onTween = new Roo.util.Event(this);
3402             this.onComplete = new Roo.util.Event(this);
3403             this._onComplete = new Roo.util.Event(this);
3404             this._onStart.addListener(onStart);
3405             this._onTween.addListener(onTween);
3406             this._onComplete.addListener(onComplete);
3407         }
3408     };
3409 })();
3410 /*
3411  * Portions of this file are based on pieces of Yahoo User Interface Library
3412  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3413  * YUI licensed under the BSD License:
3414  * http://developer.yahoo.net/yui/license.txt
3415  * <script type="text/javascript">
3416  *
3417  */
3418
3419 Roo.lib.AnimMgr = new function() {
3420
3421     var thread = null;
3422
3423
3424     var queue = [];
3425
3426
3427     var tweenCount = 0;
3428
3429
3430     this.fps = 1000;
3431
3432
3433     this.delay = 1;
3434
3435
3436     this.registerElement = function(tween) {
3437         queue[queue.length] = tween;
3438         tweenCount += 1;
3439         tween._onStart.fire();
3440         this.start();
3441     };
3442
3443
3444     this.unRegister = function(tween, index) {
3445         tween._onComplete.fire();
3446         index = index || getIndex(tween);
3447         if (index != -1) {
3448             queue.splice(index, 1);
3449         }
3450
3451         tweenCount -= 1;
3452         if (tweenCount <= 0) {
3453             this.stop();
3454         }
3455     };
3456
3457
3458     this.start = function() {
3459         if (thread === null) {
3460             thread = setInterval(this.run, this.delay);
3461         }
3462     };
3463
3464
3465     this.stop = function(tween) {
3466         if (!tween) {
3467             clearInterval(thread);
3468
3469             for (var i = 0, len = queue.length; i < len; ++i) {
3470                 if (queue[0].isAnimated()) {
3471                     this.unRegister(queue[0], 0);
3472                 }
3473             }
3474
3475             queue = [];
3476             thread = null;
3477             tweenCount = 0;
3478         }
3479         else {
3480             this.unRegister(tween);
3481         }
3482     };
3483
3484
3485     this.run = function() {
3486         for (var i = 0, len = queue.length; i < len; ++i) {
3487             var tween = queue[i];
3488             if (!tween || !tween.isAnimated()) {
3489                 continue;
3490             }
3491
3492             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3493             {
3494                 tween.currentFrame += 1;
3495
3496                 if (tween.useSeconds) {
3497                     correctFrame(tween);
3498                 }
3499                 tween._onTween.fire();
3500             }
3501             else {
3502                 Roo.lib.AnimMgr.stop(tween, i);
3503             }
3504         }
3505     };
3506
3507     var getIndex = function(anim) {
3508         for (var i = 0, len = queue.length; i < len; ++i) {
3509             if (queue[i] == anim) {
3510                 return i;
3511             }
3512         }
3513         return -1;
3514     };
3515
3516
3517     var correctFrame = function(tween) {
3518         var frames = tween.totalFrames;
3519         var frame = tween.currentFrame;
3520         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3521         var elapsed = (new Date() - tween.getStartTime());
3522         var tweak = 0;
3523
3524         if (elapsed < tween.duration * 1000) {
3525             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3526         } else {
3527             tweak = frames - (frame + 1);
3528         }
3529         if (tweak > 0 && isFinite(tweak)) {
3530             if (tween.currentFrame + tweak >= frames) {
3531                 tweak = frames - (frame + 1);
3532             }
3533
3534             tween.currentFrame += tweak;
3535         }
3536     };
3537 };
3538
3539     /*
3540  * Portions of this file are based on pieces of Yahoo User Interface Library
3541  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3542  * YUI licensed under the BSD License:
3543  * http://developer.yahoo.net/yui/license.txt
3544  * <script type="text/javascript">
3545  *
3546  */
3547 Roo.lib.Bezier = new function() {
3548
3549         this.getPosition = function(points, t) {
3550             var n = points.length;
3551             var tmp = [];
3552
3553             for (var i = 0; i < n; ++i) {
3554                 tmp[i] = [points[i][0], points[i][1]];
3555             }
3556
3557             for (var j = 1; j < n; ++j) {
3558                 for (i = 0; i < n - j; ++i) {
3559                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3560                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3561                 }
3562             }
3563
3564             return [ tmp[0][0], tmp[0][1] ];
3565
3566         };
3567     };/*
3568  * Portions of this file are based on pieces of Yahoo User Interface Library
3569  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3570  * YUI licensed under the BSD License:
3571  * http://developer.yahoo.net/yui/license.txt
3572  * <script type="text/javascript">
3573  *
3574  */
3575 (function() {
3576
3577     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3578         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3579     };
3580
3581     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3582
3583     var fly = Roo.lib.AnimBase.fly;
3584     var Y = Roo.lib;
3585     var superclass = Y.ColorAnim.superclass;
3586     var proto = Y.ColorAnim.prototype;
3587
3588     proto.toString = function() {
3589         var el = this.getEl();
3590         var id = el.id || el.tagName;
3591         return ("ColorAnim " + id);
3592     };
3593
3594     proto.patterns.color = /color$/i;
3595     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3596     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3597     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3598     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3599
3600
3601     proto.parseColor = function(s) {
3602         if (s.length == 3) {
3603             return s;
3604         }
3605
3606         var c = this.patterns.hex.exec(s);
3607         if (c && c.length == 4) {
3608             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3609         }
3610
3611         c = this.patterns.rgb.exec(s);
3612         if (c && c.length == 4) {
3613             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3614         }
3615
3616         c = this.patterns.hex3.exec(s);
3617         if (c && c.length == 4) {
3618             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3619         }
3620
3621         return null;
3622     };
3623     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3624     proto.getAttribute = function(attr) {
3625         var el = this.getEl();
3626         if (this.patterns.color.test(attr)) {
3627             var val = fly(el).getStyle(attr);
3628
3629             if (this.patterns.transparent.test(val)) {
3630                 var parent = el.parentNode;
3631                 val = fly(parent).getStyle(attr);
3632
3633                 while (parent && this.patterns.transparent.test(val)) {
3634                     parent = parent.parentNode;
3635                     val = fly(parent).getStyle(attr);
3636                     if (parent.tagName.toUpperCase() == 'HTML') {
3637                         val = '#fff';
3638                     }
3639                 }
3640             }
3641         } else {
3642             val = superclass.getAttribute.call(this, attr);
3643         }
3644
3645         return val;
3646     };
3647     proto.getAttribute = function(attr) {
3648         var el = this.getEl();
3649         if (this.patterns.color.test(attr)) {
3650             var val = fly(el).getStyle(attr);
3651
3652             if (this.patterns.transparent.test(val)) {
3653                 var parent = el.parentNode;
3654                 val = fly(parent).getStyle(attr);
3655
3656                 while (parent && this.patterns.transparent.test(val)) {
3657                     parent = parent.parentNode;
3658                     val = fly(parent).getStyle(attr);
3659                     if (parent.tagName.toUpperCase() == 'HTML') {
3660                         val = '#fff';
3661                     }
3662                 }
3663             }
3664         } else {
3665             val = superclass.getAttribute.call(this, attr);
3666         }
3667
3668         return val;
3669     };
3670
3671     proto.doMethod = function(attr, start, end) {
3672         var val;
3673
3674         if (this.patterns.color.test(attr)) {
3675             val = [];
3676             for (var i = 0, len = start.length; i < len; ++i) {
3677                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3678             }
3679
3680             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3681         }
3682         else {
3683             val = superclass.doMethod.call(this, attr, start, end);
3684         }
3685
3686         return val;
3687     };
3688
3689     proto.setRuntimeAttribute = function(attr) {
3690         superclass.setRuntimeAttribute.call(this, attr);
3691
3692         if (this.patterns.color.test(attr)) {
3693             var attributes = this.attributes;
3694             var start = this.parseColor(this.runtimeAttributes[attr].start);
3695             var end = this.parseColor(this.runtimeAttributes[attr].end);
3696
3697             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3698                 end = this.parseColor(attributes[attr].by);
3699
3700                 for (var i = 0, len = start.length; i < len; ++i) {
3701                     end[i] = start[i] + end[i];
3702                 }
3703             }
3704
3705             this.runtimeAttributes[attr].start = start;
3706             this.runtimeAttributes[attr].end = end;
3707         }
3708     };
3709 })();
3710
3711 /*
3712  * Portions of this file are based on pieces of Yahoo User Interface Library
3713  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3714  * YUI licensed under the BSD License:
3715  * http://developer.yahoo.net/yui/license.txt
3716  * <script type="text/javascript">
3717  *
3718  */
3719 Roo.lib.Easing = {
3720
3721
3722     easeNone: function (t, b, c, d) {
3723         return c * t / d + b;
3724     },
3725
3726
3727     easeIn: function (t, b, c, d) {
3728         return c * (t /= d) * t + b;
3729     },
3730
3731
3732     easeOut: function (t, b, c, d) {
3733         return -c * (t /= d) * (t - 2) + b;
3734     },
3735
3736
3737     easeBoth: function (t, b, c, d) {
3738         if ((t /= d / 2) < 1) {
3739             return c / 2 * t * t + b;
3740         }
3741
3742         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3743     },
3744
3745
3746     easeInStrong: function (t, b, c, d) {
3747         return c * (t /= d) * t * t * t + b;
3748     },
3749
3750
3751     easeOutStrong: function (t, b, c, d) {
3752         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3753     },
3754
3755
3756     easeBothStrong: function (t, b, c, d) {
3757         if ((t /= d / 2) < 1) {
3758             return c / 2 * t * t * t * t + b;
3759         }
3760
3761         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3762     },
3763
3764
3765
3766     elasticIn: function (t, b, c, d, a, p) {
3767         if (t == 0) {
3768             return b;
3769         }
3770         if ((t /= d) == 1) {
3771             return b + c;
3772         }
3773         if (!p) {
3774             p = d * .3;
3775         }
3776
3777         if (!a || a < Math.abs(c)) {
3778             a = c;
3779             var s = p / 4;
3780         }
3781         else {
3782             var s = p / (2 * Math.PI) * Math.asin(c / a);
3783         }
3784
3785         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3786     },
3787
3788
3789     elasticOut: function (t, b, c, d, a, p) {
3790         if (t == 0) {
3791             return b;
3792         }
3793         if ((t /= d) == 1) {
3794             return b + c;
3795         }
3796         if (!p) {
3797             p = d * .3;
3798         }
3799
3800         if (!a || a < Math.abs(c)) {
3801             a = c;
3802             var s = p / 4;
3803         }
3804         else {
3805             var s = p / (2 * Math.PI) * Math.asin(c / a);
3806         }
3807
3808         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3809     },
3810
3811
3812     elasticBoth: function (t, b, c, d, a, p) {
3813         if (t == 0) {
3814             return b;
3815         }
3816
3817         if ((t /= d / 2) == 2) {
3818             return b + c;
3819         }
3820
3821         if (!p) {
3822             p = d * (.3 * 1.5);
3823         }
3824
3825         if (!a || a < Math.abs(c)) {
3826             a = c;
3827             var s = p / 4;
3828         }
3829         else {
3830             var s = p / (2 * Math.PI) * Math.asin(c / a);
3831         }
3832
3833         if (t < 1) {
3834             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3835                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3836         }
3837         return a * Math.pow(2, -10 * (t -= 1)) *
3838                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3839     },
3840
3841
3842
3843     backIn: function (t, b, c, d, s) {
3844         if (typeof s == 'undefined') {
3845             s = 1.70158;
3846         }
3847         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3848     },
3849
3850
3851     backOut: function (t, b, c, d, s) {
3852         if (typeof s == 'undefined') {
3853             s = 1.70158;
3854         }
3855         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3856     },
3857
3858
3859     backBoth: function (t, b, c, d, s) {
3860         if (typeof s == 'undefined') {
3861             s = 1.70158;
3862         }
3863
3864         if ((t /= d / 2 ) < 1) {
3865             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3866         }
3867         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3868     },
3869
3870
3871     bounceIn: function (t, b, c, d) {
3872         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3873     },
3874
3875
3876     bounceOut: function (t, b, c, d) {
3877         if ((t /= d) < (1 / 2.75)) {
3878             return c * (7.5625 * t * t) + b;
3879         } else if (t < (2 / 2.75)) {
3880             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3881         } else if (t < (2.5 / 2.75)) {
3882             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3883         }
3884         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3885     },
3886
3887
3888     bounceBoth: function (t, b, c, d) {
3889         if (t < d / 2) {
3890             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3891         }
3892         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3893     }
3894 };/*
3895  * Portions of this file are based on pieces of Yahoo User Interface Library
3896  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3897  * YUI licensed under the BSD License:
3898  * http://developer.yahoo.net/yui/license.txt
3899  * <script type="text/javascript">
3900  *
3901  */
3902     (function() {
3903         Roo.lib.Motion = function(el, attributes, duration, method) {
3904             if (el) {
3905                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3906             }
3907         };
3908
3909         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3910
3911
3912         var Y = Roo.lib;
3913         var superclass = Y.Motion.superclass;
3914         var proto = Y.Motion.prototype;
3915
3916         proto.toString = function() {
3917             var el = this.getEl();
3918             var id = el.id || el.tagName;
3919             return ("Motion " + id);
3920         };
3921
3922         proto.patterns.points = /^points$/i;
3923
3924         proto.setAttribute = function(attr, val, unit) {
3925             if (this.patterns.points.test(attr)) {
3926                 unit = unit || 'px';
3927                 superclass.setAttribute.call(this, 'left', val[0], unit);
3928                 superclass.setAttribute.call(this, 'top', val[1], unit);
3929             } else {
3930                 superclass.setAttribute.call(this, attr, val, unit);
3931             }
3932         };
3933
3934         proto.getAttribute = function(attr) {
3935             if (this.patterns.points.test(attr)) {
3936                 var val = [
3937                         superclass.getAttribute.call(this, 'left'),
3938                         superclass.getAttribute.call(this, 'top')
3939                         ];
3940             } else {
3941                 val = superclass.getAttribute.call(this, attr);
3942             }
3943
3944             return val;
3945         };
3946
3947         proto.doMethod = function(attr, start, end) {
3948             var val = null;
3949
3950             if (this.patterns.points.test(attr)) {
3951                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3952                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3953             } else {
3954                 val = superclass.doMethod.call(this, attr, start, end);
3955             }
3956             return val;
3957         };
3958
3959         proto.setRuntimeAttribute = function(attr) {
3960             if (this.patterns.points.test(attr)) {
3961                 var el = this.getEl();
3962                 var attributes = this.attributes;
3963                 var start;
3964                 var control = attributes['points']['control'] || [];
3965                 var end;
3966                 var i, len;
3967
3968                 if (control.length > 0 && !(control[0] instanceof Array)) {
3969                     control = [control];
3970                 } else {
3971                     var tmp = [];
3972                     for (i = 0,len = control.length; i < len; ++i) {
3973                         tmp[i] = control[i];
3974                     }
3975                     control = tmp;
3976                 }
3977
3978                 Roo.fly(el).position();
3979
3980                 if (isset(attributes['points']['from'])) {
3981                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3982                 }
3983                 else {
3984                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3985                 }
3986
3987                 start = this.getAttribute('points');
3988
3989
3990                 if (isset(attributes['points']['to'])) {
3991                     end = translateValues.call(this, attributes['points']['to'], start);
3992
3993                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3994                     for (i = 0,len = control.length; i < len; ++i) {
3995                         control[i] = translateValues.call(this, control[i], start);
3996                     }
3997
3998
3999                 } else if (isset(attributes['points']['by'])) {
4000                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4001
4002                     for (i = 0,len = control.length; i < len; ++i) {
4003                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4004                     }
4005                 }
4006
4007                 this.runtimeAttributes[attr] = [start];
4008
4009                 if (control.length > 0) {
4010                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4011                 }
4012
4013                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4014             }
4015             else {
4016                 superclass.setRuntimeAttribute.call(this, attr);
4017             }
4018         };
4019
4020         var translateValues = function(val, start) {
4021             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4022             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4023
4024             return val;
4025         };
4026
4027         var isset = function(prop) {
4028             return (typeof prop !== 'undefined');
4029         };
4030     })();
4031 /*
4032  * Portions of this file are based on pieces of Yahoo User Interface Library
4033  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4034  * YUI licensed under the BSD License:
4035  * http://developer.yahoo.net/yui/license.txt
4036  * <script type="text/javascript">
4037  *
4038  */
4039     (function() {
4040         Roo.lib.Scroll = function(el, attributes, duration, method) {
4041             if (el) {
4042                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4043             }
4044         };
4045
4046         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4047
4048
4049         var Y = Roo.lib;
4050         var superclass = Y.Scroll.superclass;
4051         var proto = Y.Scroll.prototype;
4052
4053         proto.toString = function() {
4054             var el = this.getEl();
4055             var id = el.id || el.tagName;
4056             return ("Scroll " + id);
4057         };
4058
4059         proto.doMethod = function(attr, start, end) {
4060             var val = null;
4061
4062             if (attr == 'scroll') {
4063                 val = [
4064                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4065                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4066                         ];
4067
4068             } else {
4069                 val = superclass.doMethod.call(this, attr, start, end);
4070             }
4071             return val;
4072         };
4073
4074         proto.getAttribute = function(attr) {
4075             var val = null;
4076             var el = this.getEl();
4077
4078             if (attr == 'scroll') {
4079                 val = [ el.scrollLeft, el.scrollTop ];
4080             } else {
4081                 val = superclass.getAttribute.call(this, attr);
4082             }
4083
4084             return val;
4085         };
4086
4087         proto.setAttribute = function(attr, val, unit) {
4088             var el = this.getEl();
4089
4090             if (attr == 'scroll') {
4091                 el.scrollLeft = val[0];
4092                 el.scrollTop = val[1];
4093             } else {
4094                 superclass.setAttribute.call(this, attr, val, unit);
4095             }
4096         };
4097     })();
4098 /*
4099  * Based on:
4100  * Ext JS Library 1.1.1
4101  * Copyright(c) 2006-2007, Ext JS, LLC.
4102  *
4103  * Originally Released Under LGPL - original licence link has changed is not relivant.
4104  *
4105  * Fork - LGPL
4106  * <script type="text/javascript">
4107  */
4108
4109
4110 // nasty IE9 hack - what a pile of crap that is..
4111
4112  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4113     Range.prototype.createContextualFragment = function (html) {
4114         var doc = window.document;
4115         var container = doc.createElement("div");
4116         container.innerHTML = html;
4117         var frag = doc.createDocumentFragment(), n;
4118         while ((n = container.firstChild)) {
4119             frag.appendChild(n);
4120         }
4121         return frag;
4122     };
4123 }
4124
4125 /**
4126  * @class Roo.DomHelper
4127  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4128  * 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>.
4129  * @singleton
4130  */
4131 Roo.DomHelper = function(){
4132     var tempTableEl = null;
4133     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4134     var tableRe = /^table|tbody|tr|td$/i;
4135     var xmlns = {};
4136     // build as innerHTML where available
4137     /** @ignore */
4138     var createHtml = function(o){
4139         if(typeof o == 'string'){
4140             return o;
4141         }
4142         var b = "";
4143         if(!o.tag){
4144             o.tag = "div";
4145         }
4146         b += "<" + o.tag;
4147         for(var attr in o){
4148             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4149             if(attr == "style"){
4150                 var s = o["style"];
4151                 if(typeof s == "function"){
4152                     s = s.call();
4153                 }
4154                 if(typeof s == "string"){
4155                     b += ' style="' + s + '"';
4156                 }else if(typeof s == "object"){
4157                     b += ' style="';
4158                     for(var key in s){
4159                         if(typeof s[key] != "function"){
4160                             b += key + ":" + s[key] + ";";
4161                         }
4162                     }
4163                     b += '"';
4164                 }
4165             }else{
4166                 if(attr == "cls"){
4167                     b += ' class="' + o["cls"] + '"';
4168                 }else if(attr == "htmlFor"){
4169                     b += ' for="' + o["htmlFor"] + '"';
4170                 }else{
4171                     b += " " + attr + '="' + o[attr] + '"';
4172                 }
4173             }
4174         }
4175         if(emptyTags.test(o.tag)){
4176             b += "/>";
4177         }else{
4178             b += ">";
4179             var cn = o.children || o.cn;
4180             if(cn){
4181                 //http://bugs.kde.org/show_bug.cgi?id=71506
4182                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4183                     for(var i = 0, len = cn.length; i < len; i++) {
4184                         b += createHtml(cn[i], b);
4185                     }
4186                 }else{
4187                     b += createHtml(cn, b);
4188                 }
4189             }
4190             if(o.html){
4191                 b += o.html;
4192             }
4193             b += "</" + o.tag + ">";
4194         }
4195         return b;
4196     };
4197
4198     // build as dom
4199     /** @ignore */
4200     var createDom = function(o, parentNode){
4201          
4202         // defininition craeted..
4203         var ns = false;
4204         if (o.ns && o.ns != 'html') {
4205                
4206             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4207                 xmlns[o.ns] = o.xmlns;
4208                 ns = o.xmlns;
4209             }
4210             if (typeof(xmlns[o.ns]) == 'undefined') {
4211                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4212             }
4213             ns = xmlns[o.ns];
4214         }
4215         
4216         
4217         if (typeof(o) == 'string') {
4218             return parentNode.appendChild(document.createTextNode(o));
4219         }
4220         o.tag = o.tag || div;
4221         if (o.ns && Roo.isIE) {
4222             ns = false;
4223             o.tag = o.ns + ':' + o.tag;
4224             
4225         }
4226         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4227         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4228         for(var attr in o){
4229             
4230             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4231                     attr == "style" || typeof o[attr] == "function") continue;
4232                     
4233             if(attr=="cls" && Roo.isIE){
4234                 el.className = o["cls"];
4235             }else{
4236                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4237                 else el[attr] = o[attr];
4238             }
4239         }
4240         Roo.DomHelper.applyStyles(el, o.style);
4241         var cn = o.children || o.cn;
4242         if(cn){
4243             //http://bugs.kde.org/show_bug.cgi?id=71506
4244              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4245                 for(var i = 0, len = cn.length; i < len; i++) {
4246                     createDom(cn[i], el);
4247                 }
4248             }else{
4249                 createDom(cn, el);
4250             }
4251         }
4252         if(o.html){
4253             el.innerHTML = o.html;
4254         }
4255         if(parentNode){
4256            parentNode.appendChild(el);
4257         }
4258         return el;
4259     };
4260
4261     var ieTable = function(depth, s, h, e){
4262         tempTableEl.innerHTML = [s, h, e].join('');
4263         var i = -1, el = tempTableEl;
4264         while(++i < depth){
4265             el = el.firstChild;
4266         }
4267         return el;
4268     };
4269
4270     // kill repeat to save bytes
4271     var ts = '<table>',
4272         te = '</table>',
4273         tbs = ts+'<tbody>',
4274         tbe = '</tbody>'+te,
4275         trs = tbs + '<tr>',
4276         tre = '</tr>'+tbe;
4277
4278     /**
4279      * @ignore
4280      * Nasty code for IE's broken table implementation
4281      */
4282     var insertIntoTable = function(tag, where, el, html){
4283         if(!tempTableEl){
4284             tempTableEl = document.createElement('div');
4285         }
4286         var node;
4287         var before = null;
4288         if(tag == 'td'){
4289             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4290                 return;
4291             }
4292             if(where == 'beforebegin'){
4293                 before = el;
4294                 el = el.parentNode;
4295             } else{
4296                 before = el.nextSibling;
4297                 el = el.parentNode;
4298             }
4299             node = ieTable(4, trs, html, tre);
4300         }
4301         else if(tag == 'tr'){
4302             if(where == 'beforebegin'){
4303                 before = el;
4304                 el = el.parentNode;
4305                 node = ieTable(3, tbs, html, tbe);
4306             } else if(where == 'afterend'){
4307                 before = el.nextSibling;
4308                 el = el.parentNode;
4309                 node = ieTable(3, tbs, html, tbe);
4310             } else{ // INTO a TR
4311                 if(where == 'afterbegin'){
4312                     before = el.firstChild;
4313                 }
4314                 node = ieTable(4, trs, html, tre);
4315             }
4316         } else if(tag == 'tbody'){
4317             if(where == 'beforebegin'){
4318                 before = el;
4319                 el = el.parentNode;
4320                 node = ieTable(2, ts, html, te);
4321             } else if(where == 'afterend'){
4322                 before = el.nextSibling;
4323                 el = el.parentNode;
4324                 node = ieTable(2, ts, html, te);
4325             } else{
4326                 if(where == 'afterbegin'){
4327                     before = el.firstChild;
4328                 }
4329                 node = ieTable(3, tbs, html, tbe);
4330             }
4331         } else{ // TABLE
4332             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4333                 return;
4334             }
4335             if(where == 'afterbegin'){
4336                 before = el.firstChild;
4337             }
4338             node = ieTable(2, ts, html, te);
4339         }
4340         el.insertBefore(node, before);
4341         return node;
4342     };
4343
4344     return {
4345     /** True to force the use of DOM instead of html fragments @type Boolean */
4346     useDom : false,
4347
4348     /**
4349      * Returns the markup for the passed Element(s) config
4350      * @param {Object} o The Dom object spec (and children)
4351      * @return {String}
4352      */
4353     markup : function(o){
4354         return createHtml(o);
4355     },
4356
4357     /**
4358      * Applies a style specification to an element
4359      * @param {String/HTMLElement} el The element to apply styles to
4360      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4361      * a function which returns such a specification.
4362      */
4363     applyStyles : function(el, styles){
4364         if(styles){
4365            el = Roo.fly(el);
4366            if(typeof styles == "string"){
4367                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4368                var matches;
4369                while ((matches = re.exec(styles)) != null){
4370                    el.setStyle(matches[1], matches[2]);
4371                }
4372            }else if (typeof styles == "object"){
4373                for (var style in styles){
4374                   el.setStyle(style, styles[style]);
4375                }
4376            }else if (typeof styles == "function"){
4377                 Roo.DomHelper.applyStyles(el, styles.call());
4378            }
4379         }
4380     },
4381
4382     /**
4383      * Inserts an HTML fragment into the Dom
4384      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4385      * @param {HTMLElement} el The context element
4386      * @param {String} html The HTML fragmenet
4387      * @return {HTMLElement} The new node
4388      */
4389     insertHtml : function(where, el, html){
4390         where = where.toLowerCase();
4391         if(el.insertAdjacentHTML){
4392             if(tableRe.test(el.tagName)){
4393                 var rs;
4394                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4395                     return rs;
4396                 }
4397             }
4398             switch(where){
4399                 case "beforebegin":
4400                     el.insertAdjacentHTML('BeforeBegin', html);
4401                     return el.previousSibling;
4402                 case "afterbegin":
4403                     el.insertAdjacentHTML('AfterBegin', html);
4404                     return el.firstChild;
4405                 case "beforeend":
4406                     el.insertAdjacentHTML('BeforeEnd', html);
4407                     return el.lastChild;
4408                 case "afterend":
4409                     el.insertAdjacentHTML('AfterEnd', html);
4410                     return el.nextSibling;
4411             }
4412             throw 'Illegal insertion point -> "' + where + '"';
4413         }
4414         var range = el.ownerDocument.createRange();
4415         var frag;
4416         switch(where){
4417              case "beforebegin":
4418                 range.setStartBefore(el);
4419                 frag = range.createContextualFragment(html);
4420                 el.parentNode.insertBefore(frag, el);
4421                 return el.previousSibling;
4422              case "afterbegin":
4423                 if(el.firstChild){
4424                     range.setStartBefore(el.firstChild);
4425                     frag = range.createContextualFragment(html);
4426                     el.insertBefore(frag, el.firstChild);
4427                     return el.firstChild;
4428                 }else{
4429                     el.innerHTML = html;
4430                     return el.firstChild;
4431                 }
4432             case "beforeend":
4433                 if(el.lastChild){
4434                     range.setStartAfter(el.lastChild);
4435                     frag = range.createContextualFragment(html);
4436                     el.appendChild(frag);
4437                     return el.lastChild;
4438                 }else{
4439                     el.innerHTML = html;
4440                     return el.lastChild;
4441                 }
4442             case "afterend":
4443                 range.setStartAfter(el);
4444                 frag = range.createContextualFragment(html);
4445                 el.parentNode.insertBefore(frag, el.nextSibling);
4446                 return el.nextSibling;
4447             }
4448             throw 'Illegal insertion point -> "' + where + '"';
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and inserts them before el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     insertBefore : function(el, o, returnElement){
4459         return this.doInsert(el, o, returnElement, "beforeBegin");
4460     },
4461
4462     /**
4463      * Creates new Dom element(s) and inserts them after el
4464      * @param {String/HTMLElement/Element} el The context element
4465      * @param {Object} o The Dom object spec (and children)
4466      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4467      * @return {HTMLElement/Roo.Element} The new node
4468      */
4469     insertAfter : function(el, o, returnElement){
4470         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4471     },
4472
4473     /**
4474      * Creates new Dom element(s) and inserts them as the first child of el
4475      * @param {String/HTMLElement/Element} el The context element
4476      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4477      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4478      * @return {HTMLElement/Roo.Element} The new node
4479      */
4480     insertFirst : function(el, o, returnElement){
4481         return this.doInsert(el, o, returnElement, "afterBegin");
4482     },
4483
4484     // private
4485     doInsert : function(el, o, returnElement, pos, sibling){
4486         el = Roo.getDom(el);
4487         var newNode;
4488         if(this.useDom || o.ns){
4489             newNode = createDom(o, null);
4490             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4491         }else{
4492             var html = createHtml(o);
4493             newNode = this.insertHtml(pos, el, html);
4494         }
4495         return returnElement ? Roo.get(newNode, true) : newNode;
4496     },
4497
4498     /**
4499      * Creates new Dom element(s) and appends them to el
4500      * @param {String/HTMLElement/Element} el The context element
4501      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4502      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4503      * @return {HTMLElement/Roo.Element} The new node
4504      */
4505     append : function(el, o, returnElement){
4506         el = Roo.getDom(el);
4507         var newNode;
4508         if(this.useDom || o.ns){
4509             newNode = createDom(o, null);
4510             el.appendChild(newNode);
4511         }else{
4512             var html = createHtml(o);
4513             newNode = this.insertHtml("beforeEnd", el, html);
4514         }
4515         return returnElement ? Roo.get(newNode, true) : newNode;
4516     },
4517
4518     /**
4519      * Creates new Dom element(s) and overwrites the contents of el with them
4520      * @param {String/HTMLElement/Element} el The context element
4521      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4522      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4523      * @return {HTMLElement/Roo.Element} The new node
4524      */
4525     overwrite : function(el, o, returnElement){
4526         el = Roo.getDom(el);
4527         if (o.ns) {
4528           
4529             while (el.childNodes.length) {
4530                 el.removeChild(el.firstChild);
4531             }
4532             createDom(o, el);
4533         } else {
4534             el.innerHTML = createHtml(o);   
4535         }
4536         
4537         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4538     },
4539
4540     /**
4541      * Creates a new Roo.DomHelper.Template from the Dom object spec
4542      * @param {Object} o The Dom object spec (and children)
4543      * @return {Roo.DomHelper.Template} The new template
4544      */
4545     createTemplate : function(o){
4546         var html = createHtml(o);
4547         return new Roo.Template(html);
4548     }
4549     };
4550 }();
4551 /*
4552  * Based on:
4553  * Ext JS Library 1.1.1
4554  * Copyright(c) 2006-2007, Ext JS, LLC.
4555  *
4556  * Originally Released Under LGPL - original licence link has changed is not relivant.
4557  *
4558  * Fork - LGPL
4559  * <script type="text/javascript">
4560  */
4561  
4562 /**
4563 * @class Roo.Template
4564 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4565 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4566 * Usage:
4567 <pre><code>
4568 var t = new Roo.Template({
4569     html :  '&lt;div name="{id}"&gt;' + 
4570         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4571         '&lt;/div&gt;',
4572     myformat: function (value, allValues) {
4573         return 'XX' + value;
4574     }
4575 });
4576 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4577 </code></pre>
4578 * For more information see this blog post with examples:
4579 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4580      - Create Elements using DOM, HTML fragments and Templates</a>. 
4581 * @constructor
4582 * @param {Object} cfg - Configuration object.
4583 */
4584 Roo.Template = function(cfg){
4585     // BC!
4586     if(cfg instanceof Array){
4587         cfg = cfg.join("");
4588     }else if(arguments.length > 1){
4589         cfg = Array.prototype.join.call(arguments, "");
4590     }
4591     
4592     
4593     if (typeof(cfg) == 'object') {
4594         Roo.apply(this,cfg)
4595     } else {
4596         // bc
4597         this.html = cfg;
4598     }
4599     if (this.url) {
4600         this.load();
4601     }
4602     
4603 };
4604 Roo.Template.prototype = {
4605     
4606     /**
4607      * @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..
4608      *                    it should be fixed so that template is observable...
4609      */
4610     url : false,
4611     /**
4612      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4613      */
4614     html : '',
4615     /**
4616      * Returns an HTML fragment of this template with the specified values applied.
4617      * @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'})
4618      * @return {String} The HTML fragment
4619      */
4620     applyTemplate : function(values){
4621         try {
4622            
4623             if(this.compiled){
4624                 return this.compiled(values);
4625             }
4626             var useF = this.disableFormats !== true;
4627             var fm = Roo.util.Format, tpl = this;
4628             var fn = function(m, name, format, args){
4629                 if(format && useF){
4630                     if(format.substr(0, 5) == "this."){
4631                         return tpl.call(format.substr(5), values[name], values);
4632                     }else{
4633                         if(args){
4634                             // quoted values are required for strings in compiled templates, 
4635                             // but for non compiled we need to strip them
4636                             // quoted reversed for jsmin
4637                             var re = /^\s*['"](.*)["']\s*$/;
4638                             args = args.split(',');
4639                             for(var i = 0, len = args.length; i < len; i++){
4640                                 args[i] = args[i].replace(re, "$1");
4641                             }
4642                             args = [values[name]].concat(args);
4643                         }else{
4644                             args = [values[name]];
4645                         }
4646                         return fm[format].apply(fm, args);
4647                     }
4648                 }else{
4649                     return values[name] !== undefined ? values[name] : "";
4650                 }
4651             };
4652             return this.html.replace(this.re, fn);
4653         } catch (e) {
4654             Roo.log(e);
4655             throw e;
4656         }
4657          
4658     },
4659     
4660     loading : false,
4661       
4662     load : function ()
4663     {
4664          
4665         if (this.loading) {
4666             return;
4667         }
4668         var _t = this;
4669         
4670         this.loading = true;
4671         this.compiled = false;
4672         
4673         var cx = new Roo.data.Connection();
4674         cx.request({
4675             url : this.url,
4676             method : 'GET',
4677             success : function (response) {
4678                 _t.loading = false;
4679                 _t.html = response.responseText;
4680                 _t.url = false;
4681                 _t.compile();
4682              },
4683             failure : function(response) {
4684                 Roo.log("Template failed to load from " + _t.url);
4685                 _t.loading = false;
4686             }
4687         });
4688     },
4689
4690     /**
4691      * Sets the HTML used as the template and optionally compiles it.
4692      * @param {String} html
4693      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4694      * @return {Roo.Template} this
4695      */
4696     set : function(html, compile){
4697         this.html = html;
4698         this.compiled = null;
4699         if(compile){
4700             this.compile();
4701         }
4702         return this;
4703     },
4704     
4705     /**
4706      * True to disable format functions (defaults to false)
4707      * @type Boolean
4708      */
4709     disableFormats : false,
4710     
4711     /**
4712     * The regular expression used to match template variables 
4713     * @type RegExp
4714     * @property 
4715     */
4716     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4717     
4718     /**
4719      * Compiles the template into an internal function, eliminating the RegEx overhead.
4720      * @return {Roo.Template} this
4721      */
4722     compile : function(){
4723         var fm = Roo.util.Format;
4724         var useF = this.disableFormats !== true;
4725         var sep = Roo.isGecko ? "+" : ",";
4726         var fn = function(m, name, format, args){
4727             if(format && useF){
4728                 args = args ? ',' + args : "";
4729                 if(format.substr(0, 5) != "this."){
4730                     format = "fm." + format + '(';
4731                 }else{
4732                     format = 'this.call("'+ format.substr(5) + '", ';
4733                     args = ", values";
4734                 }
4735             }else{
4736                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4737             }
4738             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4739         };
4740         var body;
4741         // branched to use + in gecko and [].join() in others
4742         if(Roo.isGecko){
4743             body = "this.compiled = function(values){ return '" +
4744                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4745                     "';};";
4746         }else{
4747             body = ["this.compiled = function(values){ return ['"];
4748             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4749             body.push("'].join('');};");
4750             body = body.join('');
4751         }
4752         /**
4753          * eval:var:values
4754          * eval:var:fm
4755          */
4756         eval(body);
4757         return this;
4758     },
4759     
4760     // private function used to call members
4761     call : function(fnName, value, allValues){
4762         return this[fnName](value, allValues);
4763     },
4764     
4765     /**
4766      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4767      * @param {String/HTMLElement/Roo.Element} el The context element
4768      * @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'})
4769      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4770      * @return {HTMLElement/Roo.Element} The new node or Element
4771      */
4772     insertFirst: function(el, values, returnElement){
4773         return this.doInsert('afterBegin', el, values, returnElement);
4774     },
4775
4776     /**
4777      * Applies the supplied values to the template and inserts the new node(s) before el.
4778      * @param {String/HTMLElement/Roo.Element} el The context element
4779      * @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'})
4780      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4781      * @return {HTMLElement/Roo.Element} The new node or Element
4782      */
4783     insertBefore: function(el, values, returnElement){
4784         return this.doInsert('beforeBegin', el, values, returnElement);
4785     },
4786
4787     /**
4788      * Applies the supplied values to the template and inserts the new node(s) after el.
4789      * @param {String/HTMLElement/Roo.Element} el The context element
4790      * @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'})
4791      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4792      * @return {HTMLElement/Roo.Element} The new node or Element
4793      */
4794     insertAfter : function(el, values, returnElement){
4795         return this.doInsert('afterEnd', el, values, returnElement);
4796     },
4797     
4798     /**
4799      * Applies the supplied values to the template and appends the new node(s) to el.
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @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'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     append : function(el, values, returnElement){
4806         return this.doInsert('beforeEnd', el, values, returnElement);
4807     },
4808
4809     doInsert : function(where, el, values, returnEl){
4810         el = Roo.getDom(el);
4811         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4812         return returnEl ? Roo.get(newNode, true) : newNode;
4813     },
4814
4815     /**
4816      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @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'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     overwrite : function(el, values, returnElement){
4823         el = Roo.getDom(el);
4824         el.innerHTML = this.applyTemplate(values);
4825         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4826     }
4827 };
4828 /**
4829  * Alias for {@link #applyTemplate}
4830  * @method
4831  */
4832 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4833
4834 // backwards compat
4835 Roo.DomHelper.Template = Roo.Template;
4836
4837 /**
4838  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4839  * @param {String/HTMLElement} el A DOM element or its id
4840  * @returns {Roo.Template} The created template
4841  * @static
4842  */
4843 Roo.Template.from = function(el){
4844     el = Roo.getDom(el);
4845     return new Roo.Template(el.value || el.innerHTML);
4846 };/*
4847  * Based on:
4848  * Ext JS Library 1.1.1
4849  * Copyright(c) 2006-2007, Ext JS, LLC.
4850  *
4851  * Originally Released Under LGPL - original licence link has changed is not relivant.
4852  *
4853  * Fork - LGPL
4854  * <script type="text/javascript">
4855  */
4856  
4857
4858 /*
4859  * This is code is also distributed under MIT license for use
4860  * with jQuery and prototype JavaScript libraries.
4861  */
4862 /**
4863  * @class Roo.DomQuery
4864 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).
4865 <p>
4866 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>
4867
4868 <p>
4869 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.
4870 </p>
4871 <h4>Element Selectors:</h4>
4872 <ul class="list">
4873     <li> <b>*</b> any element</li>
4874     <li> <b>E</b> an element with the tag E</li>
4875     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4876     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4877     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4878     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4879 </ul>
4880 <h4>Attribute Selectors:</h4>
4881 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4882 <ul class="list">
4883     <li> <b>E[foo]</b> has an attribute "foo"</li>
4884     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4885     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4886     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4887     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4888     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4889     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4890 </ul>
4891 <h4>Pseudo Classes:</h4>
4892 <ul class="list">
4893     <li> <b>E:first-child</b> E is the first child of its parent</li>
4894     <li> <b>E:last-child</b> E is the last child of its parent</li>
4895     <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>
4896     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4897     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4898     <li> <b>E:only-child</b> E is the only child of its parent</li>
4899     <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>
4900     <li> <b>E:first</b> the first E in the resultset</li>
4901     <li> <b>E:last</b> the last E in the resultset</li>
4902     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4903     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4904     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4905     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4906     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4907     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4908     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4909     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4910     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4911 </ul>
4912 <h4>CSS Value Selectors:</h4>
4913 <ul class="list">
4914     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4915     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4916     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4917     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4918     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4919     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4920 </ul>
4921  * @singleton
4922  */
4923 Roo.DomQuery = function(){
4924     var cache = {}, simpleCache = {}, valueCache = {};
4925     var nonSpace = /\S/;
4926     var trimRe = /^\s+|\s+$/g;
4927     var tplRe = /\{(\d+)\}/g;
4928     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4929     var tagTokenRe = /^(#)?([\w-\*]+)/;
4930     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4931
4932     function child(p, index){
4933         var i = 0;
4934         var n = p.firstChild;
4935         while(n){
4936             if(n.nodeType == 1){
4937                if(++i == index){
4938                    return n;
4939                }
4940             }
4941             n = n.nextSibling;
4942         }
4943         return null;
4944     };
4945
4946     function next(n){
4947         while((n = n.nextSibling) && n.nodeType != 1);
4948         return n;
4949     };
4950
4951     function prev(n){
4952         while((n = n.previousSibling) && n.nodeType != 1);
4953         return n;
4954     };
4955
4956     function children(d){
4957         var n = d.firstChild, ni = -1;
4958             while(n){
4959                 var nx = n.nextSibling;
4960                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4961                     d.removeChild(n);
4962                 }else{
4963                     n.nodeIndex = ++ni;
4964                 }
4965                 n = nx;
4966             }
4967             return this;
4968         };
4969
4970     function byClassName(c, a, v){
4971         if(!v){
4972             return c;
4973         }
4974         var r = [], ri = -1, cn;
4975         for(var i = 0, ci; ci = c[i]; i++){
4976             if((' '+ci.className+' ').indexOf(v) != -1){
4977                 r[++ri] = ci;
4978             }
4979         }
4980         return r;
4981     };
4982
4983     function attrValue(n, attr){
4984         if(!n.tagName && typeof n.length != "undefined"){
4985             n = n[0];
4986         }
4987         if(!n){
4988             return null;
4989         }
4990         if(attr == "for"){
4991             return n.htmlFor;
4992         }
4993         if(attr == "class" || attr == "className"){
4994             return n.className;
4995         }
4996         return n.getAttribute(attr) || n[attr];
4997
4998     };
4999
5000     function getNodes(ns, mode, tagName){
5001         var result = [], ri = -1, cs;
5002         if(!ns){
5003             return result;
5004         }
5005         tagName = tagName || "*";
5006         if(typeof ns.getElementsByTagName != "undefined"){
5007             ns = [ns];
5008         }
5009         if(!mode){
5010             for(var i = 0, ni; ni = ns[i]; i++){
5011                 cs = ni.getElementsByTagName(tagName);
5012                 for(var j = 0, ci; ci = cs[j]; j++){
5013                     result[++ri] = ci;
5014                 }
5015             }
5016         }else if(mode == "/" || mode == ">"){
5017             var utag = tagName.toUpperCase();
5018             for(var i = 0, ni, cn; ni = ns[i]; i++){
5019                 cn = ni.children || ni.childNodes;
5020                 for(var j = 0, cj; cj = cn[j]; j++){
5021                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5022                         result[++ri] = cj;
5023                     }
5024                 }
5025             }
5026         }else if(mode == "+"){
5027             var utag = tagName.toUpperCase();
5028             for(var i = 0, n; n = ns[i]; i++){
5029                 while((n = n.nextSibling) && n.nodeType != 1);
5030                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5031                     result[++ri] = n;
5032                 }
5033             }
5034         }else if(mode == "~"){
5035             for(var i = 0, n; n = ns[i]; i++){
5036                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5037                 if(n){
5038                     result[++ri] = n;
5039                 }
5040             }
5041         }
5042         return result;
5043     };
5044
5045     function concat(a, b){
5046         if(b.slice){
5047             return a.concat(b);
5048         }
5049         for(var i = 0, l = b.length; i < l; i++){
5050             a[a.length] = b[i];
5051         }
5052         return a;
5053     }
5054
5055     function byTag(cs, tagName){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!tagName){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         tagName = tagName.toLowerCase();
5064         for(var i = 0, ci; ci = cs[i]; i++){
5065             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5066                 r[++ri] = ci;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byId(cs, attr, id){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!id){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         for(var i = 0,ci; ci = cs[i]; i++){
5081             if(ci && ci.id == id){
5082                 r[++ri] = ci;
5083                 return r;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byAttribute(cs, attr, value, op, custom){
5090         var r = [], ri = -1, st = custom=="{";
5091         var f = Roo.DomQuery.operators[op];
5092         for(var i = 0, ci; ci = cs[i]; i++){
5093             var a;
5094             if(st){
5095                 a = Roo.DomQuery.getStyle(ci, attr);
5096             }
5097             else if(attr == "class" || attr == "className"){
5098                 a = ci.className;
5099             }else if(attr == "for"){
5100                 a = ci.htmlFor;
5101             }else if(attr == "href"){
5102                 a = ci.getAttribute("href", 2);
5103             }else{
5104                 a = ci.getAttribute(attr);
5105             }
5106             if((f && f(a, value)) || (!f && a)){
5107                 r[++ri] = ci;
5108             }
5109         }
5110         return r;
5111     };
5112
5113     function byPseudo(cs, name, value){
5114         return Roo.DomQuery.pseudos[name](cs, value);
5115     };
5116
5117     // This is for IE MSXML which does not support expandos.
5118     // IE runs the same speed using setAttribute, however FF slows way down
5119     // and Safari completely fails so they need to continue to use expandos.
5120     var isIE = window.ActiveXObject ? true : false;
5121
5122     // this eval is stop the compressor from
5123     // renaming the variable to something shorter
5124     
5125     /** eval:var:batch */
5126     var batch = 30803; 
5127
5128     var key = 30803;
5129
5130     function nodupIEXml(cs){
5131         var d = ++key;
5132         cs[0].setAttribute("_nodup", d);
5133         var r = [cs[0]];
5134         for(var i = 1, len = cs.length; i < len; i++){
5135             var c = cs[i];
5136             if(!c.getAttribute("_nodup") != d){
5137                 c.setAttribute("_nodup", d);
5138                 r[r.length] = c;
5139             }
5140         }
5141         for(var i = 0, len = cs.length; i < len; i++){
5142             cs[i].removeAttribute("_nodup");
5143         }
5144         return r;
5145     }
5146
5147     function nodup(cs){
5148         if(!cs){
5149             return [];
5150         }
5151         var len = cs.length, c, i, r = cs, cj, ri = -1;
5152         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5153             return cs;
5154         }
5155         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5156             return nodupIEXml(cs);
5157         }
5158         var d = ++key;
5159         cs[0]._nodup = d;
5160         for(i = 1; c = cs[i]; i++){
5161             if(c._nodup != d){
5162                 c._nodup = d;
5163             }else{
5164                 r = [];
5165                 for(var j = 0; j < i; j++){
5166                     r[++ri] = cs[j];
5167                 }
5168                 for(j = i+1; cj = cs[j]; j++){
5169                     if(cj._nodup != d){
5170                         cj._nodup = d;
5171                         r[++ri] = cj;
5172                     }
5173                 }
5174                 return r;
5175             }
5176         }
5177         return r;
5178     }
5179
5180     function quickDiffIEXml(c1, c2){
5181         var d = ++key;
5182         for(var i = 0, len = c1.length; i < len; i++){
5183             c1[i].setAttribute("_qdiff", d);
5184         }
5185         var r = [];
5186         for(var i = 0, len = c2.length; i < len; i++){
5187             if(c2[i].getAttribute("_qdiff") != d){
5188                 r[r.length] = c2[i];
5189             }
5190         }
5191         for(var i = 0, len = c1.length; i < len; i++){
5192            c1[i].removeAttribute("_qdiff");
5193         }
5194         return r;
5195     }
5196
5197     function quickDiff(c1, c2){
5198         var len1 = c1.length;
5199         if(!len1){
5200             return c2;
5201         }
5202         if(isIE && c1[0].selectSingleNode){
5203             return quickDiffIEXml(c1, c2);
5204         }
5205         var d = ++key;
5206         for(var i = 0; i < len1; i++){
5207             c1[i]._qdiff = d;
5208         }
5209         var r = [];
5210         for(var i = 0, len = c2.length; i < len; i++){
5211             if(c2[i]._qdiff != d){
5212                 r[r.length] = c2[i];
5213             }
5214         }
5215         return r;
5216     }
5217
5218     function quickId(ns, mode, root, id){
5219         if(ns == root){
5220            var d = root.ownerDocument || root;
5221            return d.getElementById(id);
5222         }
5223         ns = getNodes(ns, mode, "*");
5224         return byId(ns, null, id);
5225     }
5226
5227     return {
5228         getStyle : function(el, name){
5229             return Roo.fly(el).getStyle(name);
5230         },
5231         /**
5232          * Compiles a selector/xpath query into a reusable function. The returned function
5233          * takes one parameter "root" (optional), which is the context node from where the query should start.
5234          * @param {String} selector The selector/xpath query
5235          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5236          * @return {Function}
5237          */
5238         compile : function(path, type){
5239             type = type || "select";
5240             
5241             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5242             var q = path, mode, lq;
5243             var tk = Roo.DomQuery.matchers;
5244             var tklen = tk.length;
5245             var mm;
5246
5247             // accept leading mode switch
5248             var lmode = q.match(modeRe);
5249             if(lmode && lmode[1]){
5250                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5251                 q = q.replace(lmode[1], "");
5252             }
5253             // strip leading slashes
5254             while(path.substr(0, 1)=="/"){
5255                 path = path.substr(1);
5256             }
5257
5258             while(q && lq != q){
5259                 lq = q;
5260                 var tm = q.match(tagTokenRe);
5261                 if(type == "select"){
5262                     if(tm){
5263                         if(tm[1] == "#"){
5264                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5265                         }else{
5266                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5267                         }
5268                         q = q.replace(tm[0], "");
5269                     }else if(q.substr(0, 1) != '@'){
5270                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5271                     }
5272                 }else{
5273                     if(tm){
5274                         if(tm[1] == "#"){
5275                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5276                         }else{
5277                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5278                         }
5279                         q = q.replace(tm[0], "");
5280                     }
5281                 }
5282                 while(!(mm = q.match(modeRe))){
5283                     var matched = false;
5284                     for(var j = 0; j < tklen; j++){
5285                         var t = tk[j];
5286                         var m = q.match(t.re);
5287                         if(m){
5288                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5289                                                     return m[i];
5290                                                 });
5291                             q = q.replace(m[0], "");
5292                             matched = true;
5293                             break;
5294                         }
5295                     }
5296                     // prevent infinite loop on bad selector
5297                     if(!matched){
5298                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5299                     }
5300                 }
5301                 if(mm[1]){
5302                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5303                     q = q.replace(mm[1], "");
5304                 }
5305             }
5306             fn[fn.length] = "return nodup(n);\n}";
5307             
5308              /** 
5309               * list of variables that need from compression as they are used by eval.
5310              *  eval:var:batch 
5311              *  eval:var:nodup
5312              *  eval:var:byTag
5313              *  eval:var:ById
5314              *  eval:var:getNodes
5315              *  eval:var:quickId
5316              *  eval:var:mode
5317              *  eval:var:root
5318              *  eval:var:n
5319              *  eval:var:byClassName
5320              *  eval:var:byPseudo
5321              *  eval:var:byAttribute
5322              *  eval:var:attrValue
5323              * 
5324              **/ 
5325             eval(fn.join(""));
5326             return f;
5327         },
5328
5329         /**
5330          * Selects a group of elements.
5331          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5332          * @param {Node} root (optional) The start of the query (defaults to document).
5333          * @return {Array}
5334          */
5335         select : function(path, root, type){
5336             if(!root || root == document){
5337                 root = document;
5338             }
5339             if(typeof root == "string"){
5340                 root = document.getElementById(root);
5341             }
5342             var paths = path.split(",");
5343             var results = [];
5344             for(var i = 0, len = paths.length; i < len; i++){
5345                 var p = paths[i].replace(trimRe, "");
5346                 if(!cache[p]){
5347                     cache[p] = Roo.DomQuery.compile(p);
5348                     if(!cache[p]){
5349                         throw p + " is not a valid selector";
5350                     }
5351                 }
5352                 var result = cache[p](root);
5353                 if(result && result != document){
5354                     results = results.concat(result);
5355                 }
5356             }
5357             if(paths.length > 1){
5358                 return nodup(results);
5359             }
5360             return results;
5361         },
5362
5363         /**
5364          * Selects a single element.
5365          * @param {String} selector The selector/xpath query
5366          * @param {Node} root (optional) The start of the query (defaults to document).
5367          * @return {Element}
5368          */
5369         selectNode : function(path, root){
5370             return Roo.DomQuery.select(path, root)[0];
5371         },
5372
5373         /**
5374          * Selects the value of a node, optionally replacing null with the defaultValue.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {String} defaultValue
5378          */
5379         selectValue : function(path, root, defaultValue){
5380             path = path.replace(trimRe, "");
5381             if(!valueCache[path]){
5382                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5383             }
5384             var n = valueCache[path](root);
5385             n = n[0] ? n[0] : n;
5386             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5387             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5388         },
5389
5390         /**
5391          * Selects the value of a node, parsing integers and floats.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {Number} defaultValue
5395          * @return {Number}
5396          */
5397         selectNumber : function(path, root, defaultValue){
5398             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5399             return parseFloat(v);
5400         },
5401
5402         /**
5403          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5404          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5405          * @param {String} selector The simple selector to test
5406          * @return {Boolean}
5407          */
5408         is : function(el, ss){
5409             if(typeof el == "string"){
5410                 el = document.getElementById(el);
5411             }
5412             var isArray = (el instanceof Array);
5413             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5414             return isArray ? (result.length == el.length) : (result.length > 0);
5415         },
5416
5417         /**
5418          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5419          * @param {Array} el An array of elements to filter
5420          * @param {String} selector The simple selector to test
5421          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5422          * the selector instead of the ones that match
5423          * @return {Array}
5424          */
5425         filter : function(els, ss, nonMatches){
5426             ss = ss.replace(trimRe, "");
5427             if(!simpleCache[ss]){
5428                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5429             }
5430             var result = simpleCache[ss](els);
5431             return nonMatches ? quickDiff(result, els) : result;
5432         },
5433
5434         /**
5435          * Collection of matching regular expressions and code snippets.
5436          */
5437         matchers : [{
5438                 re: /^\.([\w-]+)/,
5439                 select: 'n = byClassName(n, null, " {1} ");'
5440             }, {
5441                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5442                 select: 'n = byPseudo(n, "{1}", "{2}");'
5443             },{
5444                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5445                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5446             }, {
5447                 re: /^#([\w-]+)/,
5448                 select: 'n = byId(n, null, "{1}");'
5449             },{
5450                 re: /^@([\w-]+)/,
5451                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5452             }
5453         ],
5454
5455         /**
5456          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5457          * 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;.
5458          */
5459         operators : {
5460             "=" : function(a, v){
5461                 return a == v;
5462             },
5463             "!=" : function(a, v){
5464                 return a != v;
5465             },
5466             "^=" : function(a, v){
5467                 return a && a.substr(0, v.length) == v;
5468             },
5469             "$=" : function(a, v){
5470                 return a && a.substr(a.length-v.length) == v;
5471             },
5472             "*=" : function(a, v){
5473                 return a && a.indexOf(v) !== -1;
5474             },
5475             "%=" : function(a, v){
5476                 return (a % v) == 0;
5477             },
5478             "|=" : function(a, v){
5479                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5480             },
5481             "~=" : function(a, v){
5482                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5483             }
5484         },
5485
5486         /**
5487          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5488          * and the argument (if any) supplied in the selector.
5489          */
5490         pseudos : {
5491             "first-child" : function(c){
5492                 var r = [], ri = -1, n;
5493                 for(var i = 0, ci; ci = n = c[i]; i++){
5494                     while((n = n.previousSibling) && n.nodeType != 1);
5495                     if(!n){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "last-child" : function(c){
5503                 var r = [], ri = -1, n;
5504                 for(var i = 0, ci; ci = n = c[i]; i++){
5505                     while((n = n.nextSibling) && n.nodeType != 1);
5506                     if(!n){
5507                         r[++ri] = ci;
5508                     }
5509                 }
5510                 return r;
5511             },
5512
5513             "nth-child" : function(c, a) {
5514                 var r = [], ri = -1;
5515                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5516                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5517                 for(var i = 0, n; n = c[i]; i++){
5518                     var pn = n.parentNode;
5519                     if (batch != pn._batch) {
5520                         var j = 0;
5521                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5522                             if(cn.nodeType == 1){
5523                                cn.nodeIndex = ++j;
5524                             }
5525                         }
5526                         pn._batch = batch;
5527                     }
5528                     if (f == 1) {
5529                         if (l == 0 || n.nodeIndex == l){
5530                             r[++ri] = n;
5531                         }
5532                     } else if ((n.nodeIndex + l) % f == 0){
5533                         r[++ri] = n;
5534                     }
5535                 }
5536
5537                 return r;
5538             },
5539
5540             "only-child" : function(c){
5541                 var r = [], ri = -1;;
5542                 for(var i = 0, ci; ci = c[i]; i++){
5543                     if(!prev(ci) && !next(ci)){
5544                         r[++ri] = ci;
5545                     }
5546                 }
5547                 return r;
5548             },
5549
5550             "empty" : function(c){
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     var cns = ci.childNodes, j = 0, cn, empty = true;
5554                     while(cn = cns[j]){
5555                         ++j;
5556                         if(cn.nodeType == 1 || cn.nodeType == 3){
5557                             empty = false;
5558                             break;
5559                         }
5560                     }
5561                     if(empty){
5562                         r[++ri] = ci;
5563                     }
5564                 }
5565                 return r;
5566             },
5567
5568             "contains" : function(c, v){
5569                 var r = [], ri = -1;
5570                 for(var i = 0, ci; ci = c[i]; i++){
5571                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5572                         r[++ri] = ci;
5573                     }
5574                 }
5575                 return r;
5576             },
5577
5578             "nodeValue" : function(c, v){
5579                 var r = [], ri = -1;
5580                 for(var i = 0, ci; ci = c[i]; i++){
5581                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5582                         r[++ri] = ci;
5583                     }
5584                 }
5585                 return r;
5586             },
5587
5588             "checked" : function(c){
5589                 var r = [], ri = -1;
5590                 for(var i = 0, ci; ci = c[i]; i++){
5591                     if(ci.checked == true){
5592                         r[++ri] = ci;
5593                     }
5594                 }
5595                 return r;
5596             },
5597
5598             "not" : function(c, ss){
5599                 return Roo.DomQuery.filter(c, ss, true);
5600             },
5601
5602             "odd" : function(c){
5603                 return this["nth-child"](c, "odd");
5604             },
5605
5606             "even" : function(c){
5607                 return this["nth-child"](c, "even");
5608             },
5609
5610             "nth" : function(c, a){
5611                 return c[a-1] || [];
5612             },
5613
5614             "first" : function(c){
5615                 return c[0] || [];
5616             },
5617
5618             "last" : function(c){
5619                 return c[c.length-1] || [];
5620             },
5621
5622             "has" : function(c, ss){
5623                 var s = Roo.DomQuery.select;
5624                 var r = [], ri = -1;
5625                 for(var i = 0, ci; ci = c[i]; i++){
5626                     if(s(ss, ci).length > 0){
5627                         r[++ri] = ci;
5628                     }
5629                 }
5630                 return r;
5631             },
5632
5633             "next" : function(c, ss){
5634                 var is = Roo.DomQuery.is;
5635                 var r = [], ri = -1;
5636                 for(var i = 0, ci; ci = c[i]; i++){
5637                     var n = next(ci);
5638                     if(n && is(n, ss)){
5639                         r[++ri] = ci;
5640                     }
5641                 }
5642                 return r;
5643             },
5644
5645             "prev" : function(c, ss){
5646                 var is = Roo.DomQuery.is;
5647                 var r = [], ri = -1;
5648                 for(var i = 0, ci; ci = c[i]; i++){
5649                     var n = prev(ci);
5650                     if(n && is(n, ss)){
5651                         r[++ri] = ci;
5652                     }
5653                 }
5654                 return r;
5655             }
5656         }
5657     };
5658 }();
5659
5660 /**
5661  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5662  * @param {String} path The selector/xpath query
5663  * @param {Node} root (optional) The start of the query (defaults to document).
5664  * @return {Array}
5665  * @member Roo
5666  * @method query
5667  */
5668 Roo.query = Roo.DomQuery.select;
5669 /*
5670  * Based on:
5671  * Ext JS Library 1.1.1
5672  * Copyright(c) 2006-2007, Ext JS, LLC.
5673  *
5674  * Originally Released Under LGPL - original licence link has changed is not relivant.
5675  *
5676  * Fork - LGPL
5677  * <script type="text/javascript">
5678  */
5679
5680 /**
5681  * @class Roo.util.Observable
5682  * Base class that provides a common interface for publishing events. Subclasses are expected to
5683  * to have a property "events" with all the events defined.<br>
5684  * For example:
5685  * <pre><code>
5686  Employee = function(name){
5687     this.name = name;
5688     this.addEvents({
5689         "fired" : true,
5690         "quit" : true
5691     });
5692  }
5693  Roo.extend(Employee, Roo.util.Observable);
5694 </code></pre>
5695  * @param {Object} config properties to use (incuding events / listeners)
5696  */
5697
5698 Roo.util.Observable = function(cfg){
5699     
5700     cfg = cfg|| {};
5701     this.addEvents(cfg.events || {});
5702     if (cfg.events) {
5703         delete cfg.events; // make sure
5704     }
5705      
5706     Roo.apply(this, cfg);
5707     
5708     if(this.listeners){
5709         this.on(this.listeners);
5710         delete this.listeners;
5711     }
5712 };
5713 Roo.util.Observable.prototype = {
5714     /** 
5715  * @cfg {Object} listeners  list of events and functions to call for this object, 
5716  * For example :
5717  * <pre><code>
5718     listeners :  { 
5719        'click' : function(e) {
5720            ..... 
5721         } ,
5722         .... 
5723     } 
5724   </code></pre>
5725  */
5726     
5727     
5728     /**
5729      * Fires the specified event with the passed parameters (minus the event name).
5730      * @param {String} eventName
5731      * @param {Object...} args Variable number of parameters are passed to handlers
5732      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5733      */
5734     fireEvent : function(){
5735         var ce = this.events[arguments[0].toLowerCase()];
5736         if(typeof ce == "object"){
5737             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5738         }else{
5739             return true;
5740         }
5741     },
5742
5743     // private
5744     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5745
5746     /**
5747      * Appends an event handler to this component
5748      * @param {String}   eventName The type of event to listen for
5749      * @param {Function} handler The method the event invokes
5750      * @param {Object}   scope (optional) The scope in which to execute the handler
5751      * function. The handler function's "this" context.
5752      * @param {Object}   options (optional) An object containing handler configuration
5753      * properties. This may contain any of the following properties:<ul>
5754      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5755      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5756      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5757      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5758      * by the specified number of milliseconds. If the event fires again within that time, the original
5759      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5760      * </ul><br>
5761      * <p>
5762      * <b>Combining Options</b><br>
5763      * Using the options argument, it is possible to combine different types of listeners:<br>
5764      * <br>
5765      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5766                 <pre><code>
5767                 el.on('click', this.onClick, this, {
5768                         single: true,
5769                 delay: 100,
5770                 forumId: 4
5771                 });
5772                 </code></pre>
5773      * <p>
5774      * <b>Attaching multiple handlers in 1 call</b><br>
5775      * The method also allows for a single argument to be passed which is a config object containing properties
5776      * which specify multiple handlers.
5777      * <pre><code>
5778                 el.on({
5779                         'click': {
5780                         fn: this.onClick,
5781                         scope: this,
5782                         delay: 100
5783                 }, 
5784                 'mouseover': {
5785                         fn: this.onMouseOver,
5786                         scope: this
5787                 },
5788                 'mouseout': {
5789                         fn: this.onMouseOut,
5790                         scope: this
5791                 }
5792                 });
5793                 </code></pre>
5794      * <p>
5795      * Or a shorthand syntax which passes the same scope object to all handlers:
5796         <pre><code>
5797                 el.on({
5798                         'click': this.onClick,
5799                 'mouseover': this.onMouseOver,
5800                 'mouseout': this.onMouseOut,
5801                 scope: this
5802                 });
5803                 </code></pre>
5804      */
5805     addListener : function(eventName, fn, scope, o){
5806         if(typeof eventName == "object"){
5807             o = eventName;
5808             for(var e in o){
5809                 if(this.filterOptRe.test(e)){
5810                     continue;
5811                 }
5812                 if(typeof o[e] == "function"){
5813                     // shared options
5814                     this.addListener(e, o[e], o.scope,  o);
5815                 }else{
5816                     // individual options
5817                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5818                 }
5819             }
5820             return;
5821         }
5822         o = (!o || typeof o == "boolean") ? {} : o;
5823         eventName = eventName.toLowerCase();
5824         var ce = this.events[eventName] || true;
5825         if(typeof ce == "boolean"){
5826             ce = new Roo.util.Event(this, eventName);
5827             this.events[eventName] = ce;
5828         }
5829         ce.addListener(fn, scope, o);
5830     },
5831
5832     /**
5833      * Removes a listener
5834      * @param {String}   eventName     The type of event to listen for
5835      * @param {Function} handler        The handler to remove
5836      * @param {Object}   scope  (optional) The scope (this object) for the handler
5837      */
5838     removeListener : function(eventName, fn, scope){
5839         var ce = this.events[eventName.toLowerCase()];
5840         if(typeof ce == "object"){
5841             ce.removeListener(fn, scope);
5842         }
5843     },
5844
5845     /**
5846      * Removes all listeners for this object
5847      */
5848     purgeListeners : function(){
5849         for(var evt in this.events){
5850             if(typeof this.events[evt] == "object"){
5851                  this.events[evt].clearListeners();
5852             }
5853         }
5854     },
5855
5856     relayEvents : function(o, events){
5857         var createHandler = function(ename){
5858             return function(){
5859                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5860             };
5861         };
5862         for(var i = 0, len = events.length; i < len; i++){
5863             var ename = events[i];
5864             if(!this.events[ename]){ this.events[ename] = true; };
5865             o.on(ename, createHandler(ename), this);
5866         }
5867     },
5868
5869     /**
5870      * Used to define events on this Observable
5871      * @param {Object} object The object with the events defined
5872      */
5873     addEvents : function(o){
5874         if(!this.events){
5875             this.events = {};
5876         }
5877         Roo.applyIf(this.events, o);
5878     },
5879
5880     /**
5881      * Checks to see if this object has any listeners for a specified event
5882      * @param {String} eventName The name of the event to check for
5883      * @return {Boolean} True if the event is being listened for, else false
5884      */
5885     hasListener : function(eventName){
5886         var e = this.events[eventName];
5887         return typeof e == "object" && e.listeners.length > 0;
5888     }
5889 };
5890 /**
5891  * Appends an event handler to this element (shorthand for addListener)
5892  * @param {String}   eventName     The type of event to listen for
5893  * @param {Function} handler        The method the event invokes
5894  * @param {Object}   scope (optional) The scope in which to execute the handler
5895  * function. The handler function's "this" context.
5896  * @param {Object}   options  (optional)
5897  * @method
5898  */
5899 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5900 /**
5901  * Removes a listener (shorthand for removeListener)
5902  * @param {String}   eventName     The type of event to listen for
5903  * @param {Function} handler        The handler to remove
5904  * @param {Object}   scope  (optional) The scope (this object) for the handler
5905  * @method
5906  */
5907 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5908
5909 /**
5910  * Starts capture on the specified Observable. All events will be passed
5911  * to the supplied function with the event name + standard signature of the event
5912  * <b>before</b> the event is fired. If the supplied function returns false,
5913  * the event will not fire.
5914  * @param {Observable} o The Observable to capture
5915  * @param {Function} fn The function to call
5916  * @param {Object} scope (optional) The scope (this object) for the fn
5917  * @static
5918  */
5919 Roo.util.Observable.capture = function(o, fn, scope){
5920     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5921 };
5922
5923 /**
5924  * Removes <b>all</b> added captures from the Observable.
5925  * @param {Observable} o The Observable to release
5926  * @static
5927  */
5928 Roo.util.Observable.releaseCapture = function(o){
5929     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5930 };
5931
5932 (function(){
5933
5934     var createBuffered = function(h, o, scope){
5935         var task = new Roo.util.DelayedTask();
5936         return function(){
5937             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5938         };
5939     };
5940
5941     var createSingle = function(h, e, fn, scope){
5942         return function(){
5943             e.removeListener(fn, scope);
5944             return h.apply(scope, arguments);
5945         };
5946     };
5947
5948     var createDelayed = function(h, o, scope){
5949         return function(){
5950             var args = Array.prototype.slice.call(arguments, 0);
5951             setTimeout(function(){
5952                 h.apply(scope, args);
5953             }, o.delay || 10);
5954         };
5955     };
5956
5957     Roo.util.Event = function(obj, name){
5958         this.name = name;
5959         this.obj = obj;
5960         this.listeners = [];
5961     };
5962
5963     Roo.util.Event.prototype = {
5964         addListener : function(fn, scope, options){
5965             var o = options || {};
5966             scope = scope || this.obj;
5967             if(!this.isListening(fn, scope)){
5968                 var l = {fn: fn, scope: scope, options: o};
5969                 var h = fn;
5970                 if(o.delay){
5971                     h = createDelayed(h, o, scope);
5972                 }
5973                 if(o.single){
5974                     h = createSingle(h, this, fn, scope);
5975                 }
5976                 if(o.buffer){
5977                     h = createBuffered(h, o, scope);
5978                 }
5979                 l.fireFn = h;
5980                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5981                     this.listeners.push(l);
5982                 }else{
5983                     this.listeners = this.listeners.slice(0);
5984                     this.listeners.push(l);
5985                 }
5986             }
5987         },
5988
5989         findListener : function(fn, scope){
5990             scope = scope || this.obj;
5991             var ls = this.listeners;
5992             for(var i = 0, len = ls.length; i < len; i++){
5993                 var l = ls[i];
5994                 if(l.fn == fn && l.scope == scope){
5995                     return i;
5996                 }
5997             }
5998             return -1;
5999         },
6000
6001         isListening : function(fn, scope){
6002             return this.findListener(fn, scope) != -1;
6003         },
6004
6005         removeListener : function(fn, scope){
6006             var index;
6007             if((index = this.findListener(fn, scope)) != -1){
6008                 if(!this.firing){
6009                     this.listeners.splice(index, 1);
6010                 }else{
6011                     this.listeners = this.listeners.slice(0);
6012                     this.listeners.splice(index, 1);
6013                 }
6014                 return true;
6015             }
6016             return false;
6017         },
6018
6019         clearListeners : function(){
6020             this.listeners = [];
6021         },
6022
6023         fire : function(){
6024             var ls = this.listeners, scope, len = ls.length;
6025             if(len > 0){
6026                 this.firing = true;
6027                 var args = Array.prototype.slice.call(arguments, 0);
6028                 for(var i = 0; i < len; i++){
6029                     var l = ls[i];
6030                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6031                         this.firing = false;
6032                         return false;
6033                     }
6034                 }
6035                 this.firing = false;
6036             }
6037             return true;
6038         }
6039     };
6040 })();/*
6041  * Based on:
6042  * Ext JS Library 1.1.1
6043  * Copyright(c) 2006-2007, Ext JS, LLC.
6044  *
6045  * Originally Released Under LGPL - original licence link has changed is not relivant.
6046  *
6047  * Fork - LGPL
6048  * <script type="text/javascript">
6049  */
6050
6051 /**
6052  * @class Roo.EventManager
6053  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6054  * several useful events directly.
6055  * See {@link Roo.EventObject} for more details on normalized event objects.
6056  * @singleton
6057  */
6058 Roo.EventManager = function(){
6059     var docReadyEvent, docReadyProcId, docReadyState = false;
6060     var resizeEvent, resizeTask, textEvent, textSize;
6061     var E = Roo.lib.Event;
6062     var D = Roo.lib.Dom;
6063
6064     
6065     
6066
6067     var fireDocReady = function(){
6068         if(!docReadyState){
6069             docReadyState = true;
6070             Roo.isReady = true;
6071             if(docReadyProcId){
6072                 clearInterval(docReadyProcId);
6073             }
6074             if(Roo.isGecko || Roo.isOpera) {
6075                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6076             }
6077             if(Roo.isIE){
6078                 var defer = document.getElementById("ie-deferred-loader");
6079                 if(defer){
6080                     defer.onreadystatechange = null;
6081                     defer.parentNode.removeChild(defer);
6082                 }
6083             }
6084             if(docReadyEvent){
6085                 docReadyEvent.fire();
6086                 docReadyEvent.clearListeners();
6087             }
6088         }
6089     };
6090     
6091     var initDocReady = function(){
6092         docReadyEvent = new Roo.util.Event();
6093         if(Roo.isGecko || Roo.isOpera) {
6094             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6095         }else if(Roo.isIE){
6096             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6097             var defer = document.getElementById("ie-deferred-loader");
6098             defer.onreadystatechange = function(){
6099                 if(this.readyState == "complete"){
6100                     fireDocReady();
6101                 }
6102             };
6103         }else if(Roo.isSafari){ 
6104             docReadyProcId = setInterval(function(){
6105                 var rs = document.readyState;
6106                 if(rs == "complete") {
6107                     fireDocReady();     
6108                  }
6109             }, 10);
6110         }
6111         // no matter what, make sure it fires on load
6112         E.on(window, "load", fireDocReady);
6113     };
6114
6115     var createBuffered = function(h, o){
6116         var task = new Roo.util.DelayedTask(h);
6117         return function(e){
6118             // create new event object impl so new events don't wipe out properties
6119             e = new Roo.EventObjectImpl(e);
6120             task.delay(o.buffer, h, null, [e]);
6121         };
6122     };
6123
6124     var createSingle = function(h, el, ename, fn){
6125         return function(e){
6126             Roo.EventManager.removeListener(el, ename, fn);
6127             h(e);
6128         };
6129     };
6130
6131     var createDelayed = function(h, o){
6132         return function(e){
6133             // create new event object impl so new events don't wipe out properties
6134             e = new Roo.EventObjectImpl(e);
6135             setTimeout(function(){
6136                 h(e);
6137             }, o.delay || 10);
6138         };
6139     };
6140     var transitionEndVal = false;
6141     
6142     var transitionEnd = function()
6143     {
6144         if (transitionEndVal) {
6145             return transitionEndVal;
6146         }
6147         var el = document.createElement('div');
6148
6149         var transEndEventNames = {
6150             WebkitTransition : 'webkitTransitionEnd',
6151             MozTransition    : 'transitionend',
6152             OTransition      : 'oTransitionEnd otransitionend',
6153             transition       : 'transitionend'
6154         };
6155     
6156         for (var name in transEndEventNames) {
6157             if (el.style[name] !== undefined) {
6158                 transitionEndVal = transEndEventNames[name];
6159                 return  transitionEndVal ;
6160             }
6161         }
6162     }
6163     
6164
6165     var listen = function(element, ename, opt, fn, scope){
6166         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6167         fn = fn || o.fn; scope = scope || o.scope;
6168         var el = Roo.getDom(element);
6169         
6170         
6171         if(!el){
6172             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6173         }
6174         
6175         if (ename == 'transitionend') {
6176             ename = transitionEnd();
6177         }
6178         var h = function(e){
6179             e = Roo.EventObject.setEvent(e);
6180             var t;
6181             if(o.delegate){
6182                 t = e.getTarget(o.delegate, el);
6183                 if(!t){
6184                     return;
6185                 }
6186             }else{
6187                 t = e.target;
6188             }
6189             if(o.stopEvent === true){
6190                 e.stopEvent();
6191             }
6192             if(o.preventDefault === true){
6193                e.preventDefault();
6194             }
6195             if(o.stopPropagation === true){
6196                 e.stopPropagation();
6197             }
6198
6199             if(o.normalized === false){
6200                 e = e.browserEvent;
6201             }
6202
6203             fn.call(scope || el, e, t, o);
6204         };
6205         if(o.delay){
6206             h = createDelayed(h, o);
6207         }
6208         if(o.single){
6209             h = createSingle(h, el, ename, fn);
6210         }
6211         if(o.buffer){
6212             h = createBuffered(h, o);
6213         }
6214         fn._handlers = fn._handlers || [];
6215         
6216         
6217         fn._handlers.push([Roo.id(el), ename, h]);
6218         
6219         
6220          
6221         E.on(el, ename, h);
6222         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6223             el.addEventListener("DOMMouseScroll", h, false);
6224             E.on(window, 'unload', function(){
6225                 el.removeEventListener("DOMMouseScroll", h, false);
6226             });
6227         }
6228         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6229             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6230         }
6231         return h;
6232     };
6233
6234     var stopListening = function(el, ename, fn){
6235         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6236         if(hds){
6237             for(var i = 0, len = hds.length; i < len; i++){
6238                 var h = hds[i];
6239                 if(h[0] == id && h[1] == ename){
6240                     hd = h[2];
6241                     hds.splice(i, 1);
6242                     break;
6243                 }
6244             }
6245         }
6246         E.un(el, ename, hd);
6247         el = Roo.getDom(el);
6248         if(ename == "mousewheel" && el.addEventListener){
6249             el.removeEventListener("DOMMouseScroll", hd, false);
6250         }
6251         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6252             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6253         }
6254     };
6255
6256     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6257     
6258     var pub = {
6259         
6260         
6261         /** 
6262          * Fix for doc tools
6263          * @scope Roo.EventManager
6264          */
6265         
6266         
6267         /** 
6268          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6269          * object with a Roo.EventObject
6270          * @param {Function} fn        The method the event invokes
6271          * @param {Object}   scope    An object that becomes the scope of the handler
6272          * @param {boolean}  override If true, the obj passed in becomes
6273          *                             the execution scope of the listener
6274          * @return {Function} The wrapped function
6275          * @deprecated
6276          */
6277         wrap : function(fn, scope, override){
6278             return function(e){
6279                 Roo.EventObject.setEvent(e);
6280                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6281             };
6282         },
6283         
6284         /**
6285      * Appends an event handler to an element (shorthand for addListener)
6286      * @param {String/HTMLElement}   element        The html element or id to assign the
6287      * @param {String}   eventName The type of event to listen for
6288      * @param {Function} handler The method the event invokes
6289      * @param {Object}   scope (optional) The scope in which to execute the handler
6290      * function. The handler function's "this" context.
6291      * @param {Object}   options (optional) An object containing handler configuration
6292      * properties. This may contain any of the following properties:<ul>
6293      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6294      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6295      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6296      * <li>preventDefault {Boolean} True to prevent the default action</li>
6297      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6298      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6299      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6300      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6301      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6302      * by the specified number of milliseconds. If the event fires again within that time, the original
6303      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6304      * </ul><br>
6305      * <p>
6306      * <b>Combining Options</b><br>
6307      * Using the options argument, it is possible to combine different types of listeners:<br>
6308      * <br>
6309      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6310      * Code:<pre><code>
6311 el.on('click', this.onClick, this, {
6312     single: true,
6313     delay: 100,
6314     stopEvent : true,
6315     forumId: 4
6316 });</code></pre>
6317      * <p>
6318      * <b>Attaching multiple handlers in 1 call</b><br>
6319       * The method also allows for a single argument to be passed which is a config object containing properties
6320      * which specify multiple handlers.
6321      * <p>
6322      * Code:<pre><code>
6323 el.on({
6324     'click' : {
6325         fn: this.onClick
6326         scope: this,
6327         delay: 100
6328     },
6329     'mouseover' : {
6330         fn: this.onMouseOver
6331         scope: this
6332     },
6333     'mouseout' : {
6334         fn: this.onMouseOut
6335         scope: this
6336     }
6337 });</code></pre>
6338      * <p>
6339      * Or a shorthand syntax:<br>
6340      * Code:<pre><code>
6341 el.on({
6342     'click' : this.onClick,
6343     'mouseover' : this.onMouseOver,
6344     'mouseout' : this.onMouseOut
6345     scope: this
6346 });</code></pre>
6347      */
6348         addListener : function(element, eventName, fn, scope, options){
6349             if(typeof eventName == "object"){
6350                 var o = eventName;
6351                 for(var e in o){
6352                     if(propRe.test(e)){
6353                         continue;
6354                     }
6355                     if(typeof o[e] == "function"){
6356                         // shared options
6357                         listen(element, e, o, o[e], o.scope);
6358                     }else{
6359                         // individual options
6360                         listen(element, e, o[e]);
6361                     }
6362                 }
6363                 return;
6364             }
6365             return listen(element, eventName, options, fn, scope);
6366         },
6367         
6368         /**
6369          * Removes an event handler
6370          *
6371          * @param {String/HTMLElement}   element        The id or html element to remove the 
6372          *                             event from
6373          * @param {String}   eventName     The type of event
6374          * @param {Function} fn
6375          * @return {Boolean} True if a listener was actually removed
6376          */
6377         removeListener : function(element, eventName, fn){
6378             return stopListening(element, eventName, fn);
6379         },
6380         
6381         /**
6382          * Fires when the document is ready (before onload and before images are loaded). Can be 
6383          * accessed shorthanded Roo.onReady().
6384          * @param {Function} fn        The method the event invokes
6385          * @param {Object}   scope    An  object that becomes the scope of the handler
6386          * @param {boolean}  options
6387          */
6388         onDocumentReady : function(fn, scope, options){
6389             if(docReadyState){ // if it already fired
6390                 docReadyEvent.addListener(fn, scope, options);
6391                 docReadyEvent.fire();
6392                 docReadyEvent.clearListeners();
6393                 return;
6394             }
6395             if(!docReadyEvent){
6396                 initDocReady();
6397             }
6398             docReadyEvent.addListener(fn, scope, options);
6399         },
6400         
6401         /**
6402          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6403          * @param {Function} fn        The method the event invokes
6404          * @param {Object}   scope    An object that becomes the scope of the handler
6405          * @param {boolean}  options
6406          */
6407         onWindowResize : function(fn, scope, options){
6408             if(!resizeEvent){
6409                 resizeEvent = new Roo.util.Event();
6410                 resizeTask = new Roo.util.DelayedTask(function(){
6411                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6412                 });
6413                 E.on(window, "resize", function(){
6414                     if(Roo.isIE){
6415                         resizeTask.delay(50);
6416                     }else{
6417                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6418                     }
6419                 });
6420             }
6421             resizeEvent.addListener(fn, scope, options);
6422         },
6423
6424         /**
6425          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6426          * @param {Function} fn        The method the event invokes
6427          * @param {Object}   scope    An object that becomes the scope of the handler
6428          * @param {boolean}  options
6429          */
6430         onTextResize : function(fn, scope, options){
6431             if(!textEvent){
6432                 textEvent = new Roo.util.Event();
6433                 var textEl = new Roo.Element(document.createElement('div'));
6434                 textEl.dom.className = 'x-text-resize';
6435                 textEl.dom.innerHTML = 'X';
6436                 textEl.appendTo(document.body);
6437                 textSize = textEl.dom.offsetHeight;
6438                 setInterval(function(){
6439                     if(textEl.dom.offsetHeight != textSize){
6440                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6441                     }
6442                 }, this.textResizeInterval);
6443             }
6444             textEvent.addListener(fn, scope, options);
6445         },
6446
6447         /**
6448          * Removes the passed window resize listener.
6449          * @param {Function} fn        The method the event invokes
6450          * @param {Object}   scope    The scope of handler
6451          */
6452         removeResizeListener : function(fn, scope){
6453             if(resizeEvent){
6454                 resizeEvent.removeListener(fn, scope);
6455             }
6456         },
6457
6458         // private
6459         fireResize : function(){
6460             if(resizeEvent){
6461                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6462             }   
6463         },
6464         /**
6465          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6466          */
6467         ieDeferSrc : false,
6468         /**
6469          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6470          */
6471         textResizeInterval : 50
6472     };
6473     
6474     /**
6475      * Fix for doc tools
6476      * @scopeAlias pub=Roo.EventManager
6477      */
6478     
6479      /**
6480      * Appends an event handler to an element (shorthand for addListener)
6481      * @param {String/HTMLElement}   element        The html element or id to assign the
6482      * @param {String}   eventName The type of event to listen for
6483      * @param {Function} handler The method the event invokes
6484      * @param {Object}   scope (optional) The scope in which to execute the handler
6485      * function. The handler function's "this" context.
6486      * @param {Object}   options (optional) An object containing handler configuration
6487      * properties. This may contain any of the following properties:<ul>
6488      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6489      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6490      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6491      * <li>preventDefault {Boolean} True to prevent the default action</li>
6492      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6493      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6494      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6495      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6496      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6497      * by the specified number of milliseconds. If the event fires again within that time, the original
6498      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6499      * </ul><br>
6500      * <p>
6501      * <b>Combining Options</b><br>
6502      * Using the options argument, it is possible to combine different types of listeners:<br>
6503      * <br>
6504      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6505      * Code:<pre><code>
6506 el.on('click', this.onClick, this, {
6507     single: true,
6508     delay: 100,
6509     stopEvent : true,
6510     forumId: 4
6511 });</code></pre>
6512      * <p>
6513      * <b>Attaching multiple handlers in 1 call</b><br>
6514       * The method also allows for a single argument to be passed which is a config object containing properties
6515      * which specify multiple handlers.
6516      * <p>
6517      * Code:<pre><code>
6518 el.on({
6519     'click' : {
6520         fn: this.onClick
6521         scope: this,
6522         delay: 100
6523     },
6524     'mouseover' : {
6525         fn: this.onMouseOver
6526         scope: this
6527     },
6528     'mouseout' : {
6529         fn: this.onMouseOut
6530         scope: this
6531     }
6532 });</code></pre>
6533      * <p>
6534      * Or a shorthand syntax:<br>
6535      * Code:<pre><code>
6536 el.on({
6537     'click' : this.onClick,
6538     'mouseover' : this.onMouseOver,
6539     'mouseout' : this.onMouseOut
6540     scope: this
6541 });</code></pre>
6542      */
6543     pub.on = pub.addListener;
6544     pub.un = pub.removeListener;
6545
6546     pub.stoppedMouseDownEvent = new Roo.util.Event();
6547     return pub;
6548 }();
6549 /**
6550   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6551   * @param {Function} fn        The method the event invokes
6552   * @param {Object}   scope    An  object that becomes the scope of the handler
6553   * @param {boolean}  override If true, the obj passed in becomes
6554   *                             the execution scope of the listener
6555   * @member Roo
6556   * @method onReady
6557  */
6558 Roo.onReady = Roo.EventManager.onDocumentReady;
6559
6560 Roo.onReady(function(){
6561     var bd = Roo.get(document.body);
6562     if(!bd){ return; }
6563
6564     var cls = [
6565             Roo.isIE ? "roo-ie"
6566             : Roo.isGecko ? "roo-gecko"
6567             : Roo.isOpera ? "roo-opera"
6568             : Roo.isSafari ? "roo-safari" : ""];
6569
6570     if(Roo.isMac){
6571         cls.push("roo-mac");
6572     }
6573     if(Roo.isLinux){
6574         cls.push("roo-linux");
6575     }
6576     if(Roo.isBorderBox){
6577         cls.push('roo-border-box');
6578     }
6579     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6580         var p = bd.dom.parentNode;
6581         if(p){
6582             p.className += ' roo-strict';
6583         }
6584     }
6585     bd.addClass(cls.join(' '));
6586 });
6587
6588 /**
6589  * @class Roo.EventObject
6590  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6591  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6592  * Example:
6593  * <pre><code>
6594  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6595     e.preventDefault();
6596     var target = e.getTarget();
6597     ...
6598  }
6599  var myDiv = Roo.get("myDiv");
6600  myDiv.on("click", handleClick);
6601  //or
6602  Roo.EventManager.on("myDiv", 'click', handleClick);
6603  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6604  </code></pre>
6605  * @singleton
6606  */
6607 Roo.EventObject = function(){
6608     
6609     var E = Roo.lib.Event;
6610     
6611     // safari keypress events for special keys return bad keycodes
6612     var safariKeys = {
6613         63234 : 37, // left
6614         63235 : 39, // right
6615         63232 : 38, // up
6616         63233 : 40, // down
6617         63276 : 33, // page up
6618         63277 : 34, // page down
6619         63272 : 46, // delete
6620         63273 : 36, // home
6621         63275 : 35  // end
6622     };
6623
6624     // normalize button clicks
6625     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6626                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6627
6628     Roo.EventObjectImpl = function(e){
6629         if(e){
6630             this.setEvent(e.browserEvent || e);
6631         }
6632     };
6633     Roo.EventObjectImpl.prototype = {
6634         /**
6635          * Used to fix doc tools.
6636          * @scope Roo.EventObject.prototype
6637          */
6638             
6639
6640         
6641         
6642         /** The normal browser event */
6643         browserEvent : null,
6644         /** The button pressed in a mouse event */
6645         button : -1,
6646         /** True if the shift key was down during the event */
6647         shiftKey : false,
6648         /** True if the control key was down during the event */
6649         ctrlKey : false,
6650         /** True if the alt key was down during the event */
6651         altKey : false,
6652
6653         /** Key constant 
6654         * @type Number */
6655         BACKSPACE : 8,
6656         /** Key constant 
6657         * @type Number */
6658         TAB : 9,
6659         /** Key constant 
6660         * @type Number */
6661         RETURN : 13,
6662         /** Key constant 
6663         * @type Number */
6664         ENTER : 13,
6665         /** Key constant 
6666         * @type Number */
6667         SHIFT : 16,
6668         /** Key constant 
6669         * @type Number */
6670         CONTROL : 17,
6671         /** Key constant 
6672         * @type Number */
6673         ESC : 27,
6674         /** Key constant 
6675         * @type Number */
6676         SPACE : 32,
6677         /** Key constant 
6678         * @type Number */
6679         PAGEUP : 33,
6680         /** Key constant 
6681         * @type Number */
6682         PAGEDOWN : 34,
6683         /** Key constant 
6684         * @type Number */
6685         END : 35,
6686         /** Key constant 
6687         * @type Number */
6688         HOME : 36,
6689         /** Key constant 
6690         * @type Number */
6691         LEFT : 37,
6692         /** Key constant 
6693         * @type Number */
6694         UP : 38,
6695         /** Key constant 
6696         * @type Number */
6697         RIGHT : 39,
6698         /** Key constant 
6699         * @type Number */
6700         DOWN : 40,
6701         /** Key constant 
6702         * @type Number */
6703         DELETE : 46,
6704         /** Key constant 
6705         * @type Number */
6706         F5 : 116,
6707
6708            /** @private */
6709         setEvent : function(e){
6710             if(e == this || (e && e.browserEvent)){ // already wrapped
6711                 return e;
6712             }
6713             this.browserEvent = e;
6714             if(e){
6715                 // normalize buttons
6716                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6717                 if(e.type == 'click' && this.button == -1){
6718                     this.button = 0;
6719                 }
6720                 this.type = e.type;
6721                 this.shiftKey = e.shiftKey;
6722                 // mac metaKey behaves like ctrlKey
6723                 this.ctrlKey = e.ctrlKey || e.metaKey;
6724                 this.altKey = e.altKey;
6725                 // in getKey these will be normalized for the mac
6726                 this.keyCode = e.keyCode;
6727                 // keyup warnings on firefox.
6728                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6729                 // cache the target for the delayed and or buffered events
6730                 this.target = E.getTarget(e);
6731                 // same for XY
6732                 this.xy = E.getXY(e);
6733             }else{
6734                 this.button = -1;
6735                 this.shiftKey = false;
6736                 this.ctrlKey = false;
6737                 this.altKey = false;
6738                 this.keyCode = 0;
6739                 this.charCode =0;
6740                 this.target = null;
6741                 this.xy = [0, 0];
6742             }
6743             return this;
6744         },
6745
6746         /**
6747          * Stop the event (preventDefault and stopPropagation)
6748          */
6749         stopEvent : function(){
6750             if(this.browserEvent){
6751                 if(this.browserEvent.type == 'mousedown'){
6752                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6753                 }
6754                 E.stopEvent(this.browserEvent);
6755             }
6756         },
6757
6758         /**
6759          * Prevents the browsers default handling of the event.
6760          */
6761         preventDefault : function(){
6762             if(this.browserEvent){
6763                 E.preventDefault(this.browserEvent);
6764             }
6765         },
6766
6767         /** @private */
6768         isNavKeyPress : function(){
6769             var k = this.keyCode;
6770             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6771             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6772         },
6773
6774         isSpecialKey : function(){
6775             var k = this.keyCode;
6776             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6777             (k == 16) || (k == 17) ||
6778             (k >= 18 && k <= 20) ||
6779             (k >= 33 && k <= 35) ||
6780             (k >= 36 && k <= 39) ||
6781             (k >= 44 && k <= 45);
6782         },
6783         /**
6784          * Cancels bubbling of the event.
6785          */
6786         stopPropagation : function(){
6787             if(this.browserEvent){
6788                 if(this.type == 'mousedown'){
6789                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6790                 }
6791                 E.stopPropagation(this.browserEvent);
6792             }
6793         },
6794
6795         /**
6796          * Gets the key code for the event.
6797          * @return {Number}
6798          */
6799         getCharCode : function(){
6800             return this.charCode || this.keyCode;
6801         },
6802
6803         /**
6804          * Returns a normalized keyCode for the event.
6805          * @return {Number} The key code
6806          */
6807         getKey : function(){
6808             var k = this.keyCode || this.charCode;
6809             return Roo.isSafari ? (safariKeys[k] || k) : k;
6810         },
6811
6812         /**
6813          * Gets the x coordinate of the event.
6814          * @return {Number}
6815          */
6816         getPageX : function(){
6817             return this.xy[0];
6818         },
6819
6820         /**
6821          * Gets the y coordinate of the event.
6822          * @return {Number}
6823          */
6824         getPageY : function(){
6825             return this.xy[1];
6826         },
6827
6828         /**
6829          * Gets the time of the event.
6830          * @return {Number}
6831          */
6832         getTime : function(){
6833             if(this.browserEvent){
6834                 return E.getTime(this.browserEvent);
6835             }
6836             return null;
6837         },
6838
6839         /**
6840          * Gets the page coordinates of the event.
6841          * @return {Array} The xy values like [x, y]
6842          */
6843         getXY : function(){
6844             return this.xy;
6845         },
6846
6847         /**
6848          * Gets the target for the event.
6849          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6850          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6851                 search as a number or element (defaults to 10 || document.body)
6852          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6853          * @return {HTMLelement}
6854          */
6855         getTarget : function(selector, maxDepth, returnEl){
6856             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6857         },
6858         /**
6859          * Gets the related target.
6860          * @return {HTMLElement}
6861          */
6862         getRelatedTarget : function(){
6863             if(this.browserEvent){
6864                 return E.getRelatedTarget(this.browserEvent);
6865             }
6866             return null;
6867         },
6868
6869         /**
6870          * Normalizes mouse wheel delta across browsers
6871          * @return {Number} The delta
6872          */
6873         getWheelDelta : function(){
6874             var e = this.browserEvent;
6875             var delta = 0;
6876             if(e.wheelDelta){ /* IE/Opera. */
6877                 delta = e.wheelDelta/120;
6878             }else if(e.detail){ /* Mozilla case. */
6879                 delta = -e.detail/3;
6880             }
6881             return delta;
6882         },
6883
6884         /**
6885          * Returns true if the control, meta, shift or alt key was pressed during this event.
6886          * @return {Boolean}
6887          */
6888         hasModifier : function(){
6889             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6890         },
6891
6892         /**
6893          * Returns true if the target of this event equals el or is a child of el
6894          * @param {String/HTMLElement/Element} el
6895          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6896          * @return {Boolean}
6897          */
6898         within : function(el, related){
6899             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6900             return t && Roo.fly(el).contains(t);
6901         },
6902
6903         getPoint : function(){
6904             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6905         }
6906     };
6907
6908     return new Roo.EventObjectImpl();
6909 }();
6910             
6911     /*
6912  * Based on:
6913  * Ext JS Library 1.1.1
6914  * Copyright(c) 2006-2007, Ext JS, LLC.
6915  *
6916  * Originally Released Under LGPL - original licence link has changed is not relivant.
6917  *
6918  * Fork - LGPL
6919  * <script type="text/javascript">
6920  */
6921
6922  
6923 // was in Composite Element!??!?!
6924  
6925 (function(){
6926     var D = Roo.lib.Dom;
6927     var E = Roo.lib.Event;
6928     var A = Roo.lib.Anim;
6929
6930     // local style camelizing for speed
6931     var propCache = {};
6932     var camelRe = /(-[a-z])/gi;
6933     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6934     var view = document.defaultView;
6935
6936 /**
6937  * @class Roo.Element
6938  * Represents an Element in the DOM.<br><br>
6939  * Usage:<br>
6940 <pre><code>
6941 var el = Roo.get("my-div");
6942
6943 // or with getEl
6944 var el = getEl("my-div");
6945
6946 // or with a DOM element
6947 var el = Roo.get(myDivElement);
6948 </code></pre>
6949  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6950  * each call instead of constructing a new one.<br><br>
6951  * <b>Animations</b><br />
6952  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6953  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6954 <pre>
6955 Option    Default   Description
6956 --------- --------  ---------------------------------------------
6957 duration  .35       The duration of the animation in seconds
6958 easing    easeOut   The YUI easing method
6959 callback  none      A function to execute when the anim completes
6960 scope     this      The scope (this) of the callback function
6961 </pre>
6962 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6963 * manipulate the animation. Here's an example:
6964 <pre><code>
6965 var el = Roo.get("my-div");
6966
6967 // no animation
6968 el.setWidth(100);
6969
6970 // default animation
6971 el.setWidth(100, true);
6972
6973 // animation with some options set
6974 el.setWidth(100, {
6975     duration: 1,
6976     callback: this.foo,
6977     scope: this
6978 });
6979
6980 // using the "anim" property to get the Anim object
6981 var opt = {
6982     duration: 1,
6983     callback: this.foo,
6984     scope: this
6985 };
6986 el.setWidth(100, opt);
6987 ...
6988 if(opt.anim.isAnimated()){
6989     opt.anim.stop();
6990 }
6991 </code></pre>
6992 * <b> Composite (Collections of) Elements</b><br />
6993  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6994  * @constructor Create a new Element directly.
6995  * @param {String/HTMLElement} element
6996  * @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).
6997  */
6998     Roo.Element = function(element, forceNew){
6999         var dom = typeof element == "string" ?
7000                 document.getElementById(element) : element;
7001         if(!dom){ // invalid id/element
7002             return null;
7003         }
7004         var id = dom.id;
7005         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7006             return Roo.Element.cache[id];
7007         }
7008
7009         /**
7010          * The DOM element
7011          * @type HTMLElement
7012          */
7013         this.dom = dom;
7014
7015         /**
7016          * The DOM element ID
7017          * @type String
7018          */
7019         this.id = id || Roo.id(dom);
7020     };
7021
7022     var El = Roo.Element;
7023
7024     El.prototype = {
7025         /**
7026          * The element's default display mode  (defaults to "")
7027          * @type String
7028          */
7029         originalDisplay : "",
7030
7031         visibilityMode : 1,
7032         /**
7033          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7034          * @type String
7035          */
7036         defaultUnit : "px",
7037         /**
7038          * Sets the element's visibility mode. When setVisible() is called it
7039          * will use this to determine whether to set the visibility or the display property.
7040          * @param visMode Element.VISIBILITY or Element.DISPLAY
7041          * @return {Roo.Element} this
7042          */
7043         setVisibilityMode : function(visMode){
7044             this.visibilityMode = visMode;
7045             return this;
7046         },
7047         /**
7048          * Convenience method for setVisibilityMode(Element.DISPLAY)
7049          * @param {String} display (optional) What to set display to when visible
7050          * @return {Roo.Element} this
7051          */
7052         enableDisplayMode : function(display){
7053             this.setVisibilityMode(El.DISPLAY);
7054             if(typeof display != "undefined") this.originalDisplay = display;
7055             return this;
7056         },
7057
7058         /**
7059          * 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)
7060          * @param {String} selector The simple selector to test
7061          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7062                 search as a number or element (defaults to 10 || document.body)
7063          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7064          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7065          */
7066         findParent : function(simpleSelector, maxDepth, returnEl){
7067             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7068             maxDepth = maxDepth || 50;
7069             if(typeof maxDepth != "number"){
7070                 stopEl = Roo.getDom(maxDepth);
7071                 maxDepth = 10;
7072             }
7073             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7074                 if(dq.is(p, simpleSelector)){
7075                     return returnEl ? Roo.get(p) : p;
7076                 }
7077                 depth++;
7078                 p = p.parentNode;
7079             }
7080             return null;
7081         },
7082
7083
7084         /**
7085          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7086          * @param {String} selector The simple selector to test
7087          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7088                 search as a number or element (defaults to 10 || document.body)
7089          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7090          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7091          */
7092         findParentNode : function(simpleSelector, maxDepth, returnEl){
7093             var p = Roo.fly(this.dom.parentNode, '_internal');
7094             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7095         },
7096
7097         /**
7098          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7099          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7100          * @param {String} selector The simple selector to test
7101          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7102                 search as a number or element (defaults to 10 || document.body)
7103          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7104          */
7105         up : function(simpleSelector, maxDepth){
7106             return this.findParentNode(simpleSelector, maxDepth, true);
7107         },
7108
7109
7110
7111         /**
7112          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7113          * @param {String} selector The simple selector to test
7114          * @return {Boolean} True if this element matches the selector, else false
7115          */
7116         is : function(simpleSelector){
7117             return Roo.DomQuery.is(this.dom, simpleSelector);
7118         },
7119
7120         /**
7121          * Perform animation on this element.
7122          * @param {Object} args The YUI animation control args
7123          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7124          * @param {Function} onComplete (optional) Function to call when animation completes
7125          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7126          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7127          * @return {Roo.Element} this
7128          */
7129         animate : function(args, duration, onComplete, easing, animType){
7130             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7131             return this;
7132         },
7133
7134         /*
7135          * @private Internal animation call
7136          */
7137         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7138             animType = animType || 'run';
7139             opt = opt || {};
7140             var anim = Roo.lib.Anim[animType](
7141                 this.dom, args,
7142                 (opt.duration || defaultDur) || .35,
7143                 (opt.easing || defaultEase) || 'easeOut',
7144                 function(){
7145                     Roo.callback(cb, this);
7146                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7147                 },
7148                 this
7149             );
7150             opt.anim = anim;
7151             return anim;
7152         },
7153
7154         // private legacy anim prep
7155         preanim : function(a, i){
7156             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7157         },
7158
7159         /**
7160          * Removes worthless text nodes
7161          * @param {Boolean} forceReclean (optional) By default the element
7162          * keeps track if it has been cleaned already so
7163          * you can call this over and over. However, if you update the element and
7164          * need to force a reclean, you can pass true.
7165          */
7166         clean : function(forceReclean){
7167             if(this.isCleaned && forceReclean !== true){
7168                 return this;
7169             }
7170             var ns = /\S/;
7171             var d = this.dom, n = d.firstChild, ni = -1;
7172             while(n){
7173                 var nx = n.nextSibling;
7174                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7175                     d.removeChild(n);
7176                 }else{
7177                     n.nodeIndex = ++ni;
7178                 }
7179                 n = nx;
7180             }
7181             this.isCleaned = true;
7182             return this;
7183         },
7184
7185         // private
7186         calcOffsetsTo : function(el){
7187             el = Roo.get(el);
7188             var d = el.dom;
7189             var restorePos = false;
7190             if(el.getStyle('position') == 'static'){
7191                 el.position('relative');
7192                 restorePos = true;
7193             }
7194             var x = 0, y =0;
7195             var op = this.dom;
7196             while(op && op != d && op.tagName != 'HTML'){
7197                 x+= op.offsetLeft;
7198                 y+= op.offsetTop;
7199                 op = op.offsetParent;
7200             }
7201             if(restorePos){
7202                 el.position('static');
7203             }
7204             return [x, y];
7205         },
7206
7207         /**
7208          * Scrolls this element into view within the passed container.
7209          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7210          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7211          * @return {Roo.Element} this
7212          */
7213         scrollIntoView : function(container, hscroll){
7214             var c = Roo.getDom(container) || document.body;
7215             var el = this.dom;
7216
7217             var o = this.calcOffsetsTo(c),
7218                 l = o[0],
7219                 t = o[1],
7220                 b = t+el.offsetHeight,
7221                 r = l+el.offsetWidth;
7222
7223             var ch = c.clientHeight;
7224             var ct = parseInt(c.scrollTop, 10);
7225             var cl = parseInt(c.scrollLeft, 10);
7226             var cb = ct + ch;
7227             var cr = cl + c.clientWidth;
7228
7229             if(t < ct){
7230                 c.scrollTop = t;
7231             }else if(b > cb){
7232                 c.scrollTop = b-ch;
7233             }
7234
7235             if(hscroll !== false){
7236                 if(l < cl){
7237                     c.scrollLeft = l;
7238                 }else if(r > cr){
7239                     c.scrollLeft = r-c.clientWidth;
7240                 }
7241             }
7242             return this;
7243         },
7244
7245         // private
7246         scrollChildIntoView : function(child, hscroll){
7247             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7248         },
7249
7250         /**
7251          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7252          * the new height may not be available immediately.
7253          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7254          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7255          * @param {Function} onComplete (optional) Function to call when animation completes
7256          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7257          * @return {Roo.Element} this
7258          */
7259         autoHeight : function(animate, duration, onComplete, easing){
7260             var oldHeight = this.getHeight();
7261             this.clip();
7262             this.setHeight(1); // force clipping
7263             setTimeout(function(){
7264                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7265                 if(!animate){
7266                     this.setHeight(height);
7267                     this.unclip();
7268                     if(typeof onComplete == "function"){
7269                         onComplete();
7270                     }
7271                 }else{
7272                     this.setHeight(oldHeight); // restore original height
7273                     this.setHeight(height, animate, duration, function(){
7274                         this.unclip();
7275                         if(typeof onComplete == "function") onComplete();
7276                     }.createDelegate(this), easing);
7277                 }
7278             }.createDelegate(this), 0);
7279             return this;
7280         },
7281
7282         /**
7283          * Returns true if this element is an ancestor of the passed element
7284          * @param {HTMLElement/String} el The element to check
7285          * @return {Boolean} True if this element is an ancestor of el, else false
7286          */
7287         contains : function(el){
7288             if(!el){return false;}
7289             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7290         },
7291
7292         /**
7293          * Checks whether the element is currently visible using both visibility and display properties.
7294          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7295          * @return {Boolean} True if the element is currently visible, else false
7296          */
7297         isVisible : function(deep) {
7298             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7299             if(deep !== true || !vis){
7300                 return vis;
7301             }
7302             var p = this.dom.parentNode;
7303             while(p && p.tagName.toLowerCase() != "body"){
7304                 if(!Roo.fly(p, '_isVisible').isVisible()){
7305                     return false;
7306                 }
7307                 p = p.parentNode;
7308             }
7309             return true;
7310         },
7311
7312         /**
7313          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7314          * @param {String} selector The CSS selector
7315          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7316          * @return {CompositeElement/CompositeElementLite} The composite element
7317          */
7318         select : function(selector, unique){
7319             return El.select(selector, unique, this.dom);
7320         },
7321
7322         /**
7323          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7324          * @param {String} selector The CSS selector
7325          * @return {Array} An array of the matched nodes
7326          */
7327         query : function(selector, unique){
7328             return Roo.DomQuery.select(selector, this.dom);
7329         },
7330
7331         /**
7332          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7333          * @param {String} selector The CSS selector
7334          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7335          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7336          */
7337         child : function(selector, returnDom){
7338             var n = Roo.DomQuery.selectNode(selector, this.dom);
7339             return returnDom ? n : Roo.get(n);
7340         },
7341
7342         /**
7343          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7344          * @param {String} selector The CSS selector
7345          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7346          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7347          */
7348         down : function(selector, returnDom){
7349             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7350             return returnDom ? n : Roo.get(n);
7351         },
7352
7353         /**
7354          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7355          * @param {String} group The group the DD object is member of
7356          * @param {Object} config The DD config object
7357          * @param {Object} overrides An object containing methods to override/implement on the DD object
7358          * @return {Roo.dd.DD} The DD object
7359          */
7360         initDD : function(group, config, overrides){
7361             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7362             return Roo.apply(dd, overrides);
7363         },
7364
7365         /**
7366          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7367          * @param {String} group The group the DDProxy object is member of
7368          * @param {Object} config The DDProxy config object
7369          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7370          * @return {Roo.dd.DDProxy} The DDProxy object
7371          */
7372         initDDProxy : function(group, config, overrides){
7373             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7374             return Roo.apply(dd, overrides);
7375         },
7376
7377         /**
7378          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7379          * @param {String} group The group the DDTarget object is member of
7380          * @param {Object} config The DDTarget config object
7381          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7382          * @return {Roo.dd.DDTarget} The DDTarget object
7383          */
7384         initDDTarget : function(group, config, overrides){
7385             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7386             return Roo.apply(dd, overrides);
7387         },
7388
7389         /**
7390          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7391          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7392          * @param {Boolean} visible Whether the element is visible
7393          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7394          * @return {Roo.Element} this
7395          */
7396          setVisible : function(visible, animate){
7397             if(!animate || !A){
7398                 if(this.visibilityMode == El.DISPLAY){
7399                     this.setDisplayed(visible);
7400                 }else{
7401                     this.fixDisplay();
7402                     this.dom.style.visibility = visible ? "visible" : "hidden";
7403                 }
7404             }else{
7405                 // closure for composites
7406                 var dom = this.dom;
7407                 var visMode = this.visibilityMode;
7408                 if(visible){
7409                     this.setOpacity(.01);
7410                     this.setVisible(true);
7411                 }
7412                 this.anim({opacity: { to: (visible?1:0) }},
7413                       this.preanim(arguments, 1),
7414                       null, .35, 'easeIn', function(){
7415                          if(!visible){
7416                              if(visMode == El.DISPLAY){
7417                                  dom.style.display = "none";
7418                              }else{
7419                                  dom.style.visibility = "hidden";
7420                              }
7421                              Roo.get(dom).setOpacity(1);
7422                          }
7423                      });
7424             }
7425             return this;
7426         },
7427
7428         /**
7429          * Returns true if display is not "none"
7430          * @return {Boolean}
7431          */
7432         isDisplayed : function() {
7433             return this.getStyle("display") != "none";
7434         },
7435
7436         /**
7437          * Toggles the element's visibility or display, depending on visibility mode.
7438          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7439          * @return {Roo.Element} this
7440          */
7441         toggle : function(animate){
7442             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7443             return this;
7444         },
7445
7446         /**
7447          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7448          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7449          * @return {Roo.Element} this
7450          */
7451         setDisplayed : function(value) {
7452             if(typeof value == "boolean"){
7453                value = value ? this.originalDisplay : "none";
7454             }
7455             this.setStyle("display", value);
7456             return this;
7457         },
7458
7459         /**
7460          * Tries to focus the element. Any exceptions are caught and ignored.
7461          * @return {Roo.Element} this
7462          */
7463         focus : function() {
7464             try{
7465                 this.dom.focus();
7466             }catch(e){}
7467             return this;
7468         },
7469
7470         /**
7471          * Tries to blur the element. Any exceptions are caught and ignored.
7472          * @return {Roo.Element} this
7473          */
7474         blur : function() {
7475             try{
7476                 this.dom.blur();
7477             }catch(e){}
7478             return this;
7479         },
7480
7481         /**
7482          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7483          * @param {String/Array} className The CSS class to add, or an array of classes
7484          * @return {Roo.Element} this
7485          */
7486         addClass : function(className){
7487             if(className instanceof Array){
7488                 for(var i = 0, len = className.length; i < len; i++) {
7489                     this.addClass(className[i]);
7490                 }
7491             }else{
7492                 if(className && !this.hasClass(className)){
7493                     this.dom.className = this.dom.className + " " + className;
7494                 }
7495             }
7496             return this;
7497         },
7498
7499         /**
7500          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7501          * @param {String/Array} className The CSS class to add, or an array of classes
7502          * @return {Roo.Element} this
7503          */
7504         radioClass : function(className){
7505             var siblings = this.dom.parentNode.childNodes;
7506             for(var i = 0; i < siblings.length; i++) {
7507                 var s = siblings[i];
7508                 if(s.nodeType == 1){
7509                     Roo.get(s).removeClass(className);
7510                 }
7511             }
7512             this.addClass(className);
7513             return this;
7514         },
7515
7516         /**
7517          * Removes one or more CSS classes from the element.
7518          * @param {String/Array} className The CSS class to remove, or an array of classes
7519          * @return {Roo.Element} this
7520          */
7521         removeClass : function(className){
7522             if(!className || !this.dom.className){
7523                 return this;
7524             }
7525             if(className instanceof Array){
7526                 for(var i = 0, len = className.length; i < len; i++) {
7527                     this.removeClass(className[i]);
7528                 }
7529             }else{
7530                 if(this.hasClass(className)){
7531                     var re = this.classReCache[className];
7532                     if (!re) {
7533                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7534                        this.classReCache[className] = re;
7535                     }
7536                     this.dom.className =
7537                         this.dom.className.replace(re, " ");
7538                 }
7539             }
7540             return this;
7541         },
7542
7543         // private
7544         classReCache: {},
7545
7546         /**
7547          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7548          * @param {String} className The CSS class to toggle
7549          * @return {Roo.Element} this
7550          */
7551         toggleClass : function(className){
7552             if(this.hasClass(className)){
7553                 this.removeClass(className);
7554             }else{
7555                 this.addClass(className);
7556             }
7557             return this;
7558         },
7559
7560         /**
7561          * Checks if the specified CSS class exists on this element's DOM node.
7562          * @param {String} className The CSS class to check for
7563          * @return {Boolean} True if the class exists, else false
7564          */
7565         hasClass : function(className){
7566             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7567         },
7568
7569         /**
7570          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7571          * @param {String} oldClassName The CSS class to replace
7572          * @param {String} newClassName The replacement CSS class
7573          * @return {Roo.Element} this
7574          */
7575         replaceClass : function(oldClassName, newClassName){
7576             this.removeClass(oldClassName);
7577             this.addClass(newClassName);
7578             return this;
7579         },
7580
7581         /**
7582          * Returns an object with properties matching the styles requested.
7583          * For example, el.getStyles('color', 'font-size', 'width') might return
7584          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7585          * @param {String} style1 A style name
7586          * @param {String} style2 A style name
7587          * @param {String} etc.
7588          * @return {Object} The style object
7589          */
7590         getStyles : function(){
7591             var a = arguments, len = a.length, r = {};
7592             for(var i = 0; i < len; i++){
7593                 r[a[i]] = this.getStyle(a[i]);
7594             }
7595             return r;
7596         },
7597
7598         /**
7599          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7600          * @param {String} property The style property whose value is returned.
7601          * @return {String} The current value of the style property for this element.
7602          */
7603         getStyle : function(){
7604             return view && view.getComputedStyle ?
7605                 function(prop){
7606                     var el = this.dom, v, cs, camel;
7607                     if(prop == 'float'){
7608                         prop = "cssFloat";
7609                     }
7610                     if(el.style && (v = el.style[prop])){
7611                         return v;
7612                     }
7613                     if(cs = view.getComputedStyle(el, "")){
7614                         if(!(camel = propCache[prop])){
7615                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7616                         }
7617                         return cs[camel];
7618                     }
7619                     return null;
7620                 } :
7621                 function(prop){
7622                     var el = this.dom, v, cs, camel;
7623                     if(prop == 'opacity'){
7624                         if(typeof el.style.filter == 'string'){
7625                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7626                             if(m){
7627                                 var fv = parseFloat(m[1]);
7628                                 if(!isNaN(fv)){
7629                                     return fv ? fv / 100 : 0;
7630                                 }
7631                             }
7632                         }
7633                         return 1;
7634                     }else if(prop == 'float'){
7635                         prop = "styleFloat";
7636                     }
7637                     if(!(camel = propCache[prop])){
7638                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7639                     }
7640                     if(v = el.style[camel]){
7641                         return v;
7642                     }
7643                     if(cs = el.currentStyle){
7644                         return cs[camel];
7645                     }
7646                     return null;
7647                 };
7648         }(),
7649
7650         /**
7651          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7652          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7653          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7654          * @return {Roo.Element} this
7655          */
7656         setStyle : function(prop, value){
7657             if(typeof prop == "string"){
7658                 
7659                 if (prop == 'float') {
7660                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7661                     return this;
7662                 }
7663                 
7664                 var camel;
7665                 if(!(camel = propCache[prop])){
7666                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7667                 }
7668                 
7669                 if(camel == 'opacity') {
7670                     this.setOpacity(value);
7671                 }else{
7672                     this.dom.style[camel] = value;
7673                 }
7674             }else{
7675                 for(var style in prop){
7676                     if(typeof prop[style] != "function"){
7677                        this.setStyle(style, prop[style]);
7678                     }
7679                 }
7680             }
7681             return this;
7682         },
7683
7684         /**
7685          * More flexible version of {@link #setStyle} for setting style properties.
7686          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7687          * a function which returns such a specification.
7688          * @return {Roo.Element} this
7689          */
7690         applyStyles : function(style){
7691             Roo.DomHelper.applyStyles(this.dom, style);
7692             return this;
7693         },
7694
7695         /**
7696           * 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).
7697           * @return {Number} The X position of the element
7698           */
7699         getX : function(){
7700             return D.getX(this.dom);
7701         },
7702
7703         /**
7704           * 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).
7705           * @return {Number} The Y position of the element
7706           */
7707         getY : function(){
7708             return D.getY(this.dom);
7709         },
7710
7711         /**
7712           * 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).
7713           * @return {Array} The XY position of the element
7714           */
7715         getXY : function(){
7716             return D.getXY(this.dom);
7717         },
7718
7719         /**
7720          * 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).
7721          * @param {Number} The X position of the element
7722          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7723          * @return {Roo.Element} this
7724          */
7725         setX : function(x, animate){
7726             if(!animate || !A){
7727                 D.setX(this.dom, x);
7728             }else{
7729                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7730             }
7731             return this;
7732         },
7733
7734         /**
7735          * 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).
7736          * @param {Number} The Y position of the element
7737          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7738          * @return {Roo.Element} this
7739          */
7740         setY : function(y, animate){
7741             if(!animate || !A){
7742                 D.setY(this.dom, y);
7743             }else{
7744                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7745             }
7746             return this;
7747         },
7748
7749         /**
7750          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7751          * @param {String} left The left CSS property value
7752          * @return {Roo.Element} this
7753          */
7754         setLeft : function(left){
7755             this.setStyle("left", this.addUnits(left));
7756             return this;
7757         },
7758
7759         /**
7760          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7761          * @param {String} top The top CSS property value
7762          * @return {Roo.Element} this
7763          */
7764         setTop : function(top){
7765             this.setStyle("top", this.addUnits(top));
7766             return this;
7767         },
7768
7769         /**
7770          * Sets the element's CSS right style.
7771          * @param {String} right The right CSS property value
7772          * @return {Roo.Element} this
7773          */
7774         setRight : function(right){
7775             this.setStyle("right", this.addUnits(right));
7776             return this;
7777         },
7778
7779         /**
7780          * Sets the element's CSS bottom style.
7781          * @param {String} bottom The bottom CSS property value
7782          * @return {Roo.Element} this
7783          */
7784         setBottom : function(bottom){
7785             this.setStyle("bottom", this.addUnits(bottom));
7786             return this;
7787         },
7788
7789         /**
7790          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7791          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7792          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7793          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7794          * @return {Roo.Element} this
7795          */
7796         setXY : function(pos, animate){
7797             if(!animate || !A){
7798                 D.setXY(this.dom, pos);
7799             }else{
7800                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7801             }
7802             return this;
7803         },
7804
7805         /**
7806          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7807          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7808          * @param {Number} x X value for new position (coordinates are page-based)
7809          * @param {Number} y Y value for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         setLocation : function(x, y, animate){
7814             this.setXY([x, y], this.preanim(arguments, 2));
7815             return this;
7816         },
7817
7818         /**
7819          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7820          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7821          * @param {Number} x X value for new position (coordinates are page-based)
7822          * @param {Number} y Y value for new position (coordinates are page-based)
7823          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7824          * @return {Roo.Element} this
7825          */
7826         moveTo : function(x, y, animate){
7827             this.setXY([x, y], this.preanim(arguments, 2));
7828             return this;
7829         },
7830
7831         /**
7832          * Returns the region of the given element.
7833          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7834          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7835          */
7836         getRegion : function(){
7837             return D.getRegion(this.dom);
7838         },
7839
7840         /**
7841          * Returns the offset height of the element
7842          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7843          * @return {Number} The element's height
7844          */
7845         getHeight : function(contentHeight){
7846             var h = this.dom.offsetHeight || 0;
7847             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7848         },
7849
7850         /**
7851          * Returns the offset width of the element
7852          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7853          * @return {Number} The element's width
7854          */
7855         getWidth : function(contentWidth){
7856             var w = this.dom.offsetWidth || 0;
7857             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7858         },
7859
7860         /**
7861          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7862          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7863          * if a height has not been set using CSS.
7864          * @return {Number}
7865          */
7866         getComputedHeight : function(){
7867             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7868             if(!h){
7869                 h = parseInt(this.getStyle('height'), 10) || 0;
7870                 if(!this.isBorderBox()){
7871                     h += this.getFrameWidth('tb');
7872                 }
7873             }
7874             return h;
7875         },
7876
7877         /**
7878          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7879          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7880          * if a width has not been set using CSS.
7881          * @return {Number}
7882          */
7883         getComputedWidth : function(){
7884             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7885             if(!w){
7886                 w = parseInt(this.getStyle('width'), 10) || 0;
7887                 if(!this.isBorderBox()){
7888                     w += this.getFrameWidth('lr');
7889                 }
7890             }
7891             return w;
7892         },
7893
7894         /**
7895          * Returns the size of the element.
7896          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7897          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7898          */
7899         getSize : function(contentSize){
7900             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7901         },
7902
7903         /**
7904          * Returns the width and height of the viewport.
7905          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7906          */
7907         getViewSize : function(){
7908             var d = this.dom, doc = document, aw = 0, ah = 0;
7909             if(d == doc || d == doc.body){
7910                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7911             }else{
7912                 return {
7913                     width : d.clientWidth,
7914                     height: d.clientHeight
7915                 };
7916             }
7917         },
7918
7919         /**
7920          * Returns the value of the "value" attribute
7921          * @param {Boolean} asNumber true to parse the value as a number
7922          * @return {String/Number}
7923          */
7924         getValue : function(asNumber){
7925             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7926         },
7927
7928         // private
7929         adjustWidth : function(width){
7930             if(typeof width == "number"){
7931                 if(this.autoBoxAdjust && !this.isBorderBox()){
7932                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7933                 }
7934                 if(width < 0){
7935                     width = 0;
7936                 }
7937             }
7938             return width;
7939         },
7940
7941         // private
7942         adjustHeight : function(height){
7943             if(typeof height == "number"){
7944                if(this.autoBoxAdjust && !this.isBorderBox()){
7945                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7946                }
7947                if(height < 0){
7948                    height = 0;
7949                }
7950             }
7951             return height;
7952         },
7953
7954         /**
7955          * Set the width of the element
7956          * @param {Number} width The new width
7957          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7958          * @return {Roo.Element} this
7959          */
7960         setWidth : function(width, animate){
7961             width = this.adjustWidth(width);
7962             if(!animate || !A){
7963                 this.dom.style.width = this.addUnits(width);
7964             }else{
7965                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7966             }
7967             return this;
7968         },
7969
7970         /**
7971          * Set the height of the element
7972          * @param {Number} height The new height
7973          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7974          * @return {Roo.Element} this
7975          */
7976          setHeight : function(height, animate){
7977             height = this.adjustHeight(height);
7978             if(!animate || !A){
7979                 this.dom.style.height = this.addUnits(height);
7980             }else{
7981                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7982             }
7983             return this;
7984         },
7985
7986         /**
7987          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7988          * @param {Number} width The new width
7989          * @param {Number} height The new height
7990          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7991          * @return {Roo.Element} this
7992          */
7993          setSize : function(width, height, animate){
7994             if(typeof width == "object"){ // in case of object from getSize()
7995                 height = width.height; width = width.width;
7996             }
7997             width = this.adjustWidth(width); height = this.adjustHeight(height);
7998             if(!animate || !A){
7999                 this.dom.style.width = this.addUnits(width);
8000                 this.dom.style.height = this.addUnits(height);
8001             }else{
8002                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8003             }
8004             return this;
8005         },
8006
8007         /**
8008          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8009          * @param {Number} x X value for new position (coordinates are page-based)
8010          * @param {Number} y Y value for new position (coordinates are page-based)
8011          * @param {Number} width The new width
8012          * @param {Number} height The new height
8013          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8014          * @return {Roo.Element} this
8015          */
8016         setBounds : function(x, y, width, height, animate){
8017             if(!animate || !A){
8018                 this.setSize(width, height);
8019                 this.setLocation(x, y);
8020             }else{
8021                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8022                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8023                               this.preanim(arguments, 4), 'motion');
8024             }
8025             return this;
8026         },
8027
8028         /**
8029          * 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.
8030          * @param {Roo.lib.Region} region The region to fill
8031          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8032          * @return {Roo.Element} this
8033          */
8034         setRegion : function(region, animate){
8035             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8036             return this;
8037         },
8038
8039         /**
8040          * Appends an event handler
8041          *
8042          * @param {String}   eventName     The type of event to append
8043          * @param {Function} fn        The method the event invokes
8044          * @param {Object} scope       (optional) The scope (this object) of the fn
8045          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8046          */
8047         addListener : function(eventName, fn, scope, options){
8048             if (this.dom) {
8049                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8050             }
8051         },
8052
8053         /**
8054          * Removes an event handler from this element
8055          * @param {String} eventName the type of event to remove
8056          * @param {Function} fn the method the event invokes
8057          * @return {Roo.Element} this
8058          */
8059         removeListener : function(eventName, fn){
8060             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8061             return this;
8062         },
8063
8064         /**
8065          * Removes all previous added listeners from this element
8066          * @return {Roo.Element} this
8067          */
8068         removeAllListeners : function(){
8069             E.purgeElement(this.dom);
8070             return this;
8071         },
8072
8073         relayEvent : function(eventName, observable){
8074             this.on(eventName, function(e){
8075                 observable.fireEvent(eventName, e);
8076             });
8077         },
8078
8079         /**
8080          * Set the opacity of the element
8081          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8082          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8083          * @return {Roo.Element} this
8084          */
8085          setOpacity : function(opacity, animate){
8086             if(!animate || !A){
8087                 var s = this.dom.style;
8088                 if(Roo.isIE){
8089                     s.zoom = 1;
8090                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8091                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8092                 }else{
8093                     s.opacity = opacity;
8094                 }
8095             }else{
8096                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8097             }
8098             return this;
8099         },
8100
8101         /**
8102          * Gets the left X coordinate
8103          * @param {Boolean} local True to get the local css position instead of page coordinate
8104          * @return {Number}
8105          */
8106         getLeft : function(local){
8107             if(!local){
8108                 return this.getX();
8109             }else{
8110                 return parseInt(this.getStyle("left"), 10) || 0;
8111             }
8112         },
8113
8114         /**
8115          * Gets the right X coordinate of the element (element X position + element width)
8116          * @param {Boolean} local True to get the local css position instead of page coordinate
8117          * @return {Number}
8118          */
8119         getRight : function(local){
8120             if(!local){
8121                 return this.getX() + this.getWidth();
8122             }else{
8123                 return (this.getLeft(true) + this.getWidth()) || 0;
8124             }
8125         },
8126
8127         /**
8128          * Gets the top Y coordinate
8129          * @param {Boolean} local True to get the local css position instead of page coordinate
8130          * @return {Number}
8131          */
8132         getTop : function(local) {
8133             if(!local){
8134                 return this.getY();
8135             }else{
8136                 return parseInt(this.getStyle("top"), 10) || 0;
8137             }
8138         },
8139
8140         /**
8141          * Gets the bottom Y coordinate of the element (element Y position + element height)
8142          * @param {Boolean} local True to get the local css position instead of page coordinate
8143          * @return {Number}
8144          */
8145         getBottom : function(local){
8146             if(!local){
8147                 return this.getY() + this.getHeight();
8148             }else{
8149                 return (this.getTop(true) + this.getHeight()) || 0;
8150             }
8151         },
8152
8153         /**
8154         * Initializes positioning on this element. If a desired position is not passed, it will make the
8155         * the element positioned relative IF it is not already positioned.
8156         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8157         * @param {Number} zIndex (optional) The zIndex to apply
8158         * @param {Number} x (optional) Set the page X position
8159         * @param {Number} y (optional) Set the page Y position
8160         */
8161         position : function(pos, zIndex, x, y){
8162             if(!pos){
8163                if(this.getStyle('position') == 'static'){
8164                    this.setStyle('position', 'relative');
8165                }
8166             }else{
8167                 this.setStyle("position", pos);
8168             }
8169             if(zIndex){
8170                 this.setStyle("z-index", zIndex);
8171             }
8172             if(x !== undefined && y !== undefined){
8173                 this.setXY([x, y]);
8174             }else if(x !== undefined){
8175                 this.setX(x);
8176             }else if(y !== undefined){
8177                 this.setY(y);
8178             }
8179         },
8180
8181         /**
8182         * Clear positioning back to the default when the document was loaded
8183         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8184         * @return {Roo.Element} this
8185          */
8186         clearPositioning : function(value){
8187             value = value ||'';
8188             this.setStyle({
8189                 "left": value,
8190                 "right": value,
8191                 "top": value,
8192                 "bottom": value,
8193                 "z-index": "",
8194                 "position" : "static"
8195             });
8196             return this;
8197         },
8198
8199         /**
8200         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8201         * snapshot before performing an update and then restoring the element.
8202         * @return {Object}
8203         */
8204         getPositioning : function(){
8205             var l = this.getStyle("left");
8206             var t = this.getStyle("top");
8207             return {
8208                 "position" : this.getStyle("position"),
8209                 "left" : l,
8210                 "right" : l ? "" : this.getStyle("right"),
8211                 "top" : t,
8212                 "bottom" : t ? "" : this.getStyle("bottom"),
8213                 "z-index" : this.getStyle("z-index")
8214             };
8215         },
8216
8217         /**
8218          * Gets the width of the border(s) for the specified side(s)
8219          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8220          * passing lr would get the border (l)eft width + the border (r)ight width.
8221          * @return {Number} The width of the sides passed added together
8222          */
8223         getBorderWidth : function(side){
8224             return this.addStyles(side, El.borders);
8225         },
8226
8227         /**
8228          * Gets the width of the padding(s) for the specified side(s)
8229          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8230          * passing lr would get the padding (l)eft + the padding (r)ight.
8231          * @return {Number} The padding of the sides passed added together
8232          */
8233         getPadding : function(side){
8234             return this.addStyles(side, El.paddings);
8235         },
8236
8237         /**
8238         * Set positioning with an object returned by getPositioning().
8239         * @param {Object} posCfg
8240         * @return {Roo.Element} this
8241          */
8242         setPositioning : function(pc){
8243             this.applyStyles(pc);
8244             if(pc.right == "auto"){
8245                 this.dom.style.right = "";
8246             }
8247             if(pc.bottom == "auto"){
8248                 this.dom.style.bottom = "";
8249             }
8250             return this;
8251         },
8252
8253         // private
8254         fixDisplay : function(){
8255             if(this.getStyle("display") == "none"){
8256                 this.setStyle("visibility", "hidden");
8257                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8258                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8259                     this.setStyle("display", "block");
8260                 }
8261             }
8262         },
8263
8264         /**
8265          * Quick set left and top adding default units
8266          * @param {String} left The left CSS property value
8267          * @param {String} top The top CSS property value
8268          * @return {Roo.Element} this
8269          */
8270          setLeftTop : function(left, top){
8271             this.dom.style.left = this.addUnits(left);
8272             this.dom.style.top = this.addUnits(top);
8273             return this;
8274         },
8275
8276         /**
8277          * Move this element relative to its current position.
8278          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8279          * @param {Number} distance How far to move the element in pixels
8280          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8281          * @return {Roo.Element} this
8282          */
8283          move : function(direction, distance, animate){
8284             var xy = this.getXY();
8285             direction = direction.toLowerCase();
8286             switch(direction){
8287                 case "l":
8288                 case "left":
8289                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8290                     break;
8291                case "r":
8292                case "right":
8293                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8294                     break;
8295                case "t":
8296                case "top":
8297                case "up":
8298                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8299                     break;
8300                case "b":
8301                case "bottom":
8302                case "down":
8303                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8304                     break;
8305             }
8306             return this;
8307         },
8308
8309         /**
8310          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8311          * @return {Roo.Element} this
8312          */
8313         clip : function(){
8314             if(!this.isClipped){
8315                this.isClipped = true;
8316                this.originalClip = {
8317                    "o": this.getStyle("overflow"),
8318                    "x": this.getStyle("overflow-x"),
8319                    "y": this.getStyle("overflow-y")
8320                };
8321                this.setStyle("overflow", "hidden");
8322                this.setStyle("overflow-x", "hidden");
8323                this.setStyle("overflow-y", "hidden");
8324             }
8325             return this;
8326         },
8327
8328         /**
8329          *  Return clipping (overflow) to original clipping before clip() was called
8330          * @return {Roo.Element} this
8331          */
8332         unclip : function(){
8333             if(this.isClipped){
8334                 this.isClipped = false;
8335                 var o = this.originalClip;
8336                 if(o.o){this.setStyle("overflow", o.o);}
8337                 if(o.x){this.setStyle("overflow-x", o.x);}
8338                 if(o.y){this.setStyle("overflow-y", o.y);}
8339             }
8340             return this;
8341         },
8342
8343
8344         /**
8345          * Gets the x,y coordinates specified by the anchor position on the element.
8346          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8347          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8348          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8349          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8350          * @return {Array} [x, y] An array containing the element's x and y coordinates
8351          */
8352         getAnchorXY : function(anchor, local, s){
8353             //Passing a different size is useful for pre-calculating anchors,
8354             //especially for anchored animations that change the el size.
8355
8356             var w, h, vp = false;
8357             if(!s){
8358                 var d = this.dom;
8359                 if(d == document.body || d == document){
8360                     vp = true;
8361                     w = D.getViewWidth(); h = D.getViewHeight();
8362                 }else{
8363                     w = this.getWidth(); h = this.getHeight();
8364                 }
8365             }else{
8366                 w = s.width;  h = s.height;
8367             }
8368             var x = 0, y = 0, r = Math.round;
8369             switch((anchor || "tl").toLowerCase()){
8370                 case "c":
8371                     x = r(w*.5);
8372                     y = r(h*.5);
8373                 break;
8374                 case "t":
8375                     x = r(w*.5);
8376                     y = 0;
8377                 break;
8378                 case "l":
8379                     x = 0;
8380                     y = r(h*.5);
8381                 break;
8382                 case "r":
8383                     x = w;
8384                     y = r(h*.5);
8385                 break;
8386                 case "b":
8387                     x = r(w*.5);
8388                     y = h;
8389                 break;
8390                 case "tl":
8391                     x = 0;
8392                     y = 0;
8393                 break;
8394                 case "bl":
8395                     x = 0;
8396                     y = h;
8397                 break;
8398                 case "br":
8399                     x = w;
8400                     y = h;
8401                 break;
8402                 case "tr":
8403                     x = w;
8404                     y = 0;
8405                 break;
8406             }
8407             if(local === true){
8408                 return [x, y];
8409             }
8410             if(vp){
8411                 var sc = this.getScroll();
8412                 return [x + sc.left, y + sc.top];
8413             }
8414             //Add the element's offset xy
8415             var o = this.getXY();
8416             return [x+o[0], y+o[1]];
8417         },
8418
8419         /**
8420          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8421          * supported position values.
8422          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8423          * @param {String} position The position to align to.
8424          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8425          * @return {Array} [x, y]
8426          */
8427         getAlignToXY : function(el, p, o){
8428             el = Roo.get(el);
8429             var d = this.dom;
8430             if(!el.dom){
8431                 throw "Element.alignTo with an element that doesn't exist";
8432             }
8433             var c = false; //constrain to viewport
8434             var p1 = "", p2 = "";
8435             o = o || [0,0];
8436
8437             if(!p){
8438                 p = "tl-bl";
8439             }else if(p == "?"){
8440                 p = "tl-bl?";
8441             }else if(p.indexOf("-") == -1){
8442                 p = "tl-" + p;
8443             }
8444             p = p.toLowerCase();
8445             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8446             if(!m){
8447                throw "Element.alignTo with an invalid alignment " + p;
8448             }
8449             p1 = m[1]; p2 = m[2]; c = !!m[3];
8450
8451             //Subtract the aligned el's internal xy from the target's offset xy
8452             //plus custom offset to get the aligned el's new offset xy
8453             var a1 = this.getAnchorXY(p1, true);
8454             var a2 = el.getAnchorXY(p2, false);
8455             var x = a2[0] - a1[0] + o[0];
8456             var y = a2[1] - a1[1] + o[1];
8457             if(c){
8458                 //constrain the aligned el to viewport if necessary
8459                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8460                 // 5px of margin for ie
8461                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8462
8463                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8464                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8465                 //otherwise swap the aligned el to the opposite border of the target.
8466                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8467                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8468                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8469                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8470
8471                var doc = document;
8472                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8473                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8474
8475                if((x+w) > dw + scrollX){
8476                     x = swapX ? r.left-w : dw+scrollX-w;
8477                 }
8478                if(x < scrollX){
8479                    x = swapX ? r.right : scrollX;
8480                }
8481                if((y+h) > dh + scrollY){
8482                     y = swapY ? r.top-h : dh+scrollY-h;
8483                 }
8484                if (y < scrollY){
8485                    y = swapY ? r.bottom : scrollY;
8486                }
8487             }
8488             return [x,y];
8489         },
8490
8491         // private
8492         getConstrainToXY : function(){
8493             var os = {top:0, left:0, bottom:0, right: 0};
8494
8495             return function(el, local, offsets, proposedXY){
8496                 el = Roo.get(el);
8497                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8498
8499                 var vw, vh, vx = 0, vy = 0;
8500                 if(el.dom == document.body || el.dom == document){
8501                     vw = Roo.lib.Dom.getViewWidth();
8502                     vh = Roo.lib.Dom.getViewHeight();
8503                 }else{
8504                     vw = el.dom.clientWidth;
8505                     vh = el.dom.clientHeight;
8506                     if(!local){
8507                         var vxy = el.getXY();
8508                         vx = vxy[0];
8509                         vy = vxy[1];
8510                     }
8511                 }
8512
8513                 var s = el.getScroll();
8514
8515                 vx += offsets.left + s.left;
8516                 vy += offsets.top + s.top;
8517
8518                 vw -= offsets.right;
8519                 vh -= offsets.bottom;
8520
8521                 var vr = vx+vw;
8522                 var vb = vy+vh;
8523
8524                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8525                 var x = xy[0], y = xy[1];
8526                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8527
8528                 // only move it if it needs it
8529                 var moved = false;
8530
8531                 // first validate right/bottom
8532                 if((x + w) > vr){
8533                     x = vr - w;
8534                     moved = true;
8535                 }
8536                 if((y + h) > vb){
8537                     y = vb - h;
8538                     moved = true;
8539                 }
8540                 // then make sure top/left isn't negative
8541                 if(x < vx){
8542                     x = vx;
8543                     moved = true;
8544                 }
8545                 if(y < vy){
8546                     y = vy;
8547                     moved = true;
8548                 }
8549                 return moved ? [x, y] : false;
8550             };
8551         }(),
8552
8553         // private
8554         adjustForConstraints : function(xy, parent, offsets){
8555             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8556         },
8557
8558         /**
8559          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8560          * document it aligns it to the viewport.
8561          * The position parameter is optional, and can be specified in any one of the following formats:
8562          * <ul>
8563          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8564          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8565          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8566          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8567          *   <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
8568          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8569          * </ul>
8570          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8571          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8572          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8573          * that specified in order to enforce the viewport constraints.
8574          * Following are all of the supported anchor positions:
8575     <pre>
8576     Value  Description
8577     -----  -----------------------------
8578     tl     The top left corner (default)
8579     t      The center of the top edge
8580     tr     The top right corner
8581     l      The center of the left edge
8582     c      In the center of the element
8583     r      The center of the right edge
8584     bl     The bottom left corner
8585     b      The center of the bottom edge
8586     br     The bottom right corner
8587     </pre>
8588     Example Usage:
8589     <pre><code>
8590     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8591     el.alignTo("other-el");
8592
8593     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8594     el.alignTo("other-el", "tr?");
8595
8596     // align the bottom right corner of el with the center left edge of other-el
8597     el.alignTo("other-el", "br-l?");
8598
8599     // align the center of el with the bottom left corner of other-el and
8600     // adjust the x position by -6 pixels (and the y position by 0)
8601     el.alignTo("other-el", "c-bl", [-6, 0]);
8602     </code></pre>
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          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         alignTo : function(element, position, offsets, animate){
8610             var xy = this.getAlignToXY(element, position, offsets);
8611             this.setXY(xy, this.preanim(arguments, 3));
8612             return this;
8613         },
8614
8615         /**
8616          * Anchors an element to another element and realigns it when the window is resized.
8617          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8618          * @param {String} position The position to align to.
8619          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8620          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8621          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8622          * is a number, it is used as the buffer delay (defaults to 50ms).
8623          * @param {Function} callback The function to call after the animation finishes
8624          * @return {Roo.Element} this
8625          */
8626         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8627             var action = function(){
8628                 this.alignTo(el, alignment, offsets, animate);
8629                 Roo.callback(callback, this);
8630             };
8631             Roo.EventManager.onWindowResize(action, this);
8632             var tm = typeof monitorScroll;
8633             if(tm != 'undefined'){
8634                 Roo.EventManager.on(window, 'scroll', action, this,
8635                     {buffer: tm == 'number' ? monitorScroll : 50});
8636             }
8637             action.call(this); // align immediately
8638             return this;
8639         },
8640         /**
8641          * Clears any opacity settings from this element. Required in some cases for IE.
8642          * @return {Roo.Element} this
8643          */
8644         clearOpacity : function(){
8645             if (window.ActiveXObject) {
8646                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8647                     this.dom.style.filter = "";
8648                 }
8649             } else {
8650                 this.dom.style.opacity = "";
8651                 this.dom.style["-moz-opacity"] = "";
8652                 this.dom.style["-khtml-opacity"] = "";
8653             }
8654             return this;
8655         },
8656
8657         /**
8658          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8659          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8660          * @return {Roo.Element} this
8661          */
8662         hide : function(animate){
8663             this.setVisible(false, this.preanim(arguments, 0));
8664             return this;
8665         },
8666
8667         /**
8668         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8669         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8670          * @return {Roo.Element} this
8671          */
8672         show : function(animate){
8673             this.setVisible(true, this.preanim(arguments, 0));
8674             return this;
8675         },
8676
8677         /**
8678          * @private Test if size has a unit, otherwise appends the default
8679          */
8680         addUnits : function(size){
8681             return Roo.Element.addUnits(size, this.defaultUnit);
8682         },
8683
8684         /**
8685          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8686          * @return {Roo.Element} this
8687          */
8688         beginMeasure : function(){
8689             var el = this.dom;
8690             if(el.offsetWidth || el.offsetHeight){
8691                 return this; // offsets work already
8692             }
8693             var changed = [];
8694             var p = this.dom, b = document.body; // start with this element
8695             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8696                 var pe = Roo.get(p);
8697                 if(pe.getStyle('display') == 'none'){
8698                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8699                     p.style.visibility = "hidden";
8700                     p.style.display = "block";
8701                 }
8702                 p = p.parentNode;
8703             }
8704             this._measureChanged = changed;
8705             return this;
8706
8707         },
8708
8709         /**
8710          * Restores displays to before beginMeasure was called
8711          * @return {Roo.Element} this
8712          */
8713         endMeasure : function(){
8714             var changed = this._measureChanged;
8715             if(changed){
8716                 for(var i = 0, len = changed.length; i < len; i++) {
8717                     var r = changed[i];
8718                     r.el.style.visibility = r.visibility;
8719                     r.el.style.display = "none";
8720                 }
8721                 this._measureChanged = null;
8722             }
8723             return this;
8724         },
8725
8726         /**
8727         * Update the innerHTML of this element, optionally searching for and processing scripts
8728         * @param {String} html The new HTML
8729         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8730         * @param {Function} callback For async script loading you can be noticed when the update completes
8731         * @return {Roo.Element} this
8732          */
8733         update : function(html, loadScripts, callback){
8734             if(typeof html == "undefined"){
8735                 html = "";
8736             }
8737             if(loadScripts !== true){
8738                 this.dom.innerHTML = html;
8739                 if(typeof callback == "function"){
8740                     callback();
8741                 }
8742                 return this;
8743             }
8744             var id = Roo.id();
8745             var dom = this.dom;
8746
8747             html += '<span id="' + id + '"></span>';
8748
8749             E.onAvailable(id, function(){
8750                 var hd = document.getElementsByTagName("head")[0];
8751                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8752                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8753                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8754
8755                 var match;
8756                 while(match = re.exec(html)){
8757                     var attrs = match[1];
8758                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8759                     if(srcMatch && srcMatch[2]){
8760                        var s = document.createElement("script");
8761                        s.src = srcMatch[2];
8762                        var typeMatch = attrs.match(typeRe);
8763                        if(typeMatch && typeMatch[2]){
8764                            s.type = typeMatch[2];
8765                        }
8766                        hd.appendChild(s);
8767                     }else if(match[2] && match[2].length > 0){
8768                         if(window.execScript) {
8769                            window.execScript(match[2]);
8770                         } else {
8771                             /**
8772                              * eval:var:id
8773                              * eval:var:dom
8774                              * eval:var:html
8775                              * 
8776                              */
8777                            window.eval(match[2]);
8778                         }
8779                     }
8780                 }
8781                 var el = document.getElementById(id);
8782                 if(el){el.parentNode.removeChild(el);}
8783                 if(typeof callback == "function"){
8784                     callback();
8785                 }
8786             });
8787             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8788             return this;
8789         },
8790
8791         /**
8792          * Direct access to the UpdateManager update() method (takes the same parameters).
8793          * @param {String/Function} url The url for this request or a function to call to get the url
8794          * @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}
8795          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8796          * @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.
8797          * @return {Roo.Element} this
8798          */
8799         load : function(){
8800             var um = this.getUpdateManager();
8801             um.update.apply(um, arguments);
8802             return this;
8803         },
8804
8805         /**
8806         * Gets this element's UpdateManager
8807         * @return {Roo.UpdateManager} The UpdateManager
8808         */
8809         getUpdateManager : function(){
8810             if(!this.updateManager){
8811                 this.updateManager = new Roo.UpdateManager(this);
8812             }
8813             return this.updateManager;
8814         },
8815
8816         /**
8817          * Disables text selection for this element (normalized across browsers)
8818          * @return {Roo.Element} this
8819          */
8820         unselectable : function(){
8821             this.dom.unselectable = "on";
8822             this.swallowEvent("selectstart", true);
8823             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8824             this.addClass("x-unselectable");
8825             return this;
8826         },
8827
8828         /**
8829         * Calculates the x, y to center this element on the screen
8830         * @return {Array} The x, y values [x, y]
8831         */
8832         getCenterXY : function(){
8833             return this.getAlignToXY(document, 'c-c');
8834         },
8835
8836         /**
8837         * Centers the Element in either the viewport, or another Element.
8838         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8839         */
8840         center : function(centerIn){
8841             this.alignTo(centerIn || document, 'c-c');
8842             return this;
8843         },
8844
8845         /**
8846          * Tests various css rules/browsers to determine if this element uses a border box
8847          * @return {Boolean}
8848          */
8849         isBorderBox : function(){
8850             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8851         },
8852
8853         /**
8854          * Return a box {x, y, width, height} that can be used to set another elements
8855          * size/location to match this element.
8856          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8857          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8858          * @return {Object} box An object in the format {x, y, width, height}
8859          */
8860         getBox : function(contentBox, local){
8861             var xy;
8862             if(!local){
8863                 xy = this.getXY();
8864             }else{
8865                 var left = parseInt(this.getStyle("left"), 10) || 0;
8866                 var top = parseInt(this.getStyle("top"), 10) || 0;
8867                 xy = [left, top];
8868             }
8869             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8870             if(!contentBox){
8871                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8872             }else{
8873                 var l = this.getBorderWidth("l")+this.getPadding("l");
8874                 var r = this.getBorderWidth("r")+this.getPadding("r");
8875                 var t = this.getBorderWidth("t")+this.getPadding("t");
8876                 var b = this.getBorderWidth("b")+this.getPadding("b");
8877                 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)};
8878             }
8879             bx.right = bx.x + bx.width;
8880             bx.bottom = bx.y + bx.height;
8881             return bx;
8882         },
8883
8884         /**
8885          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8886          for more information about the sides.
8887          * @param {String} sides
8888          * @return {Number}
8889          */
8890         getFrameWidth : function(sides, onlyContentBox){
8891             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8892         },
8893
8894         /**
8895          * 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.
8896          * @param {Object} box The box to fill {x, y, width, height}
8897          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8898          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8899          * @return {Roo.Element} this
8900          */
8901         setBox : function(box, adjust, animate){
8902             var w = box.width, h = box.height;
8903             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8904                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8905                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8906             }
8907             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8908             return this;
8909         },
8910
8911         /**
8912          * Forces the browser to repaint this element
8913          * @return {Roo.Element} this
8914          */
8915          repaint : function(){
8916             var dom = this.dom;
8917             this.addClass("x-repaint");
8918             setTimeout(function(){
8919                 Roo.get(dom).removeClass("x-repaint");
8920             }, 1);
8921             return this;
8922         },
8923
8924         /**
8925          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8926          * then it returns the calculated width of the sides (see getPadding)
8927          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8928          * @return {Object/Number}
8929          */
8930         getMargins : function(side){
8931             if(!side){
8932                 return {
8933                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8934                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8935                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8936                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8937                 };
8938             }else{
8939                 return this.addStyles(side, El.margins);
8940              }
8941         },
8942
8943         // private
8944         addStyles : function(sides, styles){
8945             var val = 0, v, w;
8946             for(var i = 0, len = sides.length; i < len; i++){
8947                 v = this.getStyle(styles[sides.charAt(i)]);
8948                 if(v){
8949                      w = parseInt(v, 10);
8950                      if(w){ val += w; }
8951                 }
8952             }
8953             return val;
8954         },
8955
8956         /**
8957          * Creates a proxy element of this element
8958          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8959          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8960          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8961          * @return {Roo.Element} The new proxy element
8962          */
8963         createProxy : function(config, renderTo, matchBox){
8964             if(renderTo){
8965                 renderTo = Roo.getDom(renderTo);
8966             }else{
8967                 renderTo = document.body;
8968             }
8969             config = typeof config == "object" ?
8970                 config : {tag : "div", cls: config};
8971             var proxy = Roo.DomHelper.append(renderTo, config, true);
8972             if(matchBox){
8973                proxy.setBox(this.getBox());
8974             }
8975             return proxy;
8976         },
8977
8978         /**
8979          * Puts a mask over this element to disable user interaction. Requires core.css.
8980          * This method can only be applied to elements which accept child nodes.
8981          * @param {String} msg (optional) A message to display in the mask
8982          * @param {String} msgCls (optional) A css class to apply to the msg element
8983          * @return {Element} The mask  element
8984          */
8985         mask : function(msg, msgCls)
8986         {
8987             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8988                 this.setStyle("position", "relative");
8989             }
8990             if(!this._mask){
8991                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8992             }
8993             this.addClass("x-masked");
8994             this._mask.setDisplayed(true);
8995             
8996             // we wander
8997             var z = 0;
8998             var dom = this.dom
8999             while (dom && dom.style) {
9000                 if (!isNaN(parseInt(dom.style.zIndex))) {
9001                     z = Math.max(z, parseInt(dom.style.zIndex));
9002                 }
9003                 dom = dom.parentNode;
9004             }
9005             // if we are masking the body - then it hides everything..
9006             if (this.dom == document.body) {
9007                 z = 1000000;
9008                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9009                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9010             }
9011            
9012             if(typeof msg == 'string'){
9013                 if(!this._maskMsg){
9014                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9015                 }
9016                 var mm = this._maskMsg;
9017                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9018                 if (mm.dom.firstChild) { // weird IE issue?
9019                     mm.dom.firstChild.innerHTML = msg;
9020                 }
9021                 mm.setDisplayed(true);
9022                 mm.center(this);
9023                 mm.setStyle('z-index', z + 102);
9024             }
9025             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9026                 this._mask.setHeight(this.getHeight());
9027             }
9028             this._mask.setStyle('z-index', z + 100);
9029             
9030             return this._mask;
9031         },
9032
9033         /**
9034          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9035          * it is cached for reuse.
9036          */
9037         unmask : function(removeEl){
9038             if(this._mask){
9039                 if(removeEl === true){
9040                     this._mask.remove();
9041                     delete this._mask;
9042                     if(this._maskMsg){
9043                         this._maskMsg.remove();
9044                         delete this._maskMsg;
9045                     }
9046                 }else{
9047                     this._mask.setDisplayed(false);
9048                     if(this._maskMsg){
9049                         this._maskMsg.setDisplayed(false);
9050                     }
9051                 }
9052             }
9053             this.removeClass("x-masked");
9054         },
9055
9056         /**
9057          * Returns true if this element is masked
9058          * @return {Boolean}
9059          */
9060         isMasked : function(){
9061             return this._mask && this._mask.isVisible();
9062         },
9063
9064         /**
9065          * Creates an iframe shim for this element to keep selects and other windowed objects from
9066          * showing through.
9067          * @return {Roo.Element} The new shim element
9068          */
9069         createShim : function(){
9070             var el = document.createElement('iframe');
9071             el.frameBorder = 'no';
9072             el.className = 'roo-shim';
9073             if(Roo.isIE && Roo.isSecure){
9074                 el.src = Roo.SSL_SECURE_URL;
9075             }
9076             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9077             shim.autoBoxAdjust = false;
9078             return shim;
9079         },
9080
9081         /**
9082          * Removes this element from the DOM and deletes it from the cache
9083          */
9084         remove : function(){
9085             if(this.dom.parentNode){
9086                 this.dom.parentNode.removeChild(this.dom);
9087             }
9088             delete El.cache[this.dom.id];
9089         },
9090
9091         /**
9092          * Sets up event handlers to add and remove a css class when the mouse is over this element
9093          * @param {String} className
9094          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9095          * mouseout events for children elements
9096          * @return {Roo.Element} this
9097          */
9098         addClassOnOver : function(className, preventFlicker){
9099             this.on("mouseover", function(){
9100                 Roo.fly(this, '_internal').addClass(className);
9101             }, this.dom);
9102             var removeFn = function(e){
9103                 if(preventFlicker !== true || !e.within(this, true)){
9104                     Roo.fly(this, '_internal').removeClass(className);
9105                 }
9106             };
9107             this.on("mouseout", removeFn, this.dom);
9108             return this;
9109         },
9110
9111         /**
9112          * Sets up event handlers to add and remove a css class when this element has the focus
9113          * @param {String} className
9114          * @return {Roo.Element} this
9115          */
9116         addClassOnFocus : function(className){
9117             this.on("focus", function(){
9118                 Roo.fly(this, '_internal').addClass(className);
9119             }, this.dom);
9120             this.on("blur", function(){
9121                 Roo.fly(this, '_internal').removeClass(className);
9122             }, this.dom);
9123             return this;
9124         },
9125         /**
9126          * 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)
9127          * @param {String} className
9128          * @return {Roo.Element} this
9129          */
9130         addClassOnClick : function(className){
9131             var dom = this.dom;
9132             this.on("mousedown", function(){
9133                 Roo.fly(dom, '_internal').addClass(className);
9134                 var d = Roo.get(document);
9135                 var fn = function(){
9136                     Roo.fly(dom, '_internal').removeClass(className);
9137                     d.removeListener("mouseup", fn);
9138                 };
9139                 d.on("mouseup", fn);
9140             });
9141             return this;
9142         },
9143
9144         /**
9145          * Stops the specified event from bubbling and optionally prevents the default action
9146          * @param {String} eventName
9147          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9148          * @return {Roo.Element} this
9149          */
9150         swallowEvent : function(eventName, preventDefault){
9151             var fn = function(e){
9152                 e.stopPropagation();
9153                 if(preventDefault){
9154                     e.preventDefault();
9155                 }
9156             };
9157             if(eventName instanceof Array){
9158                 for(var i = 0, len = eventName.length; i < len; i++){
9159                      this.on(eventName[i], fn);
9160                 }
9161                 return this;
9162             }
9163             this.on(eventName, fn);
9164             return this;
9165         },
9166
9167         /**
9168          * @private
9169          */
9170       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9171
9172         /**
9173          * Sizes this element to its parent element's dimensions performing
9174          * neccessary box adjustments.
9175          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9176          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9177          * @return {Roo.Element} this
9178          */
9179         fitToParent : function(monitorResize, targetParent) {
9180           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9181           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9182           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9183             return;
9184           }
9185           var p = Roo.get(targetParent || this.dom.parentNode);
9186           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9187           if (monitorResize === true) {
9188             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9189             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9190           }
9191           return this;
9192         },
9193
9194         /**
9195          * Gets the next sibling, skipping text nodes
9196          * @return {HTMLElement} The next sibling or null
9197          */
9198         getNextSibling : function(){
9199             var n = this.dom.nextSibling;
9200             while(n && n.nodeType != 1){
9201                 n = n.nextSibling;
9202             }
9203             return n;
9204         },
9205
9206         /**
9207          * Gets the previous sibling, skipping text nodes
9208          * @return {HTMLElement} The previous sibling or null
9209          */
9210         getPrevSibling : function(){
9211             var n = this.dom.previousSibling;
9212             while(n && n.nodeType != 1){
9213                 n = n.previousSibling;
9214             }
9215             return n;
9216         },
9217
9218
9219         /**
9220          * Appends the passed element(s) to this element
9221          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9222          * @return {Roo.Element} this
9223          */
9224         appendChild: function(el){
9225             el = Roo.get(el);
9226             el.appendTo(this);
9227             return this;
9228         },
9229
9230         /**
9231          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9232          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9233          * automatically generated with the specified attributes.
9234          * @param {HTMLElement} insertBefore (optional) a child element of this element
9235          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9236          * @return {Roo.Element} The new child element
9237          */
9238         createChild: function(config, insertBefore, returnDom){
9239             config = config || {tag:'div'};
9240             if(insertBefore){
9241                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9242             }
9243             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9244         },
9245
9246         /**
9247          * Appends this element to the passed element
9248          * @param {String/HTMLElement/Element} el The new parent element
9249          * @return {Roo.Element} this
9250          */
9251         appendTo: function(el){
9252             el = Roo.getDom(el);
9253             el.appendChild(this.dom);
9254             return this;
9255         },
9256
9257         /**
9258          * Inserts this element before the passed element in the DOM
9259          * @param {String/HTMLElement/Element} el The element to insert before
9260          * @return {Roo.Element} this
9261          */
9262         insertBefore: function(el){
9263             el = Roo.getDom(el);
9264             el.parentNode.insertBefore(this.dom, el);
9265             return this;
9266         },
9267
9268         /**
9269          * Inserts this element after the passed element in the DOM
9270          * @param {String/HTMLElement/Element} el The element to insert after
9271          * @return {Roo.Element} this
9272          */
9273         insertAfter: function(el){
9274             el = Roo.getDom(el);
9275             el.parentNode.insertBefore(this.dom, el.nextSibling);
9276             return this;
9277         },
9278
9279         /**
9280          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9282          * @return {Roo.Element} The new child
9283          */
9284         insertFirst: function(el, returnDom){
9285             el = el || {};
9286             if(typeof el == 'object' && !el.nodeType){ // dh config
9287                 return this.createChild(el, this.dom.firstChild, returnDom);
9288             }else{
9289                 el = Roo.getDom(el);
9290                 this.dom.insertBefore(el, this.dom.firstChild);
9291                 return !returnDom ? Roo.get(el) : el;
9292             }
9293         },
9294
9295         /**
9296          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9297          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9298          * @param {String} where (optional) 'before' or 'after' defaults to before
9299          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9300          * @return {Roo.Element} the inserted Element
9301          */
9302         insertSibling: function(el, where, returnDom){
9303             where = where ? where.toLowerCase() : 'before';
9304             el = el || {};
9305             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9306
9307             if(typeof el == 'object' && !el.nodeType){ // dh config
9308                 if(where == 'after' && !this.dom.nextSibling){
9309                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9310                 }else{
9311                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9312                 }
9313
9314             }else{
9315                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9316                             where == 'before' ? this.dom : this.dom.nextSibling);
9317                 if(!returnDom){
9318                     rt = Roo.get(rt);
9319                 }
9320             }
9321             return rt;
9322         },
9323
9324         /**
9325          * Creates and wraps this element with another element
9326          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9327          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9328          * @return {HTMLElement/Element} The newly created wrapper element
9329          */
9330         wrap: function(config, returnDom){
9331             if(!config){
9332                 config = {tag: "div"};
9333             }
9334             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9335             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9336             return newEl;
9337         },
9338
9339         /**
9340          * Replaces the passed element with this element
9341          * @param {String/HTMLElement/Element} el The element to replace
9342          * @return {Roo.Element} this
9343          */
9344         replace: function(el){
9345             el = Roo.get(el);
9346             this.insertBefore(el);
9347             el.remove();
9348             return this;
9349         },
9350
9351         /**
9352          * Inserts an html fragment into this element
9353          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9354          * @param {String} html The HTML fragment
9355          * @param {Boolean} returnEl True to return an Roo.Element
9356          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9357          */
9358         insertHtml : function(where, html, returnEl){
9359             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9360             return returnEl ? Roo.get(el) : el;
9361         },
9362
9363         /**
9364          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9365          * @param {Object} o The object with the attributes
9366          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9367          * @return {Roo.Element} this
9368          */
9369         set : function(o, useSet){
9370             var el = this.dom;
9371             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9372             for(var attr in o){
9373                 if(attr == "style" || typeof o[attr] == "function") continue;
9374                 if(attr=="cls"){
9375                     el.className = o["cls"];
9376                 }else{
9377                     if(useSet) el.setAttribute(attr, o[attr]);
9378                     else el[attr] = o[attr];
9379                 }
9380             }
9381             if(o.style){
9382                 Roo.DomHelper.applyStyles(el, o.style);
9383             }
9384             return this;
9385         },
9386
9387         /**
9388          * Convenience method for constructing a KeyMap
9389          * @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:
9390          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9391          * @param {Function} fn The function to call
9392          * @param {Object} scope (optional) The scope of the function
9393          * @return {Roo.KeyMap} The KeyMap created
9394          */
9395         addKeyListener : function(key, fn, scope){
9396             var config;
9397             if(typeof key != "object" || key instanceof Array){
9398                 config = {
9399                     key: key,
9400                     fn: fn,
9401                     scope: scope
9402                 };
9403             }else{
9404                 config = {
9405                     key : key.key,
9406                     shift : key.shift,
9407                     ctrl : key.ctrl,
9408                     alt : key.alt,
9409                     fn: fn,
9410                     scope: scope
9411                 };
9412             }
9413             return new Roo.KeyMap(this, config);
9414         },
9415
9416         /**
9417          * Creates a KeyMap for this element
9418          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9419          * @return {Roo.KeyMap} The KeyMap created
9420          */
9421         addKeyMap : function(config){
9422             return new Roo.KeyMap(this, config);
9423         },
9424
9425         /**
9426          * Returns true if this element is scrollable.
9427          * @return {Boolean}
9428          */
9429          isScrollable : function(){
9430             var dom = this.dom;
9431             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9432         },
9433
9434         /**
9435          * 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().
9436          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9437          * @param {Number} value The new scroll value
9438          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9439          * @return {Element} this
9440          */
9441
9442         scrollTo : function(side, value, animate){
9443             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9444             if(!animate || !A){
9445                 this.dom[prop] = value;
9446             }else{
9447                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9448                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9449             }
9450             return this;
9451         },
9452
9453         /**
9454          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9455          * within this element's scrollable range.
9456          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9457          * @param {Number} distance How far to scroll the element in pixels
9458          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9459          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9460          * was scrolled as far as it could go.
9461          */
9462          scroll : function(direction, distance, animate){
9463              if(!this.isScrollable()){
9464                  return;
9465              }
9466              var el = this.dom;
9467              var l = el.scrollLeft, t = el.scrollTop;
9468              var w = el.scrollWidth, h = el.scrollHeight;
9469              var cw = el.clientWidth, ch = el.clientHeight;
9470              direction = direction.toLowerCase();
9471              var scrolled = false;
9472              var a = this.preanim(arguments, 2);
9473              switch(direction){
9474                  case "l":
9475                  case "left":
9476                      if(w - l > cw){
9477                          var v = Math.min(l + distance, w-cw);
9478                          this.scrollTo("left", v, a);
9479                          scrolled = true;
9480                      }
9481                      break;
9482                 case "r":
9483                 case "right":
9484                      if(l > 0){
9485                          var v = Math.max(l - distance, 0);
9486                          this.scrollTo("left", v, a);
9487                          scrolled = true;
9488                      }
9489                      break;
9490                 case "t":
9491                 case "top":
9492                 case "up":
9493                      if(t > 0){
9494                          var v = Math.max(t - distance, 0);
9495                          this.scrollTo("top", v, a);
9496                          scrolled = true;
9497                      }
9498                      break;
9499                 case "b":
9500                 case "bottom":
9501                 case "down":
9502                      if(h - t > ch){
9503                          var v = Math.min(t + distance, h-ch);
9504                          this.scrollTo("top", v, a);
9505                          scrolled = true;
9506                      }
9507                      break;
9508              }
9509              return scrolled;
9510         },
9511
9512         /**
9513          * Translates the passed page coordinates into left/top css values for this element
9514          * @param {Number/Array} x The page x or an array containing [x, y]
9515          * @param {Number} y The page y
9516          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9517          */
9518         translatePoints : function(x, y){
9519             if(typeof x == 'object' || x instanceof Array){
9520                 y = x[1]; x = x[0];
9521             }
9522             var p = this.getStyle('position');
9523             var o = this.getXY();
9524
9525             var l = parseInt(this.getStyle('left'), 10);
9526             var t = parseInt(this.getStyle('top'), 10);
9527
9528             if(isNaN(l)){
9529                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9530             }
9531             if(isNaN(t)){
9532                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9533             }
9534
9535             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9536         },
9537
9538         /**
9539          * Returns the current scroll position of the element.
9540          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9541          */
9542         getScroll : function(){
9543             var d = this.dom, doc = document;
9544             if(d == doc || d == doc.body){
9545                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9546                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9547                 return {left: l, top: t};
9548             }else{
9549                 return {left: d.scrollLeft, top: d.scrollTop};
9550             }
9551         },
9552
9553         /**
9554          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9555          * are convert to standard 6 digit hex color.
9556          * @param {String} attr The css attribute
9557          * @param {String} defaultValue The default value to use when a valid color isn't found
9558          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9559          * YUI color anims.
9560          */
9561         getColor : function(attr, defaultValue, prefix){
9562             var v = this.getStyle(attr);
9563             if(!v || v == "transparent" || v == "inherit") {
9564                 return defaultValue;
9565             }
9566             var color = typeof prefix == "undefined" ? "#" : prefix;
9567             if(v.substr(0, 4) == "rgb("){
9568                 var rvs = v.slice(4, v.length -1).split(",");
9569                 for(var i = 0; i < 3; i++){
9570                     var h = parseInt(rvs[i]).toString(16);
9571                     if(h < 16){
9572                         h = "0" + h;
9573                     }
9574                     color += h;
9575                 }
9576             } else {
9577                 if(v.substr(0, 1) == "#"){
9578                     if(v.length == 4) {
9579                         for(var i = 1; i < 4; i++){
9580                             var c = v.charAt(i);
9581                             color +=  c + c;
9582                         }
9583                     }else if(v.length == 7){
9584                         color += v.substr(1);
9585                     }
9586                 }
9587             }
9588             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9589         },
9590
9591         /**
9592          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9593          * gradient background, rounded corners and a 4-way shadow.
9594          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9595          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9596          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9597          * @return {Roo.Element} this
9598          */
9599         boxWrap : function(cls){
9600             cls = cls || 'x-box';
9601             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9602             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9603             return el;
9604         },
9605
9606         /**
9607          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9608          * @param {String} namespace The namespace in which to look for the attribute
9609          * @param {String} name The attribute name
9610          * @return {String} The attribute value
9611          */
9612         getAttributeNS : Roo.isIE ? function(ns, name){
9613             var d = this.dom;
9614             var type = typeof d[ns+":"+name];
9615             if(type != 'undefined' && type != 'unknown'){
9616                 return d[ns+":"+name];
9617             }
9618             return d[name];
9619         } : function(ns, name){
9620             var d = this.dom;
9621             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9622         },
9623         
9624         
9625         /**
9626          * Sets or Returns the value the dom attribute value
9627          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9628          * @param {String} value (optional) The value to set the attribute to
9629          * @return {String} The attribute value
9630          */
9631         attr : function(name){
9632             if (arguments.length > 1) {
9633                 this.dom.setAttribute(name, arguments[1]);
9634                 return arguments[1];
9635             }
9636             if (typeof(name) == 'object') {
9637                 for(var i in name) {
9638                     this.attr(i, name[i]);
9639                 }
9640                 return name;
9641             }
9642             
9643             
9644             if (!this.dom.hasAttribute(name)) {
9645                 return undefined;
9646             }
9647             return this.dom.getAttribute(name);
9648         }
9649         
9650         
9651         
9652     };
9653
9654     var ep = El.prototype;
9655
9656     /**
9657      * Appends an event handler (Shorthand for addListener)
9658      * @param {String}   eventName     The type of event to append
9659      * @param {Function} fn        The method the event invokes
9660      * @param {Object} scope       (optional) The scope (this object) of the fn
9661      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9662      * @method
9663      */
9664     ep.on = ep.addListener;
9665         // backwards compat
9666     ep.mon = ep.addListener;
9667
9668     /**
9669      * Removes an event handler from this element (shorthand for removeListener)
9670      * @param {String} eventName the type of event to remove
9671      * @param {Function} fn the method the event invokes
9672      * @return {Roo.Element} this
9673      * @method
9674      */
9675     ep.un = ep.removeListener;
9676
9677     /**
9678      * true to automatically adjust width and height settings for box-model issues (default to true)
9679      */
9680     ep.autoBoxAdjust = true;
9681
9682     // private
9683     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9684
9685     // private
9686     El.addUnits = function(v, defaultUnit){
9687         if(v === "" || v == "auto"){
9688             return v;
9689         }
9690         if(v === undefined){
9691             return '';
9692         }
9693         if(typeof v == "number" || !El.unitPattern.test(v)){
9694             return v + (defaultUnit || 'px');
9695         }
9696         return v;
9697     };
9698
9699     // special markup used throughout Roo when box wrapping elements
9700     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>';
9701     /**
9702      * Visibility mode constant - Use visibility to hide element
9703      * @static
9704      * @type Number
9705      */
9706     El.VISIBILITY = 1;
9707     /**
9708      * Visibility mode constant - Use display to hide element
9709      * @static
9710      * @type Number
9711      */
9712     El.DISPLAY = 2;
9713
9714     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9715     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9716     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9717
9718
9719
9720     /**
9721      * @private
9722      */
9723     El.cache = {};
9724
9725     var docEl;
9726
9727     /**
9728      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9729      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9730      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9731      * @return {Element} The Element object
9732      * @static
9733      */
9734     El.get = function(el){
9735         var ex, elm, id;
9736         if(!el){ return null; }
9737         if(typeof el == "string"){ // element id
9738             if(!(elm = document.getElementById(el))){
9739                 return null;
9740             }
9741             if(ex = El.cache[el]){
9742                 ex.dom = elm;
9743             }else{
9744                 ex = El.cache[el] = new El(elm);
9745             }
9746             return ex;
9747         }else if(el.tagName){ // dom element
9748             if(!(id = el.id)){
9749                 id = Roo.id(el);
9750             }
9751             if(ex = El.cache[id]){
9752                 ex.dom = el;
9753             }else{
9754                 ex = El.cache[id] = new El(el);
9755             }
9756             return ex;
9757         }else if(el instanceof El){
9758             if(el != docEl){
9759                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9760                                                               // catch case where it hasn't been appended
9761                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9762             }
9763             return el;
9764         }else if(el.isComposite){
9765             return el;
9766         }else if(el instanceof Array){
9767             return El.select(el);
9768         }else if(el == document){
9769             // create a bogus element object representing the document object
9770             if(!docEl){
9771                 var f = function(){};
9772                 f.prototype = El.prototype;
9773                 docEl = new f();
9774                 docEl.dom = document;
9775             }
9776             return docEl;
9777         }
9778         return null;
9779     };
9780
9781     // private
9782     El.uncache = function(el){
9783         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9784             if(a[i]){
9785                 delete El.cache[a[i].id || a[i]];
9786             }
9787         }
9788     };
9789
9790     // private
9791     // Garbage collection - uncache elements/purge listeners on orphaned elements
9792     // so we don't hold a reference and cause the browser to retain them
9793     El.garbageCollect = function(){
9794         if(!Roo.enableGarbageCollector){
9795             clearInterval(El.collectorThread);
9796             return;
9797         }
9798         for(var eid in El.cache){
9799             var el = El.cache[eid], d = el.dom;
9800             // -------------------------------------------------------
9801             // Determining what is garbage:
9802             // -------------------------------------------------------
9803             // !d
9804             // dom node is null, definitely garbage
9805             // -------------------------------------------------------
9806             // !d.parentNode
9807             // no parentNode == direct orphan, definitely garbage
9808             // -------------------------------------------------------
9809             // !d.offsetParent && !document.getElementById(eid)
9810             // display none elements have no offsetParent so we will
9811             // also try to look it up by it's id. However, check
9812             // offsetParent first so we don't do unneeded lookups.
9813             // This enables collection of elements that are not orphans
9814             // directly, but somewhere up the line they have an orphan
9815             // parent.
9816             // -------------------------------------------------------
9817             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9818                 delete El.cache[eid];
9819                 if(d && Roo.enableListenerCollection){
9820                     E.purgeElement(d);
9821                 }
9822             }
9823         }
9824     }
9825     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9826
9827
9828     // dom is optional
9829     El.Flyweight = function(dom){
9830         this.dom = dom;
9831     };
9832     El.Flyweight.prototype = El.prototype;
9833
9834     El._flyweights = {};
9835     /**
9836      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9837      * the dom node can be overwritten by other code.
9838      * @param {String/HTMLElement} el The dom node or id
9839      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9840      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9841      * @static
9842      * @return {Element} The shared Element object
9843      */
9844     El.fly = function(el, named){
9845         named = named || '_global';
9846         el = Roo.getDom(el);
9847         if(!el){
9848             return null;
9849         }
9850         if(!El._flyweights[named]){
9851             El._flyweights[named] = new El.Flyweight();
9852         }
9853         El._flyweights[named].dom = el;
9854         return El._flyweights[named];
9855     };
9856
9857     /**
9858      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9859      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9860      * Shorthand of {@link Roo.Element#get}
9861      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9862      * @return {Element} The Element object
9863      * @member Roo
9864      * @method get
9865      */
9866     Roo.get = El.get;
9867     /**
9868      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9869      * the dom node can be overwritten by other code.
9870      * Shorthand of {@link Roo.Element#fly}
9871      * @param {String/HTMLElement} el The dom node or id
9872      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9873      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9874      * @static
9875      * @return {Element} The shared Element object
9876      * @member Roo
9877      * @method fly
9878      */
9879     Roo.fly = El.fly;
9880
9881     // speedy lookup for elements never to box adjust
9882     var noBoxAdjust = Roo.isStrict ? {
9883         select:1
9884     } : {
9885         input:1, select:1, textarea:1
9886     };
9887     if(Roo.isIE || Roo.isGecko){
9888         noBoxAdjust['button'] = 1;
9889     }
9890
9891
9892     Roo.EventManager.on(window, 'unload', function(){
9893         delete El.cache;
9894         delete El._flyweights;
9895     });
9896 })();
9897
9898
9899
9900
9901 if(Roo.DomQuery){
9902     Roo.Element.selectorFunction = Roo.DomQuery.select;
9903 }
9904
9905 Roo.Element.select = function(selector, unique, root){
9906     var els;
9907     if(typeof selector == "string"){
9908         els = Roo.Element.selectorFunction(selector, root);
9909     }else if(selector.length !== undefined){
9910         els = selector;
9911     }else{
9912         throw "Invalid selector";
9913     }
9914     if(unique === true){
9915         return new Roo.CompositeElement(els);
9916     }else{
9917         return new Roo.CompositeElementLite(els);
9918     }
9919 };
9920 /**
9921  * Selects elements based on the passed CSS selector to enable working on them as 1.
9922  * @param {String/Array} selector The CSS selector or an array of elements
9923  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9924  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9925  * @return {CompositeElementLite/CompositeElement}
9926  * @member Roo
9927  * @method select
9928  */
9929 Roo.select = Roo.Element.select;
9930
9931
9932
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944 /*
9945  * Based on:
9946  * Ext JS Library 1.1.1
9947  * Copyright(c) 2006-2007, Ext JS, LLC.
9948  *
9949  * Originally Released Under LGPL - original licence link has changed is not relivant.
9950  *
9951  * Fork - LGPL
9952  * <script type="text/javascript">
9953  */
9954
9955
9956
9957 //Notifies Element that fx methods are available
9958 Roo.enableFx = true;
9959
9960 /**
9961  * @class Roo.Fx
9962  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9963  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9964  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9965  * Element effects to work.</p><br/>
9966  *
9967  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9968  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9969  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9970  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9971  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9972  * expected results and should be done with care.</p><br/>
9973  *
9974  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9975  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9976 <pre>
9977 Value  Description
9978 -----  -----------------------------
9979 tl     The top left corner
9980 t      The center of the top edge
9981 tr     The top right corner
9982 l      The center of the left edge
9983 r      The center of the right edge
9984 bl     The bottom left corner
9985 b      The center of the bottom edge
9986 br     The bottom right corner
9987 </pre>
9988  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9989  * below are common options that can be passed to any Fx method.</b>
9990  * @cfg {Function} callback A function called when the effect is finished
9991  * @cfg {Object} scope The scope of the effect function
9992  * @cfg {String} easing A valid Easing value for the effect
9993  * @cfg {String} afterCls A css class to apply after the effect
9994  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9995  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9996  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9997  * effects that end with the element being visually hidden, ignored otherwise)
9998  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9999  * a function which returns such a specification that will be applied to the Element after the effect finishes
10000  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10001  * @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
10002  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10003  */
10004 Roo.Fx = {
10005         /**
10006          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10007          * origin for the slide effect.  This function automatically handles wrapping the element with
10008          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10009          * Usage:
10010          *<pre><code>
10011 // default: slide the element in from the top
10012 el.slideIn();
10013
10014 // custom: slide the element in from the right with a 2-second duration
10015 el.slideIn('r', { duration: 2 });
10016
10017 // common config options shown with default values
10018 el.slideIn('t', {
10019     easing: 'easeOut',
10020     duration: .5
10021 });
10022 </code></pre>
10023          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10024          * @param {Object} options (optional) Object literal with any of the Fx config options
10025          * @return {Roo.Element} The Element
10026          */
10027     slideIn : function(anchor, o){
10028         var el = this.getFxEl();
10029         o = o || {};
10030
10031         el.queueFx(o, function(){
10032
10033             anchor = anchor || "t";
10034
10035             // fix display to visibility
10036             this.fixDisplay();
10037
10038             // restore values after effect
10039             var r = this.getFxRestore();
10040             var b = this.getBox();
10041             // fixed size for slide
10042             this.setSize(b);
10043
10044             // wrap if needed
10045             var wrap = this.fxWrap(r.pos, o, "hidden");
10046
10047             var st = this.dom.style;
10048             st.visibility = "visible";
10049             st.position = "absolute";
10050
10051             // clear out temp styles after slide and unwrap
10052             var after = function(){
10053                 el.fxUnwrap(wrap, r.pos, o);
10054                 st.width = r.width;
10055                 st.height = r.height;
10056                 el.afterFx(o);
10057             };
10058             // time to calc the positions
10059             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10060
10061             switch(anchor.toLowerCase()){
10062                 case "t":
10063                     wrap.setSize(b.width, 0);
10064                     st.left = st.bottom = "0";
10065                     a = {height: bh};
10066                 break;
10067                 case "l":
10068                     wrap.setSize(0, b.height);
10069                     st.right = st.top = "0";
10070                     a = {width: bw};
10071                 break;
10072                 case "r":
10073                     wrap.setSize(0, b.height);
10074                     wrap.setX(b.right);
10075                     st.left = st.top = "0";
10076                     a = {width: bw, points: pt};
10077                 break;
10078                 case "b":
10079                     wrap.setSize(b.width, 0);
10080                     wrap.setY(b.bottom);
10081                     st.left = st.top = "0";
10082                     a = {height: bh, points: pt};
10083                 break;
10084                 case "tl":
10085                     wrap.setSize(0, 0);
10086                     st.right = st.bottom = "0";
10087                     a = {width: bw, height: bh};
10088                 break;
10089                 case "bl":
10090                     wrap.setSize(0, 0);
10091                     wrap.setY(b.y+b.height);
10092                     st.right = st.top = "0";
10093                     a = {width: bw, height: bh, points: pt};
10094                 break;
10095                 case "br":
10096                     wrap.setSize(0, 0);
10097                     wrap.setXY([b.right, b.bottom]);
10098                     st.left = st.top = "0";
10099                     a = {width: bw, height: bh, points: pt};
10100                 break;
10101                 case "tr":
10102                     wrap.setSize(0, 0);
10103                     wrap.setX(b.x+b.width);
10104                     st.left = st.bottom = "0";
10105                     a = {width: bw, height: bh, points: pt};
10106                 break;
10107             }
10108             this.dom.style.visibility = "visible";
10109             wrap.show();
10110
10111             arguments.callee.anim = wrap.fxanim(a,
10112                 o,
10113                 'motion',
10114                 .5,
10115                 'easeOut', after);
10116         });
10117         return this;
10118     },
10119     
10120         /**
10121          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10122          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10123          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10124          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10125          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10126          * Usage:
10127          *<pre><code>
10128 // default: slide the element out to the top
10129 el.slideOut();
10130
10131 // custom: slide the element out to the right with a 2-second duration
10132 el.slideOut('r', { duration: 2 });
10133
10134 // common config options shown with default values
10135 el.slideOut('t', {
10136     easing: 'easeOut',
10137     duration: .5,
10138     remove: false,
10139     useDisplay: false
10140 });
10141 </code></pre>
10142          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10143          * @param {Object} options (optional) Object literal with any of the Fx config options
10144          * @return {Roo.Element} The Element
10145          */
10146     slideOut : function(anchor, o){
10147         var el = this.getFxEl();
10148         o = o || {};
10149
10150         el.queueFx(o, function(){
10151
10152             anchor = anchor || "t";
10153
10154             // restore values after effect
10155             var r = this.getFxRestore();
10156             
10157             var b = this.getBox();
10158             // fixed size for slide
10159             this.setSize(b);
10160
10161             // wrap if needed
10162             var wrap = this.fxWrap(r.pos, o, "visible");
10163
10164             var st = this.dom.style;
10165             st.visibility = "visible";
10166             st.position = "absolute";
10167
10168             wrap.setSize(b);
10169
10170             var after = function(){
10171                 if(o.useDisplay){
10172                     el.setDisplayed(false);
10173                 }else{
10174                     el.hide();
10175                 }
10176
10177                 el.fxUnwrap(wrap, r.pos, o);
10178
10179                 st.width = r.width;
10180                 st.height = r.height;
10181
10182                 el.afterFx(o);
10183             };
10184
10185             var a, zero = {to: 0};
10186             switch(anchor.toLowerCase()){
10187                 case "t":
10188                     st.left = st.bottom = "0";
10189                     a = {height: zero};
10190                 break;
10191                 case "l":
10192                     st.right = st.top = "0";
10193                     a = {width: zero};
10194                 break;
10195                 case "r":
10196                     st.left = st.top = "0";
10197                     a = {width: zero, points: {to:[b.right, b.y]}};
10198                 break;
10199                 case "b":
10200                     st.left = st.top = "0";
10201                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10202                 break;
10203                 case "tl":
10204                     st.right = st.bottom = "0";
10205                     a = {width: zero, height: zero};
10206                 break;
10207                 case "bl":
10208                     st.right = st.top = "0";
10209                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10210                 break;
10211                 case "br":
10212                     st.left = st.top = "0";
10213                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10214                 break;
10215                 case "tr":
10216                     st.left = st.bottom = "0";
10217                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10218                 break;
10219             }
10220
10221             arguments.callee.anim = wrap.fxanim(a,
10222                 o,
10223                 'motion',
10224                 .5,
10225                 "easeOut", after);
10226         });
10227         return this;
10228     },
10229
10230         /**
10231          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10232          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10233          * The element must be removed from the DOM using the 'remove' config option if desired.
10234          * Usage:
10235          *<pre><code>
10236 // default
10237 el.puff();
10238
10239 // common config options shown with default values
10240 el.puff({
10241     easing: 'easeOut',
10242     duration: .5,
10243     remove: false,
10244     useDisplay: false
10245 });
10246 </code></pre>
10247          * @param {Object} options (optional) Object literal with any of the Fx config options
10248          * @return {Roo.Element} The Element
10249          */
10250     puff : function(o){
10251         var el = this.getFxEl();
10252         o = o || {};
10253
10254         el.queueFx(o, function(){
10255             this.clearOpacity();
10256             this.show();
10257
10258             // restore values after effect
10259             var r = this.getFxRestore();
10260             var st = this.dom.style;
10261
10262             var after = function(){
10263                 if(o.useDisplay){
10264                     el.setDisplayed(false);
10265                 }else{
10266                     el.hide();
10267                 }
10268
10269                 el.clearOpacity();
10270
10271                 el.setPositioning(r.pos);
10272                 st.width = r.width;
10273                 st.height = r.height;
10274                 st.fontSize = '';
10275                 el.afterFx(o);
10276             };
10277
10278             var width = this.getWidth();
10279             var height = this.getHeight();
10280
10281             arguments.callee.anim = this.fxanim({
10282                     width : {to: this.adjustWidth(width * 2)},
10283                     height : {to: this.adjustHeight(height * 2)},
10284                     points : {by: [-(width * .5), -(height * .5)]},
10285                     opacity : {to: 0},
10286                     fontSize: {to:200, unit: "%"}
10287                 },
10288                 o,
10289                 'motion',
10290                 .5,
10291                 "easeOut", after);
10292         });
10293         return this;
10294     },
10295
10296         /**
10297          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10298          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10299          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10300          * Usage:
10301          *<pre><code>
10302 // default
10303 el.switchOff();
10304
10305 // all config options shown with default values
10306 el.switchOff({
10307     easing: 'easeIn',
10308     duration: .3,
10309     remove: false,
10310     useDisplay: false
10311 });
10312 </code></pre>
10313          * @param {Object} options (optional) Object literal with any of the Fx config options
10314          * @return {Roo.Element} The Element
10315          */
10316     switchOff : function(o){
10317         var el = this.getFxEl();
10318         o = o || {};
10319
10320         el.queueFx(o, function(){
10321             this.clearOpacity();
10322             this.clip();
10323
10324             // restore values after effect
10325             var r = this.getFxRestore();
10326             var st = this.dom.style;
10327
10328             var after = function(){
10329                 if(o.useDisplay){
10330                     el.setDisplayed(false);
10331                 }else{
10332                     el.hide();
10333                 }
10334
10335                 el.clearOpacity();
10336                 el.setPositioning(r.pos);
10337                 st.width = r.width;
10338                 st.height = r.height;
10339
10340                 el.afterFx(o);
10341             };
10342
10343             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10344                 this.clearOpacity();
10345                 (function(){
10346                     this.fxanim({
10347                         height:{to:1},
10348                         points:{by:[0, this.getHeight() * .5]}
10349                     }, o, 'motion', 0.3, 'easeIn', after);
10350                 }).defer(100, this);
10351             });
10352         });
10353         return this;
10354     },
10355
10356     /**
10357      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10358      * changed using the "attr" config option) and then fading back to the original color. If no original
10359      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10360      * Usage:
10361 <pre><code>
10362 // default: highlight background to yellow
10363 el.highlight();
10364
10365 // custom: highlight foreground text to blue for 2 seconds
10366 el.highlight("0000ff", { attr: 'color', duration: 2 });
10367
10368 // common config options shown with default values
10369 el.highlight("ffff9c", {
10370     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10371     endColor: (current color) or "ffffff",
10372     easing: 'easeIn',
10373     duration: 1
10374 });
10375 </code></pre>
10376      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10377      * @param {Object} options (optional) Object literal with any of the Fx config options
10378      * @return {Roo.Element} The Element
10379      */ 
10380     highlight : function(color, o){
10381         var el = this.getFxEl();
10382         o = o || {};
10383
10384         el.queueFx(o, function(){
10385             color = color || "ffff9c";
10386             attr = o.attr || "backgroundColor";
10387
10388             this.clearOpacity();
10389             this.show();
10390
10391             var origColor = this.getColor(attr);
10392             var restoreColor = this.dom.style[attr];
10393             endColor = (o.endColor || origColor) || "ffffff";
10394
10395             var after = function(){
10396                 el.dom.style[attr] = restoreColor;
10397                 el.afterFx(o);
10398             };
10399
10400             var a = {};
10401             a[attr] = {from: color, to: endColor};
10402             arguments.callee.anim = this.fxanim(a,
10403                 o,
10404                 'color',
10405                 1,
10406                 'easeIn', after);
10407         });
10408         return this;
10409     },
10410
10411    /**
10412     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10413     * Usage:
10414 <pre><code>
10415 // default: a single light blue ripple
10416 el.frame();
10417
10418 // custom: 3 red ripples lasting 3 seconds total
10419 el.frame("ff0000", 3, { duration: 3 });
10420
10421 // common config options shown with default values
10422 el.frame("C3DAF9", 1, {
10423     duration: 1 //duration of entire animation (not each individual ripple)
10424     // Note: Easing is not configurable and will be ignored if included
10425 });
10426 </code></pre>
10427     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10428     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10429     * @param {Object} options (optional) Object literal with any of the Fx config options
10430     * @return {Roo.Element} The Element
10431     */
10432     frame : function(color, count, o){
10433         var el = this.getFxEl();
10434         o = o || {};
10435
10436         el.queueFx(o, function(){
10437             color = color || "#C3DAF9";
10438             if(color.length == 6){
10439                 color = "#" + color;
10440             }
10441             count = count || 1;
10442             duration = o.duration || 1;
10443             this.show();
10444
10445             var b = this.getBox();
10446             var animFn = function(){
10447                 var proxy = this.createProxy({
10448
10449                      style:{
10450                         visbility:"hidden",
10451                         position:"absolute",
10452                         "z-index":"35000", // yee haw
10453                         border:"0px solid " + color
10454                      }
10455                   });
10456                 var scale = Roo.isBorderBox ? 2 : 1;
10457                 proxy.animate({
10458                     top:{from:b.y, to:b.y - 20},
10459                     left:{from:b.x, to:b.x - 20},
10460                     borderWidth:{from:0, to:10},
10461                     opacity:{from:1, to:0},
10462                     height:{from:b.height, to:(b.height + (20*scale))},
10463                     width:{from:b.width, to:(b.width + (20*scale))}
10464                 }, duration, function(){
10465                     proxy.remove();
10466                 });
10467                 if(--count > 0){
10468                      animFn.defer((duration/2)*1000, this);
10469                 }else{
10470                     el.afterFx(o);
10471                 }
10472             };
10473             animFn.call(this);
10474         });
10475         return this;
10476     },
10477
10478    /**
10479     * Creates a pause before any subsequent queued effects begin.  If there are
10480     * no effects queued after the pause it will have no effect.
10481     * Usage:
10482 <pre><code>
10483 el.pause(1);
10484 </code></pre>
10485     * @param {Number} seconds The length of time to pause (in seconds)
10486     * @return {Roo.Element} The Element
10487     */
10488     pause : function(seconds){
10489         var el = this.getFxEl();
10490         var o = {};
10491
10492         el.queueFx(o, function(){
10493             setTimeout(function(){
10494                 el.afterFx(o);
10495             }, seconds * 1000);
10496         });
10497         return this;
10498     },
10499
10500    /**
10501     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10502     * using the "endOpacity" config option.
10503     * Usage:
10504 <pre><code>
10505 // default: fade in from opacity 0 to 100%
10506 el.fadeIn();
10507
10508 // custom: fade in from opacity 0 to 75% over 2 seconds
10509 el.fadeIn({ endOpacity: .75, duration: 2});
10510
10511 // common config options shown with default values
10512 el.fadeIn({
10513     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10514     easing: 'easeOut',
10515     duration: .5
10516 });
10517 </code></pre>
10518     * @param {Object} options (optional) Object literal with any of the Fx config options
10519     * @return {Roo.Element} The Element
10520     */
10521     fadeIn : function(o){
10522         var el = this.getFxEl();
10523         o = o || {};
10524         el.queueFx(o, function(){
10525             this.setOpacity(0);
10526             this.fixDisplay();
10527             this.dom.style.visibility = 'visible';
10528             var to = o.endOpacity || 1;
10529             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10530                 o, null, .5, "easeOut", function(){
10531                 if(to == 1){
10532                     this.clearOpacity();
10533                 }
10534                 el.afterFx(o);
10535             });
10536         });
10537         return this;
10538     },
10539
10540    /**
10541     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10542     * using the "endOpacity" config option.
10543     * Usage:
10544 <pre><code>
10545 // default: fade out from the element's current opacity to 0
10546 el.fadeOut();
10547
10548 // custom: fade out from the element's current opacity to 25% over 2 seconds
10549 el.fadeOut({ endOpacity: .25, duration: 2});
10550
10551 // common config options shown with default values
10552 el.fadeOut({
10553     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10554     easing: 'easeOut',
10555     duration: .5
10556     remove: false,
10557     useDisplay: false
10558 });
10559 </code></pre>
10560     * @param {Object} options (optional) Object literal with any of the Fx config options
10561     * @return {Roo.Element} The Element
10562     */
10563     fadeOut : function(o){
10564         var el = this.getFxEl();
10565         o = o || {};
10566         el.queueFx(o, function(){
10567             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10568                 o, null, .5, "easeOut", function(){
10569                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10570                      this.dom.style.display = "none";
10571                 }else{
10572                      this.dom.style.visibility = "hidden";
10573                 }
10574                 this.clearOpacity();
10575                 el.afterFx(o);
10576             });
10577         });
10578         return this;
10579     },
10580
10581    /**
10582     * Animates the transition of an element's dimensions from a starting height/width
10583     * to an ending height/width.
10584     * Usage:
10585 <pre><code>
10586 // change height and width to 100x100 pixels
10587 el.scale(100, 100);
10588
10589 // common config options shown with default values.  The height and width will default to
10590 // the element's existing values if passed as null.
10591 el.scale(
10592     [element's width],
10593     [element's height], {
10594     easing: 'easeOut',
10595     duration: .35
10596 });
10597 </code></pre>
10598     * @param {Number} width  The new width (pass undefined to keep the original width)
10599     * @param {Number} height  The new height (pass undefined to keep the original height)
10600     * @param {Object} options (optional) Object literal with any of the Fx config options
10601     * @return {Roo.Element} The Element
10602     */
10603     scale : function(w, h, o){
10604         this.shift(Roo.apply({}, o, {
10605             width: w,
10606             height: h
10607         }));
10608         return this;
10609     },
10610
10611    /**
10612     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10613     * Any of these properties not specified in the config object will not be changed.  This effect 
10614     * requires that at least one new dimension, position or opacity setting must be passed in on
10615     * the config object in order for the function to have any effect.
10616     * Usage:
10617 <pre><code>
10618 // slide the element horizontally to x position 200 while changing the height and opacity
10619 el.shift({ x: 200, height: 50, opacity: .8 });
10620
10621 // common config options shown with default values.
10622 el.shift({
10623     width: [element's width],
10624     height: [element's height],
10625     x: [element's x position],
10626     y: [element's y position],
10627     opacity: [element's opacity],
10628     easing: 'easeOut',
10629     duration: .35
10630 });
10631 </code></pre>
10632     * @param {Object} options  Object literal with any of the Fx config options
10633     * @return {Roo.Element} The Element
10634     */
10635     shift : function(o){
10636         var el = this.getFxEl();
10637         o = o || {};
10638         el.queueFx(o, function(){
10639             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10640             if(w !== undefined){
10641                 a.width = {to: this.adjustWidth(w)};
10642             }
10643             if(h !== undefined){
10644                 a.height = {to: this.adjustHeight(h)};
10645             }
10646             if(x !== undefined || y !== undefined){
10647                 a.points = {to: [
10648                     x !== undefined ? x : this.getX(),
10649                     y !== undefined ? y : this.getY()
10650                 ]};
10651             }
10652             if(op !== undefined){
10653                 a.opacity = {to: op};
10654             }
10655             if(o.xy !== undefined){
10656                 a.points = {to: o.xy};
10657             }
10658             arguments.callee.anim = this.fxanim(a,
10659                 o, 'motion', .35, "easeOut", function(){
10660                 el.afterFx(o);
10661             });
10662         });
10663         return this;
10664     },
10665
10666         /**
10667          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10668          * ending point of the effect.
10669          * Usage:
10670          *<pre><code>
10671 // default: slide the element downward while fading out
10672 el.ghost();
10673
10674 // custom: slide the element out to the right with a 2-second duration
10675 el.ghost('r', { duration: 2 });
10676
10677 // common config options shown with default values
10678 el.ghost('b', {
10679     easing: 'easeOut',
10680     duration: .5
10681     remove: false,
10682     useDisplay: false
10683 });
10684 </code></pre>
10685          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10686          * @param {Object} options (optional) Object literal with any of the Fx config options
10687          * @return {Roo.Element} The Element
10688          */
10689     ghost : function(anchor, o){
10690         var el = this.getFxEl();
10691         o = o || {};
10692
10693         el.queueFx(o, function(){
10694             anchor = anchor || "b";
10695
10696             // restore values after effect
10697             var r = this.getFxRestore();
10698             var w = this.getWidth(),
10699                 h = this.getHeight();
10700
10701             var st = this.dom.style;
10702
10703             var after = function(){
10704                 if(o.useDisplay){
10705                     el.setDisplayed(false);
10706                 }else{
10707                     el.hide();
10708                 }
10709
10710                 el.clearOpacity();
10711                 el.setPositioning(r.pos);
10712                 st.width = r.width;
10713                 st.height = r.height;
10714
10715                 el.afterFx(o);
10716             };
10717
10718             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10719             switch(anchor.toLowerCase()){
10720                 case "t":
10721                     pt.by = [0, -h];
10722                 break;
10723                 case "l":
10724                     pt.by = [-w, 0];
10725                 break;
10726                 case "r":
10727                     pt.by = [w, 0];
10728                 break;
10729                 case "b":
10730                     pt.by = [0, h];
10731                 break;
10732                 case "tl":
10733                     pt.by = [-w, -h];
10734                 break;
10735                 case "bl":
10736                     pt.by = [-w, h];
10737                 break;
10738                 case "br":
10739                     pt.by = [w, h];
10740                 break;
10741                 case "tr":
10742                     pt.by = [w, -h];
10743                 break;
10744             }
10745
10746             arguments.callee.anim = this.fxanim(a,
10747                 o,
10748                 'motion',
10749                 .5,
10750                 "easeOut", after);
10751         });
10752         return this;
10753     },
10754
10755         /**
10756          * Ensures that all effects queued after syncFx is called on the element are
10757          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10758          * @return {Roo.Element} The Element
10759          */
10760     syncFx : function(){
10761         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10762             block : false,
10763             concurrent : true,
10764             stopFx : false
10765         });
10766         return this;
10767     },
10768
10769         /**
10770          * Ensures that all effects queued after sequenceFx is called on the element are
10771          * run in sequence.  This is the opposite of {@link #syncFx}.
10772          * @return {Roo.Element} The Element
10773          */
10774     sequenceFx : function(){
10775         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10776             block : false,
10777             concurrent : false,
10778             stopFx : false
10779         });
10780         return this;
10781     },
10782
10783         /* @private */
10784     nextFx : function(){
10785         var ef = this.fxQueue[0];
10786         if(ef){
10787             ef.call(this);
10788         }
10789     },
10790
10791         /**
10792          * Returns true if the element has any effects actively running or queued, else returns false.
10793          * @return {Boolean} True if element has active effects, else false
10794          */
10795     hasActiveFx : function(){
10796         return this.fxQueue && this.fxQueue[0];
10797     },
10798
10799         /**
10800          * Stops any running effects and clears the element's internal effects queue if it contains
10801          * any additional effects that haven't started yet.
10802          * @return {Roo.Element} The Element
10803          */
10804     stopFx : function(){
10805         if(this.hasActiveFx()){
10806             var cur = this.fxQueue[0];
10807             if(cur && cur.anim && cur.anim.isAnimated()){
10808                 this.fxQueue = [cur]; // clear out others
10809                 cur.anim.stop(true);
10810             }
10811         }
10812         return this;
10813     },
10814
10815         /* @private */
10816     beforeFx : function(o){
10817         if(this.hasActiveFx() && !o.concurrent){
10818            if(o.stopFx){
10819                this.stopFx();
10820                return true;
10821            }
10822            return false;
10823         }
10824         return true;
10825     },
10826
10827         /**
10828          * Returns true if the element is currently blocking so that no other effect can be queued
10829          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10830          * used to ensure that an effect initiated by a user action runs to completion prior to the
10831          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10832          * @return {Boolean} True if blocking, else false
10833          */
10834     hasFxBlock : function(){
10835         var q = this.fxQueue;
10836         return q && q[0] && q[0].block;
10837     },
10838
10839         /* @private */
10840     queueFx : function(o, fn){
10841         if(!this.fxQueue){
10842             this.fxQueue = [];
10843         }
10844         if(!this.hasFxBlock()){
10845             Roo.applyIf(o, this.fxDefaults);
10846             if(!o.concurrent){
10847                 var run = this.beforeFx(o);
10848                 fn.block = o.block;
10849                 this.fxQueue.push(fn);
10850                 if(run){
10851                     this.nextFx();
10852                 }
10853             }else{
10854                 fn.call(this);
10855             }
10856         }
10857         return this;
10858     },
10859
10860         /* @private */
10861     fxWrap : function(pos, o, vis){
10862         var wrap;
10863         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10864             var wrapXY;
10865             if(o.fixPosition){
10866                 wrapXY = this.getXY();
10867             }
10868             var div = document.createElement("div");
10869             div.style.visibility = vis;
10870             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10871             wrap.setPositioning(pos);
10872             if(wrap.getStyle("position") == "static"){
10873                 wrap.position("relative");
10874             }
10875             this.clearPositioning('auto');
10876             wrap.clip();
10877             wrap.dom.appendChild(this.dom);
10878             if(wrapXY){
10879                 wrap.setXY(wrapXY);
10880             }
10881         }
10882         return wrap;
10883     },
10884
10885         /* @private */
10886     fxUnwrap : function(wrap, pos, o){
10887         this.clearPositioning();
10888         this.setPositioning(pos);
10889         if(!o.wrap){
10890             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10891             wrap.remove();
10892         }
10893     },
10894
10895         /* @private */
10896     getFxRestore : function(){
10897         var st = this.dom.style;
10898         return {pos: this.getPositioning(), width: st.width, height : st.height};
10899     },
10900
10901         /* @private */
10902     afterFx : function(o){
10903         if(o.afterStyle){
10904             this.applyStyles(o.afterStyle);
10905         }
10906         if(o.afterCls){
10907             this.addClass(o.afterCls);
10908         }
10909         if(o.remove === true){
10910             this.remove();
10911         }
10912         Roo.callback(o.callback, o.scope, [this]);
10913         if(!o.concurrent){
10914             this.fxQueue.shift();
10915             this.nextFx();
10916         }
10917     },
10918
10919         /* @private */
10920     getFxEl : function(){ // support for composite element fx
10921         return Roo.get(this.dom);
10922     },
10923
10924         /* @private */
10925     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10926         animType = animType || 'run';
10927         opt = opt || {};
10928         var anim = Roo.lib.Anim[animType](
10929             this.dom, args,
10930             (opt.duration || defaultDur) || .35,
10931             (opt.easing || defaultEase) || 'easeOut',
10932             function(){
10933                 Roo.callback(cb, this);
10934             },
10935             this
10936         );
10937         opt.anim = anim;
10938         return anim;
10939     }
10940 };
10941
10942 // backwords compat
10943 Roo.Fx.resize = Roo.Fx.scale;
10944
10945 //When included, Roo.Fx is automatically applied to Element so that all basic
10946 //effects are available directly via the Element API
10947 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10948  * Based on:
10949  * Ext JS Library 1.1.1
10950  * Copyright(c) 2006-2007, Ext JS, LLC.
10951  *
10952  * Originally Released Under LGPL - original licence link has changed is not relivant.
10953  *
10954  * Fork - LGPL
10955  * <script type="text/javascript">
10956  */
10957
10958
10959 /**
10960  * @class Roo.CompositeElement
10961  * Standard composite class. Creates a Roo.Element for every element in the collection.
10962  * <br><br>
10963  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10964  * actions will be performed on all the elements in this collection.</b>
10965  * <br><br>
10966  * All methods return <i>this</i> and can be chained.
10967  <pre><code>
10968  var els = Roo.select("#some-el div.some-class", true);
10969  // or select directly from an existing element
10970  var el = Roo.get('some-el');
10971  el.select('div.some-class', true);
10972
10973  els.setWidth(100); // all elements become 100 width
10974  els.hide(true); // all elements fade out and hide
10975  // or
10976  els.setWidth(100).hide(true);
10977  </code></pre>
10978  */
10979 Roo.CompositeElement = function(els){
10980     this.elements = [];
10981     this.addElements(els);
10982 };
10983 Roo.CompositeElement.prototype = {
10984     isComposite: true,
10985     addElements : function(els){
10986         if(!els) return this;
10987         if(typeof els == "string"){
10988             els = Roo.Element.selectorFunction(els);
10989         }
10990         var yels = this.elements;
10991         var index = yels.length-1;
10992         for(var i = 0, len = els.length; i < len; i++) {
10993                 yels[++index] = Roo.get(els[i]);
10994         }
10995         return this;
10996     },
10997
10998     /**
10999     * Clears this composite and adds the elements returned by the passed selector.
11000     * @param {String/Array} els A string CSS selector, an array of elements or an element
11001     * @return {CompositeElement} this
11002     */
11003     fill : function(els){
11004         this.elements = [];
11005         this.add(els);
11006         return this;
11007     },
11008
11009     /**
11010     * Filters this composite to only elements that match the passed selector.
11011     * @param {String} selector A string CSS selector
11012     * @param {Boolean} inverse return inverse filter (not matches)
11013     * @return {CompositeElement} this
11014     */
11015     filter : function(selector, inverse){
11016         var els = [];
11017         inverse = inverse || false;
11018         this.each(function(el){
11019             var match = inverse ? !el.is(selector) : el.is(selector);
11020             if(match){
11021                 els[els.length] = el.dom;
11022             }
11023         });
11024         this.fill(els);
11025         return this;
11026     },
11027
11028     invoke : function(fn, args){
11029         var els = this.elements;
11030         for(var i = 0, len = els.length; i < len; i++) {
11031                 Roo.Element.prototype[fn].apply(els[i], args);
11032         }
11033         return this;
11034     },
11035     /**
11036     * Adds elements to this composite.
11037     * @param {String/Array} els A string CSS selector, an array of elements or an element
11038     * @return {CompositeElement} this
11039     */
11040     add : function(els){
11041         if(typeof els == "string"){
11042             this.addElements(Roo.Element.selectorFunction(els));
11043         }else if(els.length !== undefined){
11044             this.addElements(els);
11045         }else{
11046             this.addElements([els]);
11047         }
11048         return this;
11049     },
11050     /**
11051     * Calls the passed function passing (el, this, index) for each element in this composite.
11052     * @param {Function} fn The function to call
11053     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11054     * @return {CompositeElement} this
11055     */
11056     each : function(fn, scope){
11057         var els = this.elements;
11058         for(var i = 0, len = els.length; i < len; i++){
11059             if(fn.call(scope || els[i], els[i], this, i) === false) {
11060                 break;
11061             }
11062         }
11063         return this;
11064     },
11065
11066     /**
11067      * Returns the Element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         return this.elements[index] || null;
11073     },
11074
11075     /**
11076      * Returns the first Element
11077      * @return {Roo.Element}
11078      */
11079     first : function(){
11080         return this.item(0);
11081     },
11082
11083     /**
11084      * Returns the last Element
11085      * @return {Roo.Element}
11086      */
11087     last : function(){
11088         return this.item(this.elements.length-1);
11089     },
11090
11091     /**
11092      * Returns the number of elements in this composite
11093      * @return Number
11094      */
11095     getCount : function(){
11096         return this.elements.length;
11097     },
11098
11099     /**
11100      * Returns true if this composite contains the passed element
11101      * @return Boolean
11102      */
11103     contains : function(el){
11104         return this.indexOf(el) !== -1;
11105     },
11106
11107     /**
11108      * Returns true if this composite contains the passed element
11109      * @return Boolean
11110      */
11111     indexOf : function(el){
11112         return this.elements.indexOf(Roo.get(el));
11113     },
11114
11115
11116     /**
11117     * Removes the specified element(s).
11118     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11119     * or an array of any of those.
11120     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11121     * @return {CompositeElement} this
11122     */
11123     removeElement : function(el, removeDom){
11124         if(el instanceof Array){
11125             for(var i = 0, len = el.length; i < len; i++){
11126                 this.removeElement(el[i]);
11127             }
11128             return this;
11129         }
11130         var index = typeof el == 'number' ? el : this.indexOf(el);
11131         if(index !== -1){
11132             if(removeDom){
11133                 var d = this.elements[index];
11134                 if(d.dom){
11135                     d.remove();
11136                 }else{
11137                     d.parentNode.removeChild(d);
11138                 }
11139             }
11140             this.elements.splice(index, 1);
11141         }
11142         return this;
11143     },
11144
11145     /**
11146     * Replaces the specified element with the passed element.
11147     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11148     * to replace.
11149     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11150     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11151     * @return {CompositeElement} this
11152     */
11153     replaceElement : function(el, replacement, domReplace){
11154         var index = typeof el == 'number' ? el : this.indexOf(el);
11155         if(index !== -1){
11156             if(domReplace){
11157                 this.elements[index].replaceWith(replacement);
11158             }else{
11159                 this.elements.splice(index, 1, Roo.get(replacement))
11160             }
11161         }
11162         return this;
11163     },
11164
11165     /**
11166      * Removes all elements.
11167      */
11168     clear : function(){
11169         this.elements = [];
11170     }
11171 };
11172 (function(){
11173     Roo.CompositeElement.createCall = function(proto, fnName){
11174         if(!proto[fnName]){
11175             proto[fnName] = function(){
11176                 return this.invoke(fnName, arguments);
11177             };
11178         }
11179     };
11180     for(var fnName in Roo.Element.prototype){
11181         if(typeof Roo.Element.prototype[fnName] == "function"){
11182             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11183         }
11184     };
11185 })();
11186 /*
11187  * Based on:
11188  * Ext JS Library 1.1.1
11189  * Copyright(c) 2006-2007, Ext JS, LLC.
11190  *
11191  * Originally Released Under LGPL - original licence link has changed is not relivant.
11192  *
11193  * Fork - LGPL
11194  * <script type="text/javascript">
11195  */
11196
11197 /**
11198  * @class Roo.CompositeElementLite
11199  * @extends Roo.CompositeElement
11200  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11201  <pre><code>
11202  var els = Roo.select("#some-el div.some-class");
11203  // or select directly from an existing element
11204  var el = Roo.get('some-el');
11205  el.select('div.some-class');
11206
11207  els.setWidth(100); // all elements become 100 width
11208  els.hide(true); // all elements fade out and hide
11209  // or
11210  els.setWidth(100).hide(true);
11211  </code></pre><br><br>
11212  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11213  * actions will be performed on all the elements in this collection.</b>
11214  */
11215 Roo.CompositeElementLite = function(els){
11216     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11217     this.el = new Roo.Element.Flyweight();
11218 };
11219 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11220     addElements : function(els){
11221         if(els){
11222             if(els instanceof Array){
11223                 this.elements = this.elements.concat(els);
11224             }else{
11225                 var yels = this.elements;
11226                 var index = yels.length-1;
11227                 for(var i = 0, len = els.length; i < len; i++) {
11228                     yels[++index] = els[i];
11229                 }
11230             }
11231         }
11232         return this;
11233     },
11234     invoke : function(fn, args){
11235         var els = this.elements;
11236         var el = this.el;
11237         for(var i = 0, len = els.length; i < len; i++) {
11238             el.dom = els[i];
11239                 Roo.Element.prototype[fn].apply(el, args);
11240         }
11241         return this;
11242     },
11243     /**
11244      * Returns a flyweight Element of the dom element object at the specified index
11245      * @param {Number} index
11246      * @return {Roo.Element}
11247      */
11248     item : function(index){
11249         if(!this.elements[index]){
11250             return null;
11251         }
11252         this.el.dom = this.elements[index];
11253         return this.el;
11254     },
11255
11256     // fixes scope with flyweight
11257     addListener : function(eventName, handler, scope, opt){
11258         var els = this.elements;
11259         for(var i = 0, len = els.length; i < len; i++) {
11260             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11261         }
11262         return this;
11263     },
11264
11265     /**
11266     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11267     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11268     * a reference to the dom node, use el.dom.</b>
11269     * @param {Function} fn The function to call
11270     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11271     * @return {CompositeElement} this
11272     */
11273     each : function(fn, scope){
11274         var els = this.elements;
11275         var el = this.el;
11276         for(var i = 0, len = els.length; i < len; i++){
11277             el.dom = els[i];
11278                 if(fn.call(scope || el, el, this, i) === false){
11279                 break;
11280             }
11281         }
11282         return this;
11283     },
11284
11285     indexOf : function(el){
11286         return this.elements.indexOf(Roo.getDom(el));
11287     },
11288
11289     replaceElement : function(el, replacement, domReplace){
11290         var index = typeof el == 'number' ? el : this.indexOf(el);
11291         if(index !== -1){
11292             replacement = Roo.getDom(replacement);
11293             if(domReplace){
11294                 var d = this.elements[index];
11295                 d.parentNode.insertBefore(replacement, d);
11296                 d.parentNode.removeChild(d);
11297             }
11298             this.elements.splice(index, 1, replacement);
11299         }
11300         return this;
11301     }
11302 });
11303 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11304
11305 /*
11306  * Based on:
11307  * Ext JS Library 1.1.1
11308  * Copyright(c) 2006-2007, Ext JS, LLC.
11309  *
11310  * Originally Released Under LGPL - original licence link has changed is not relivant.
11311  *
11312  * Fork - LGPL
11313  * <script type="text/javascript">
11314  */
11315
11316  
11317
11318 /**
11319  * @class Roo.data.Connection
11320  * @extends Roo.util.Observable
11321  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11322  * either to a configured URL, or to a URL specified at request time.<br><br>
11323  * <p>
11324  * Requests made by this class are asynchronous, and will return immediately. No data from
11325  * the server will be available to the statement immediately following the {@link #request} call.
11326  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11327  * <p>
11328  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11329  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11330  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11331  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11332  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11333  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11334  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11335  * standard DOM methods.
11336  * @constructor
11337  * @param {Object} config a configuration object.
11338  */
11339 Roo.data.Connection = function(config){
11340     Roo.apply(this, config);
11341     this.addEvents({
11342         /**
11343          * @event beforerequest
11344          * Fires before a network request is made to retrieve a data object.
11345          * @param {Connection} conn This Connection object.
11346          * @param {Object} options The options config object passed to the {@link #request} method.
11347          */
11348         "beforerequest" : true,
11349         /**
11350          * @event requestcomplete
11351          * Fires if the request was successfully completed.
11352          * @param {Connection} conn This Connection object.
11353          * @param {Object} response The XHR object containing the response data.
11354          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11355          * @param {Object} options The options config object passed to the {@link #request} method.
11356          */
11357         "requestcomplete" : true,
11358         /**
11359          * @event requestexception
11360          * Fires if an error HTTP status was returned from the server.
11361          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11362          * @param {Connection} conn This Connection object.
11363          * @param {Object} response The XHR object containing the response data.
11364          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11365          * @param {Object} options The options config object passed to the {@link #request} method.
11366          */
11367         "requestexception" : true
11368     });
11369     Roo.data.Connection.superclass.constructor.call(this);
11370 };
11371
11372 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11373     /**
11374      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11375      */
11376     /**
11377      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11378      * extra parameters to each request made by this object. (defaults to undefined)
11379      */
11380     /**
11381      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11382      *  to each request made by this object. (defaults to undefined)
11383      */
11384     /**
11385      * @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)
11386      */
11387     /**
11388      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11389      */
11390     timeout : 30000,
11391     /**
11392      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11393      * @type Boolean
11394      */
11395     autoAbort:false,
11396
11397     /**
11398      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11399      * @type Boolean
11400      */
11401     disableCaching: true,
11402
11403     /**
11404      * Sends an HTTP request to a remote server.
11405      * @param {Object} options An object which may contain the following properties:<ul>
11406      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11407      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11408      * request, a url encoded string or a function to call to get either.</li>
11409      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11410      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11411      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11412      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11413      * <li>options {Object} The parameter to the request call.</li>
11414      * <li>success {Boolean} True if the request succeeded.</li>
11415      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11416      * </ul></li>
11417      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11418      * The callback is passed the following parameters:<ul>
11419      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11420      * <li>options {Object} The parameter to the request call.</li>
11421      * </ul></li>
11422      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11423      * The callback is passed the following parameters:<ul>
11424      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11425      * <li>options {Object} The parameter to the request call.</li>
11426      * </ul></li>
11427      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11428      * for the callback function. Defaults to the browser window.</li>
11429      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11430      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11431      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11432      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11433      * params for the post data. Any params will be appended to the URL.</li>
11434      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11435      * </ul>
11436      * @return {Number} transactionId
11437      */
11438     request : function(o){
11439         if(this.fireEvent("beforerequest", this, o) !== false){
11440             var p = o.params;
11441
11442             if(typeof p == "function"){
11443                 p = p.call(o.scope||window, o);
11444             }
11445             if(typeof p == "object"){
11446                 p = Roo.urlEncode(o.params);
11447             }
11448             if(this.extraParams){
11449                 var extras = Roo.urlEncode(this.extraParams);
11450                 p = p ? (p + '&' + extras) : extras;
11451             }
11452
11453             var url = o.url || this.url;
11454             if(typeof url == 'function'){
11455                 url = url.call(o.scope||window, o);
11456             }
11457
11458             if(o.form){
11459                 var form = Roo.getDom(o.form);
11460                 url = url || form.action;
11461
11462                 var enctype = form.getAttribute("enctype");
11463                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11464                     return this.doFormUpload(o, p, url);
11465                 }
11466                 var f = Roo.lib.Ajax.serializeForm(form);
11467                 p = p ? (p + '&' + f) : f;
11468             }
11469
11470             var hs = o.headers;
11471             if(this.defaultHeaders){
11472                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11473                 if(!o.headers){
11474                     o.headers = hs;
11475                 }
11476             }
11477
11478             var cb = {
11479                 success: this.handleResponse,
11480                 failure: this.handleFailure,
11481                 scope: this,
11482                 argument: {options: o},
11483                 timeout : o.timeout || this.timeout
11484             };
11485
11486             var method = o.method||this.method||(p ? "POST" : "GET");
11487
11488             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11489                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11490             }
11491
11492             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11493                 if(o.autoAbort){
11494                     this.abort();
11495                 }
11496             }else if(this.autoAbort !== false){
11497                 this.abort();
11498             }
11499
11500             if((method == 'GET' && p) || o.xmlData){
11501                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11502                 p = '';
11503             }
11504             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11505             return this.transId;
11506         }else{
11507             Roo.callback(o.callback, o.scope, [o, null, null]);
11508             return null;
11509         }
11510     },
11511
11512     /**
11513      * Determine whether this object has a request outstanding.
11514      * @param {Number} transactionId (Optional) defaults to the last transaction
11515      * @return {Boolean} True if there is an outstanding request.
11516      */
11517     isLoading : function(transId){
11518         if(transId){
11519             return Roo.lib.Ajax.isCallInProgress(transId);
11520         }else{
11521             return this.transId ? true : false;
11522         }
11523     },
11524
11525     /**
11526      * Aborts any outstanding request.
11527      * @param {Number} transactionId (Optional) defaults to the last transaction
11528      */
11529     abort : function(transId){
11530         if(transId || this.isLoading()){
11531             Roo.lib.Ajax.abort(transId || this.transId);
11532         }
11533     },
11534
11535     // private
11536     handleResponse : function(response){
11537         this.transId = false;
11538         var options = response.argument.options;
11539         response.argument = options ? options.argument : null;
11540         this.fireEvent("requestcomplete", this, response, options);
11541         Roo.callback(options.success, options.scope, [response, options]);
11542         Roo.callback(options.callback, options.scope, [options, true, response]);
11543     },
11544
11545     // private
11546     handleFailure : function(response, e){
11547         this.transId = false;
11548         var options = response.argument.options;
11549         response.argument = options ? options.argument : null;
11550         this.fireEvent("requestexception", this, response, options, e);
11551         Roo.callback(options.failure, options.scope, [response, options]);
11552         Roo.callback(options.callback, options.scope, [options, false, response]);
11553     },
11554
11555     // private
11556     doFormUpload : function(o, ps, url){
11557         var id = Roo.id();
11558         var frame = document.createElement('iframe');
11559         frame.id = id;
11560         frame.name = id;
11561         frame.className = 'x-hidden';
11562         if(Roo.isIE){
11563             frame.src = Roo.SSL_SECURE_URL;
11564         }
11565         document.body.appendChild(frame);
11566
11567         if(Roo.isIE){
11568            document.frames[id].name = id;
11569         }
11570
11571         var form = Roo.getDom(o.form);
11572         form.target = id;
11573         form.method = 'POST';
11574         form.enctype = form.encoding = 'multipart/form-data';
11575         if(url){
11576             form.action = url;
11577         }
11578
11579         var hiddens, hd;
11580         if(ps){ // add dynamic params
11581             hiddens = [];
11582             ps = Roo.urlDecode(ps, false);
11583             for(var k in ps){
11584                 if(ps.hasOwnProperty(k)){
11585                     hd = document.createElement('input');
11586                     hd.type = 'hidden';
11587                     hd.name = k;
11588                     hd.value = ps[k];
11589                     form.appendChild(hd);
11590                     hiddens.push(hd);
11591                 }
11592             }
11593         }
11594
11595         function cb(){
11596             var r = {  // bogus response object
11597                 responseText : '',
11598                 responseXML : null
11599             };
11600
11601             r.argument = o ? o.argument : null;
11602
11603             try { //
11604                 var doc;
11605                 if(Roo.isIE){
11606                     doc = frame.contentWindow.document;
11607                 }else {
11608                     doc = (frame.contentDocument || window.frames[id].document);
11609                 }
11610                 if(doc && doc.body){
11611                     r.responseText = doc.body.innerHTML;
11612                 }
11613                 if(doc && doc.XMLDocument){
11614                     r.responseXML = doc.XMLDocument;
11615                 }else {
11616                     r.responseXML = doc;
11617                 }
11618             }
11619             catch(e) {
11620                 // ignore
11621             }
11622
11623             Roo.EventManager.removeListener(frame, 'load', cb, this);
11624
11625             this.fireEvent("requestcomplete", this, r, o);
11626             Roo.callback(o.success, o.scope, [r, o]);
11627             Roo.callback(o.callback, o.scope, [o, true, r]);
11628
11629             setTimeout(function(){document.body.removeChild(frame);}, 100);
11630         }
11631
11632         Roo.EventManager.on(frame, 'load', cb, this);
11633         form.submit();
11634
11635         if(hiddens){ // remove dynamic params
11636             for(var i = 0, len = hiddens.length; i < len; i++){
11637                 form.removeChild(hiddens[i]);
11638             }
11639         }
11640     }
11641 });
11642 /*
11643  * Based on:
11644  * Ext JS Library 1.1.1
11645  * Copyright(c) 2006-2007, Ext JS, LLC.
11646  *
11647  * Originally Released Under LGPL - original licence link has changed is not relivant.
11648  *
11649  * Fork - LGPL
11650  * <script type="text/javascript">
11651  */
11652  
11653 /**
11654  * Global Ajax request class.
11655  * 
11656  * @class Roo.Ajax
11657  * @extends Roo.data.Connection
11658  * @static
11659  * 
11660  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11661  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11662  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11663  * @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)
11664  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11665  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11666  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11667  */
11668 Roo.Ajax = new Roo.data.Connection({
11669     // fix up the docs
11670     /**
11671      * @scope Roo.Ajax
11672      * @type {Boolear} 
11673      */
11674     autoAbort : false,
11675
11676     /**
11677      * Serialize the passed form into a url encoded string
11678      * @scope Roo.Ajax
11679      * @param {String/HTMLElement} form
11680      * @return {String}
11681      */
11682     serializeForm : function(form){
11683         return Roo.lib.Ajax.serializeForm(form);
11684     }
11685 });/*
11686  * Based on:
11687  * Ext JS Library 1.1.1
11688  * Copyright(c) 2006-2007, Ext JS, LLC.
11689  *
11690  * Originally Released Under LGPL - original licence link has changed is not relivant.
11691  *
11692  * Fork - LGPL
11693  * <script type="text/javascript">
11694  */
11695
11696  
11697 /**
11698  * @class Roo.UpdateManager
11699  * @extends Roo.util.Observable
11700  * Provides AJAX-style update for Element object.<br><br>
11701  * Usage:<br>
11702  * <pre><code>
11703  * // Get it from a Roo.Element object
11704  * var el = Roo.get("foo");
11705  * var mgr = el.getUpdateManager();
11706  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11707  * ...
11708  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11709  * <br>
11710  * // or directly (returns the same UpdateManager instance)
11711  * var mgr = new Roo.UpdateManager("myElementId");
11712  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11713  * mgr.on("update", myFcnNeedsToKnow);
11714  * <br>
11715    // short handed call directly from the element object
11716    Roo.get("foo").load({
11717         url: "bar.php",
11718         scripts:true,
11719         params: "for=bar",
11720         text: "Loading Foo..."
11721    });
11722  * </code></pre>
11723  * @constructor
11724  * Create new UpdateManager directly.
11725  * @param {String/HTMLElement/Roo.Element} el The element to update
11726  * @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).
11727  */
11728 Roo.UpdateManager = function(el, forceNew){
11729     el = Roo.get(el);
11730     if(!forceNew && el.updateManager){
11731         return el.updateManager;
11732     }
11733     /**
11734      * The Element object
11735      * @type Roo.Element
11736      */
11737     this.el = el;
11738     /**
11739      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11740      * @type String
11741      */
11742     this.defaultUrl = null;
11743
11744     this.addEvents({
11745         /**
11746          * @event beforeupdate
11747          * Fired before an update is made, return false from your handler and the update is cancelled.
11748          * @param {Roo.Element} el
11749          * @param {String/Object/Function} url
11750          * @param {String/Object} params
11751          */
11752         "beforeupdate": true,
11753         /**
11754          * @event update
11755          * Fired after successful update is made.
11756          * @param {Roo.Element} el
11757          * @param {Object} oResponseObject The response Object
11758          */
11759         "update": true,
11760         /**
11761          * @event failure
11762          * Fired on update failure.
11763          * @param {Roo.Element} el
11764          * @param {Object} oResponseObject The response Object
11765          */
11766         "failure": true
11767     });
11768     var d = Roo.UpdateManager.defaults;
11769     /**
11770      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11771      * @type String
11772      */
11773     this.sslBlankUrl = d.sslBlankUrl;
11774     /**
11775      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11776      * @type Boolean
11777      */
11778     this.disableCaching = d.disableCaching;
11779     /**
11780      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11781      * @type String
11782      */
11783     this.indicatorText = d.indicatorText;
11784     /**
11785      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11786      * @type String
11787      */
11788     this.showLoadIndicator = d.showLoadIndicator;
11789     /**
11790      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11791      * @type Number
11792      */
11793     this.timeout = d.timeout;
11794
11795     /**
11796      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11797      * @type Boolean
11798      */
11799     this.loadScripts = d.loadScripts;
11800
11801     /**
11802      * Transaction object of current executing transaction
11803      */
11804     this.transaction = null;
11805
11806     /**
11807      * @private
11808      */
11809     this.autoRefreshProcId = null;
11810     /**
11811      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11812      * @type Function
11813      */
11814     this.refreshDelegate = this.refresh.createDelegate(this);
11815     /**
11816      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11817      * @type Function
11818      */
11819     this.updateDelegate = this.update.createDelegate(this);
11820     /**
11821      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11822      * @type Function
11823      */
11824     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11825     /**
11826      * @private
11827      */
11828     this.successDelegate = this.processSuccess.createDelegate(this);
11829     /**
11830      * @private
11831      */
11832     this.failureDelegate = this.processFailure.createDelegate(this);
11833
11834     if(!this.renderer){
11835      /**
11836       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11837       */
11838     this.renderer = new Roo.UpdateManager.BasicRenderer();
11839     }
11840     
11841     Roo.UpdateManager.superclass.constructor.call(this);
11842 };
11843
11844 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11845     /**
11846      * Get the Element this UpdateManager is bound to
11847      * @return {Roo.Element} The element
11848      */
11849     getEl : function(){
11850         return this.el;
11851     },
11852     /**
11853      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11854      * @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:
11855 <pre><code>
11856 um.update({<br/>
11857     url: "your-url.php",<br/>
11858     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11859     callback: yourFunction,<br/>
11860     scope: yourObject, //(optional scope)  <br/>
11861     discardUrl: false, <br/>
11862     nocache: false,<br/>
11863     text: "Loading...",<br/>
11864     timeout: 30,<br/>
11865     scripts: false<br/>
11866 });
11867 </code></pre>
11868      * The only required property is url. The optional properties nocache, text and scripts
11869      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11870      * @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}
11871      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11872      * @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.
11873      */
11874     update : function(url, params, callback, discardUrl){
11875         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11876             var method = this.method,
11877                 cfg;
11878             if(typeof url == "object"){ // must be config object
11879                 cfg = url;
11880                 url = cfg.url;
11881                 params = params || cfg.params;
11882                 callback = callback || cfg.callback;
11883                 discardUrl = discardUrl || cfg.discardUrl;
11884                 if(callback && cfg.scope){
11885                     callback = callback.createDelegate(cfg.scope);
11886                 }
11887                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11888                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11889                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11890                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11891                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11892             }
11893             this.showLoading();
11894             if(!discardUrl){
11895                 this.defaultUrl = url;
11896             }
11897             if(typeof url == "function"){
11898                 url = url.call(this);
11899             }
11900
11901             method = method || (params ? "POST" : "GET");
11902             if(method == "GET"){
11903                 url = this.prepareUrl(url);
11904             }
11905
11906             var o = Roo.apply(cfg ||{}, {
11907                 url : url,
11908                 params: params,
11909                 success: this.successDelegate,
11910                 failure: this.failureDelegate,
11911                 callback: undefined,
11912                 timeout: (this.timeout*1000),
11913                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11914             });
11915             Roo.log("updated manager called with timeout of " + o.timeout);
11916             this.transaction = Roo.Ajax.request(o);
11917         }
11918     },
11919
11920     /**
11921      * 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.
11922      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11923      * @param {String/HTMLElement} form The form Id or form element
11924      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11925      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11926      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11927      */
11928     formUpdate : function(form, url, reset, callback){
11929         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11930             if(typeof url == "function"){
11931                 url = url.call(this);
11932             }
11933             form = Roo.getDom(form);
11934             this.transaction = Roo.Ajax.request({
11935                 form: form,
11936                 url:url,
11937                 success: this.successDelegate,
11938                 failure: this.failureDelegate,
11939                 timeout: (this.timeout*1000),
11940                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11941             });
11942             this.showLoading.defer(1, this);
11943         }
11944     },
11945
11946     /**
11947      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11948      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11949      */
11950     refresh : function(callback){
11951         if(this.defaultUrl == null){
11952             return;
11953         }
11954         this.update(this.defaultUrl, null, callback, true);
11955     },
11956
11957     /**
11958      * Set this element to auto refresh.
11959      * @param {Number} interval How often to update (in seconds).
11960      * @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)
11961      * @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}
11962      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11963      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11964      */
11965     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11966         if(refreshNow){
11967             this.update(url || this.defaultUrl, params, callback, true);
11968         }
11969         if(this.autoRefreshProcId){
11970             clearInterval(this.autoRefreshProcId);
11971         }
11972         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11973     },
11974
11975     /**
11976      * Stop auto refresh on this element.
11977      */
11978      stopAutoRefresh : function(){
11979         if(this.autoRefreshProcId){
11980             clearInterval(this.autoRefreshProcId);
11981             delete this.autoRefreshProcId;
11982         }
11983     },
11984
11985     isAutoRefreshing : function(){
11986        return this.autoRefreshProcId ? true : false;
11987     },
11988     /**
11989      * Called to update the element to "Loading" state. Override to perform custom action.
11990      */
11991     showLoading : function(){
11992         if(this.showLoadIndicator){
11993             this.el.update(this.indicatorText);
11994         }
11995     },
11996
11997     /**
11998      * Adds unique parameter to query string if disableCaching = true
11999      * @private
12000      */
12001     prepareUrl : function(url){
12002         if(this.disableCaching){
12003             var append = "_dc=" + (new Date().getTime());
12004             if(url.indexOf("?") !== -1){
12005                 url += "&" + append;
12006             }else{
12007                 url += "?" + append;
12008             }
12009         }
12010         return url;
12011     },
12012
12013     /**
12014      * @private
12015      */
12016     processSuccess : function(response){
12017         this.transaction = null;
12018         if(response.argument.form && response.argument.reset){
12019             try{ // put in try/catch since some older FF releases had problems with this
12020                 response.argument.form.reset();
12021             }catch(e){}
12022         }
12023         if(this.loadScripts){
12024             this.renderer.render(this.el, response, this,
12025                 this.updateComplete.createDelegate(this, [response]));
12026         }else{
12027             this.renderer.render(this.el, response, this);
12028             this.updateComplete(response);
12029         }
12030     },
12031
12032     updateComplete : function(response){
12033         this.fireEvent("update", this.el, response);
12034         if(typeof response.argument.callback == "function"){
12035             response.argument.callback(this.el, true, response);
12036         }
12037     },
12038
12039     /**
12040      * @private
12041      */
12042     processFailure : function(response){
12043         this.transaction = null;
12044         this.fireEvent("failure", this.el, response);
12045         if(typeof response.argument.callback == "function"){
12046             response.argument.callback(this.el, false, response);
12047         }
12048     },
12049
12050     /**
12051      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12052      * @param {Object} renderer The object implementing the render() method
12053      */
12054     setRenderer : function(renderer){
12055         this.renderer = renderer;
12056     },
12057
12058     getRenderer : function(){
12059        return this.renderer;
12060     },
12061
12062     /**
12063      * Set the defaultUrl used for updates
12064      * @param {String/Function} defaultUrl The url or a function to call to get the url
12065      */
12066     setDefaultUrl : function(defaultUrl){
12067         this.defaultUrl = defaultUrl;
12068     },
12069
12070     /**
12071      * Aborts the executing transaction
12072      */
12073     abort : function(){
12074         if(this.transaction){
12075             Roo.Ajax.abort(this.transaction);
12076         }
12077     },
12078
12079     /**
12080      * Returns true if an update is in progress
12081      * @return {Boolean}
12082      */
12083     isUpdating : function(){
12084         if(this.transaction){
12085             return Roo.Ajax.isLoading(this.transaction);
12086         }
12087         return false;
12088     }
12089 });
12090
12091 /**
12092  * @class Roo.UpdateManager.defaults
12093  * @static (not really - but it helps the doc tool)
12094  * The defaults collection enables customizing the default properties of UpdateManager
12095  */
12096    Roo.UpdateManager.defaults = {
12097        /**
12098          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12099          * @type Number
12100          */
12101          timeout : 30,
12102
12103          /**
12104          * True to process scripts by default (Defaults to false).
12105          * @type Boolean
12106          */
12107         loadScripts : false,
12108
12109         /**
12110         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12111         * @type String
12112         */
12113         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12114         /**
12115          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12116          * @type Boolean
12117          */
12118         disableCaching : false,
12119         /**
12120          * Whether to show indicatorText when loading (Defaults to true).
12121          * @type Boolean
12122          */
12123         showLoadIndicator : true,
12124         /**
12125          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12126          * @type String
12127          */
12128         indicatorText : '<div class="loading-indicator">Loading...</div>'
12129    };
12130
12131 /**
12132  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12133  *Usage:
12134  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12135  * @param {String/HTMLElement/Roo.Element} el The element to update
12136  * @param {String} url The url
12137  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12138  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12139  * @static
12140  * @deprecated
12141  * @member Roo.UpdateManager
12142  */
12143 Roo.UpdateManager.updateElement = function(el, url, params, options){
12144     var um = Roo.get(el, true).getUpdateManager();
12145     Roo.apply(um, options);
12146     um.update(url, params, options ? options.callback : null);
12147 };
12148 // alias for backwards compat
12149 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12150 /**
12151  * @class Roo.UpdateManager.BasicRenderer
12152  * Default Content renderer. Updates the elements innerHTML with the responseText.
12153  */
12154 Roo.UpdateManager.BasicRenderer = function(){};
12155
12156 Roo.UpdateManager.BasicRenderer.prototype = {
12157     /**
12158      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12159      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12160      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12161      * @param {Roo.Element} el The element being rendered
12162      * @param {Object} response The YUI Connect response object
12163      * @param {UpdateManager} updateManager The calling update manager
12164      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12165      */
12166      render : function(el, response, updateManager, callback){
12167         el.update(response.responseText, updateManager.loadScripts, callback);
12168     }
12169 };
12170 /*
12171  * Based on:
12172  * Roo JS
12173  * (c)) Alan Knowles
12174  * Licence : LGPL
12175  */
12176
12177
12178 /**
12179  * @class Roo.DomTemplate
12180  * @extends Roo.Template
12181  * An effort at a dom based template engine..
12182  *
12183  * Similar to XTemplate, except it uses dom parsing to create the template..
12184  *
12185  * Supported features:
12186  *
12187  *  Tags:
12188
12189 <pre><code>
12190       {a_variable} - output encoded.
12191       {a_variable.format:("Y-m-d")} - call a method on the variable
12192       {a_variable:raw} - unencoded output
12193       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12194       {a_variable:this.method_on_template(...)} - call a method on the template object.
12195  
12196 </code></pre>
12197  *  The tpl tag:
12198 <pre><code>
12199         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12200         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12201         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12202         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12203   
12204 </code></pre>
12205  *      
12206  */
12207 Roo.DomTemplate = function()
12208 {
12209      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12210      if (this.html) {
12211         this.compile();
12212      }
12213 };
12214
12215
12216 Roo.extend(Roo.DomTemplate, Roo.Template, {
12217     /**
12218      * id counter for sub templates.
12219      */
12220     id : 0,
12221     /**
12222      * flag to indicate if dom parser is inside a pre,
12223      * it will strip whitespace if not.
12224      */
12225     inPre : false,
12226     
12227     /**
12228      * The various sub templates
12229      */
12230     tpls : false,
12231     
12232     
12233     
12234     /**
12235      *
12236      * basic tag replacing syntax
12237      * WORD:WORD()
12238      *
12239      * // you can fake an object call by doing this
12240      *  x.t:(test,tesT) 
12241      * 
12242      */
12243     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12244     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12245     
12246     iterChild : function (node, method) {
12247         
12248         var oldPre = this.inPre;
12249         if (node.tagName == 'PRE') {
12250             this.inPre = true;
12251         }
12252         for( var i = 0; i < node.childNodes.length; i++) {
12253             method.call(this, node.childNodes[i]);
12254         }
12255         this.inPre = oldPre;
12256     },
12257     
12258     
12259     
12260     /**
12261      * compile the template
12262      *
12263      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12264      *
12265      */
12266     compile: function()
12267     {
12268         var s = this.html;
12269         
12270         // covert the html into DOM...
12271         var doc = false;
12272         var div =false;
12273         try {
12274             doc = document.implementation.createHTMLDocument("");
12275             doc.documentElement.innerHTML =   this.html  ;
12276             div = doc.documentElement;
12277         } catch (e) {
12278             // old IE... - nasty -- it causes all sorts of issues.. with
12279             // images getting pulled from server..
12280             div = document.createElement('div');
12281             div.innerHTML = this.html;
12282         }
12283         //doc.documentElement.innerHTML = htmlBody
12284          
12285         
12286         
12287         this.tpls = [];
12288         var _t = this;
12289         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12290         
12291         var tpls = this.tpls;
12292         
12293         // create a top level template from the snippet..
12294         
12295         //Roo.log(div.innerHTML);
12296         
12297         var tpl = {
12298             uid : 'master',
12299             id : this.id++,
12300             attr : false,
12301             value : false,
12302             body : div.innerHTML,
12303             
12304             forCall : false,
12305             execCall : false,
12306             dom : div,
12307             isTop : true
12308             
12309         };
12310         tpls.unshift(tpl);
12311         
12312         
12313         // compile them...
12314         this.tpls = [];
12315         Roo.each(tpls, function(tp){
12316             this.compileTpl(tp);
12317             this.tpls[tp.id] = tp;
12318         }, this);
12319         
12320         this.master = tpls[0];
12321         return this;
12322         
12323         
12324     },
12325     
12326     compileNode : function(node, istop) {
12327         // test for
12328         //Roo.log(node);
12329         
12330         
12331         // skip anything not a tag..
12332         if (node.nodeType != 1) {
12333             if (node.nodeType == 3 && !this.inPre) {
12334                 // reduce white space..
12335                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12336                 
12337             }
12338             return;
12339         }
12340         
12341         var tpl = {
12342             uid : false,
12343             id : false,
12344             attr : false,
12345             value : false,
12346             body : '',
12347             
12348             forCall : false,
12349             execCall : false,
12350             dom : false,
12351             isTop : istop
12352             
12353             
12354         };
12355         
12356         
12357         switch(true) {
12358             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12359             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12360             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12361             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12362             // no default..
12363         }
12364         
12365         
12366         if (!tpl.attr) {
12367             // just itterate children..
12368             this.iterChild(node,this.compileNode);
12369             return;
12370         }
12371         tpl.uid = this.id++;
12372         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12373         node.removeAttribute('roo-'+ tpl.attr);
12374         if (tpl.attr != 'name') {
12375             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12376             node.parentNode.replaceChild(placeholder,  node);
12377         } else {
12378             
12379             var placeholder =  document.createElement('span');
12380             placeholder.className = 'roo-tpl-' + tpl.value;
12381             node.parentNode.replaceChild(placeholder,  node);
12382         }
12383         
12384         // parent now sees '{domtplXXXX}
12385         this.iterChild(node,this.compileNode);
12386         
12387         // we should now have node body...
12388         var div = document.createElement('div');
12389         div.appendChild(node);
12390         tpl.dom = node;
12391         // this has the unfortunate side effect of converting tagged attributes
12392         // eg. href="{...}" into %7C...%7D
12393         // this has been fixed by searching for those combo's although it's a bit hacky..
12394         
12395         
12396         tpl.body = div.innerHTML;
12397         
12398         
12399          
12400         tpl.id = tpl.uid;
12401         switch(tpl.attr) {
12402             case 'for' :
12403                 switch (tpl.value) {
12404                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12405                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12406                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12407                 }
12408                 break;
12409             
12410             case 'exec':
12411                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12412                 break;
12413             
12414             case 'if':     
12415                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12416                 break;
12417             
12418             case 'name':
12419                 tpl.id  = tpl.value; // replace non characters???
12420                 break;
12421             
12422         }
12423         
12424         
12425         this.tpls.push(tpl);
12426         
12427         
12428         
12429     },
12430     
12431     
12432     
12433     
12434     /**
12435      * Compile a segment of the template into a 'sub-template'
12436      *
12437      * 
12438      * 
12439      *
12440      */
12441     compileTpl : function(tpl)
12442     {
12443         var fm = Roo.util.Format;
12444         var useF = this.disableFormats !== true;
12445         
12446         var sep = Roo.isGecko ? "+\n" : ",\n";
12447         
12448         var undef = function(str) {
12449             Roo.debug && Roo.log("Property not found :"  + str);
12450             return '';
12451         };
12452           
12453         //Roo.log(tpl.body);
12454         
12455         
12456         
12457         var fn = function(m, lbrace, name, format, args)
12458         {
12459             //Roo.log("ARGS");
12460             //Roo.log(arguments);
12461             args = args ? args.replace(/\\'/g,"'") : args;
12462             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12463             if (typeof(format) == 'undefined') {
12464                 format =  'htmlEncode'; 
12465             }
12466             if (format == 'raw' ) {
12467                 format = false;
12468             }
12469             
12470             if(name.substr(0, 6) == 'domtpl'){
12471                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12472             }
12473             
12474             // build an array of options to determine if value is undefined..
12475             
12476             // basically get 'xxxx.yyyy' then do
12477             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12478             //    (function () { Roo.log("Property not found"); return ''; })() :
12479             //    ......
12480             
12481             var udef_ar = [];
12482             var lookfor = '';
12483             Roo.each(name.split('.'), function(st) {
12484                 lookfor += (lookfor.length ? '.': '') + st;
12485                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12486             });
12487             
12488             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12489             
12490             
12491             if(format && useF){
12492                 
12493                 args = args ? ',' + args : "";
12494                  
12495                 if(format.substr(0, 5) != "this."){
12496                     format = "fm." + format + '(';
12497                 }else{
12498                     format = 'this.call("'+ format.substr(5) + '", ';
12499                     args = ", values";
12500                 }
12501                 
12502                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12503             }
12504              
12505             if (args && args.length) {
12506                 // called with xxyx.yuu:(test,test)
12507                 // change to ()
12508                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12509             }
12510             // raw.. - :raw modifier..
12511             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12512             
12513         };
12514         var body;
12515         // branched to use + in gecko and [].join() in others
12516         if(Roo.isGecko){
12517             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12518                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12519                     "';};};";
12520         }else{
12521             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12522             body.push(tpl.body.replace(/(\r\n|\n)/g,
12523                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12524             body.push("'].join('');};};");
12525             body = body.join('');
12526         }
12527         
12528         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12529        
12530         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12531         eval(body);
12532         
12533         return this;
12534     },
12535      
12536     /**
12537      * same as applyTemplate, except it's done to one of the subTemplates
12538      * when using named templates, you can do:
12539      *
12540      * var str = pl.applySubTemplate('your-name', values);
12541      *
12542      * 
12543      * @param {Number} id of the template
12544      * @param {Object} values to apply to template
12545      * @param {Object} parent (normaly the instance of this object)
12546      */
12547     applySubTemplate : function(id, values, parent)
12548     {
12549         
12550         
12551         var t = this.tpls[id];
12552         
12553         
12554         try { 
12555             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12556                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12557                 return '';
12558             }
12559         } catch(e) {
12560             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12561             Roo.log(values);
12562           
12563             return '';
12564         }
12565         try { 
12566             
12567             if(t.execCall && t.execCall.call(this, values, parent)){
12568                 return '';
12569             }
12570         } catch(e) {
12571             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12572             Roo.log(values);
12573             return '';
12574         }
12575         
12576         try {
12577             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12578             parent = t.target ? values : parent;
12579             if(t.forCall && vs instanceof Array){
12580                 var buf = [];
12581                 for(var i = 0, len = vs.length; i < len; i++){
12582                     try {
12583                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12584                     } catch (e) {
12585                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12586                         Roo.log(e.body);
12587                         //Roo.log(t.compiled);
12588                         Roo.log(vs[i]);
12589                     }   
12590                 }
12591                 return buf.join('');
12592             }
12593         } catch (e) {
12594             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12595             Roo.log(values);
12596             return '';
12597         }
12598         try {
12599             return t.compiled.call(this, vs, parent);
12600         } catch (e) {
12601             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12602             Roo.log(e.body);
12603             //Roo.log(t.compiled);
12604             Roo.log(values);
12605             return '';
12606         }
12607     },
12608
12609    
12610
12611     applyTemplate : function(values){
12612         return this.master.compiled.call(this, values, {});
12613         //var s = this.subs;
12614     },
12615
12616     apply : function(){
12617         return this.applyTemplate.apply(this, arguments);
12618     }
12619
12620  });
12621
12622 Roo.DomTemplate.from = function(el){
12623     el = Roo.getDom(el);
12624     return new Roo.Domtemplate(el.value || el.innerHTML);
12625 };/*
12626  * Based on:
12627  * Ext JS Library 1.1.1
12628  * Copyright(c) 2006-2007, Ext JS, LLC.
12629  *
12630  * Originally Released Under LGPL - original licence link has changed is not relivant.
12631  *
12632  * Fork - LGPL
12633  * <script type="text/javascript">
12634  */
12635
12636 /**
12637  * @class Roo.util.DelayedTask
12638  * Provides a convenient method of performing setTimeout where a new
12639  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12640  * You can use this class to buffer
12641  * the keypress events for a certain number of milliseconds, and perform only if they stop
12642  * for that amount of time.
12643  * @constructor The parameters to this constructor serve as defaults and are not required.
12644  * @param {Function} fn (optional) The default function to timeout
12645  * @param {Object} scope (optional) The default scope of that timeout
12646  * @param {Array} args (optional) The default Array of arguments
12647  */
12648 Roo.util.DelayedTask = function(fn, scope, args){
12649     var id = null, d, t;
12650
12651     var call = function(){
12652         var now = new Date().getTime();
12653         if(now - t >= d){
12654             clearInterval(id);
12655             id = null;
12656             fn.apply(scope, args || []);
12657         }
12658     };
12659     /**
12660      * Cancels any pending timeout and queues a new one
12661      * @param {Number} delay The milliseconds to delay
12662      * @param {Function} newFn (optional) Overrides function passed to constructor
12663      * @param {Object} newScope (optional) Overrides scope passed to constructor
12664      * @param {Array} newArgs (optional) Overrides args passed to constructor
12665      */
12666     this.delay = function(delay, newFn, newScope, newArgs){
12667         if(id && delay != d){
12668             this.cancel();
12669         }
12670         d = delay;
12671         t = new Date().getTime();
12672         fn = newFn || fn;
12673         scope = newScope || scope;
12674         args = newArgs || args;
12675         if(!id){
12676             id = setInterval(call, d);
12677         }
12678     };
12679
12680     /**
12681      * Cancel the last queued timeout
12682      */
12683     this.cancel = function(){
12684         if(id){
12685             clearInterval(id);
12686             id = null;
12687         }
12688     };
12689 };/*
12690  * Based on:
12691  * Ext JS Library 1.1.1
12692  * Copyright(c) 2006-2007, Ext JS, LLC.
12693  *
12694  * Originally Released Under LGPL - original licence link has changed is not relivant.
12695  *
12696  * Fork - LGPL
12697  * <script type="text/javascript">
12698  */
12699  
12700  
12701 Roo.util.TaskRunner = function(interval){
12702     interval = interval || 10;
12703     var tasks = [], removeQueue = [];
12704     var id = 0;
12705     var running = false;
12706
12707     var stopThread = function(){
12708         running = false;
12709         clearInterval(id);
12710         id = 0;
12711     };
12712
12713     var startThread = function(){
12714         if(!running){
12715             running = true;
12716             id = setInterval(runTasks, interval);
12717         }
12718     };
12719
12720     var removeTask = function(task){
12721         removeQueue.push(task);
12722         if(task.onStop){
12723             task.onStop();
12724         }
12725     };
12726
12727     var runTasks = function(){
12728         if(removeQueue.length > 0){
12729             for(var i = 0, len = removeQueue.length; i < len; i++){
12730                 tasks.remove(removeQueue[i]);
12731             }
12732             removeQueue = [];
12733             if(tasks.length < 1){
12734                 stopThread();
12735                 return;
12736             }
12737         }
12738         var now = new Date().getTime();
12739         for(var i = 0, len = tasks.length; i < len; ++i){
12740             var t = tasks[i];
12741             var itime = now - t.taskRunTime;
12742             if(t.interval <= itime){
12743                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12744                 t.taskRunTime = now;
12745                 if(rt === false || t.taskRunCount === t.repeat){
12746                     removeTask(t);
12747                     return;
12748                 }
12749             }
12750             if(t.duration && t.duration <= (now - t.taskStartTime)){
12751                 removeTask(t);
12752             }
12753         }
12754     };
12755
12756     /**
12757      * Queues a new task.
12758      * @param {Object} task
12759      */
12760     this.start = function(task){
12761         tasks.push(task);
12762         task.taskStartTime = new Date().getTime();
12763         task.taskRunTime = 0;
12764         task.taskRunCount = 0;
12765         startThread();
12766         return task;
12767     };
12768
12769     this.stop = function(task){
12770         removeTask(task);
12771         return task;
12772     };
12773
12774     this.stopAll = function(){
12775         stopThread();
12776         for(var i = 0, len = tasks.length; i < len; i++){
12777             if(tasks[i].onStop){
12778                 tasks[i].onStop();
12779             }
12780         }
12781         tasks = [];
12782         removeQueue = [];
12783     };
12784 };
12785
12786 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12787  * Based on:
12788  * Ext JS Library 1.1.1
12789  * Copyright(c) 2006-2007, Ext JS, LLC.
12790  *
12791  * Originally Released Under LGPL - original licence link has changed is not relivant.
12792  *
12793  * Fork - LGPL
12794  * <script type="text/javascript">
12795  */
12796
12797  
12798 /**
12799  * @class Roo.util.MixedCollection
12800  * @extends Roo.util.Observable
12801  * A Collection class that maintains both numeric indexes and keys and exposes events.
12802  * @constructor
12803  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12804  * collection (defaults to false)
12805  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12806  * and return the key value for that item.  This is used when available to look up the key on items that
12807  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12808  * equivalent to providing an implementation for the {@link #getKey} method.
12809  */
12810 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12811     this.items = [];
12812     this.map = {};
12813     this.keys = [];
12814     this.length = 0;
12815     this.addEvents({
12816         /**
12817          * @event clear
12818          * Fires when the collection is cleared.
12819          */
12820         "clear" : true,
12821         /**
12822          * @event add
12823          * Fires when an item is added to the collection.
12824          * @param {Number} index The index at which the item was added.
12825          * @param {Object} o The item added.
12826          * @param {String} key The key associated with the added item.
12827          */
12828         "add" : true,
12829         /**
12830          * @event replace
12831          * Fires when an item is replaced in the collection.
12832          * @param {String} key he key associated with the new added.
12833          * @param {Object} old The item being replaced.
12834          * @param {Object} new The new item.
12835          */
12836         "replace" : true,
12837         /**
12838          * @event remove
12839          * Fires when an item is removed from the collection.
12840          * @param {Object} o The item being removed.
12841          * @param {String} key (optional) The key associated with the removed item.
12842          */
12843         "remove" : true,
12844         "sort" : true
12845     });
12846     this.allowFunctions = allowFunctions === true;
12847     if(keyFn){
12848         this.getKey = keyFn;
12849     }
12850     Roo.util.MixedCollection.superclass.constructor.call(this);
12851 };
12852
12853 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12854     allowFunctions : false,
12855     
12856 /**
12857  * Adds an item to the collection.
12858  * @param {String} key The key to associate with the item
12859  * @param {Object} o The item to add.
12860  * @return {Object} The item added.
12861  */
12862     add : function(key, o){
12863         if(arguments.length == 1){
12864             o = arguments[0];
12865             key = this.getKey(o);
12866         }
12867         if(typeof key == "undefined" || key === null){
12868             this.length++;
12869             this.items.push(o);
12870             this.keys.push(null);
12871         }else{
12872             var old = this.map[key];
12873             if(old){
12874                 return this.replace(key, o);
12875             }
12876             this.length++;
12877             this.items.push(o);
12878             this.map[key] = o;
12879             this.keys.push(key);
12880         }
12881         this.fireEvent("add", this.length-1, o, key);
12882         return o;
12883     },
12884        
12885 /**
12886   * MixedCollection has a generic way to fetch keys if you implement getKey.
12887 <pre><code>
12888 // normal way
12889 var mc = new Roo.util.MixedCollection();
12890 mc.add(someEl.dom.id, someEl);
12891 mc.add(otherEl.dom.id, otherEl);
12892 //and so on
12893
12894 // using getKey
12895 var mc = new Roo.util.MixedCollection();
12896 mc.getKey = function(el){
12897    return el.dom.id;
12898 };
12899 mc.add(someEl);
12900 mc.add(otherEl);
12901
12902 // or via the constructor
12903 var mc = new Roo.util.MixedCollection(false, function(el){
12904    return el.dom.id;
12905 });
12906 mc.add(someEl);
12907 mc.add(otherEl);
12908 </code></pre>
12909  * @param o {Object} The item for which to find the key.
12910  * @return {Object} The key for the passed item.
12911  */
12912     getKey : function(o){
12913          return o.id; 
12914     },
12915    
12916 /**
12917  * Replaces an item in the collection.
12918  * @param {String} key The key associated with the item to replace, or the item to replace.
12919  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12920  * @return {Object}  The new item.
12921  */
12922     replace : function(key, o){
12923         if(arguments.length == 1){
12924             o = arguments[0];
12925             key = this.getKey(o);
12926         }
12927         var old = this.item(key);
12928         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12929              return this.add(key, o);
12930         }
12931         var index = this.indexOfKey(key);
12932         this.items[index] = o;
12933         this.map[key] = o;
12934         this.fireEvent("replace", key, old, o);
12935         return o;
12936     },
12937    
12938 /**
12939  * Adds all elements of an Array or an Object to the collection.
12940  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12941  * an Array of values, each of which are added to the collection.
12942  */
12943     addAll : function(objs){
12944         if(arguments.length > 1 || objs instanceof Array){
12945             var args = arguments.length > 1 ? arguments : objs;
12946             for(var i = 0, len = args.length; i < len; i++){
12947                 this.add(args[i]);
12948             }
12949         }else{
12950             for(var key in objs){
12951                 if(this.allowFunctions || typeof objs[key] != "function"){
12952                     this.add(key, objs[key]);
12953                 }
12954             }
12955         }
12956     },
12957    
12958 /**
12959  * Executes the specified function once for every item in the collection, passing each
12960  * item as the first and only parameter. returning false from the function will stop the iteration.
12961  * @param {Function} fn The function to execute for each item.
12962  * @param {Object} scope (optional) The scope in which to execute the function.
12963  */
12964     each : function(fn, scope){
12965         var items = [].concat(this.items); // each safe for removal
12966         for(var i = 0, len = items.length; i < len; i++){
12967             if(fn.call(scope || items[i], items[i], i, len) === false){
12968                 break;
12969             }
12970         }
12971     },
12972    
12973 /**
12974  * Executes the specified function once for every key in the collection, passing each
12975  * key, and its associated item as the first two parameters.
12976  * @param {Function} fn The function to execute for each item.
12977  * @param {Object} scope (optional) The scope in which to execute the function.
12978  */
12979     eachKey : function(fn, scope){
12980         for(var i = 0, len = this.keys.length; i < len; i++){
12981             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12982         }
12983     },
12984    
12985 /**
12986  * Returns the first item in the collection which elicits a true return value from the
12987  * passed selection function.
12988  * @param {Function} fn The selection function to execute for each item.
12989  * @param {Object} scope (optional) The scope in which to execute the function.
12990  * @return {Object} The first item in the collection which returned true from the selection function.
12991  */
12992     find : function(fn, scope){
12993         for(var i = 0, len = this.items.length; i < len; i++){
12994             if(fn.call(scope || window, this.items[i], this.keys[i])){
12995                 return this.items[i];
12996             }
12997         }
12998         return null;
12999     },
13000    
13001 /**
13002  * Inserts an item at the specified index in the collection.
13003  * @param {Number} index The index to insert the item at.
13004  * @param {String} key The key to associate with the new item, or the item itself.
13005  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13006  * @return {Object} The item inserted.
13007  */
13008     insert : function(index, key, o){
13009         if(arguments.length == 2){
13010             o = arguments[1];
13011             key = this.getKey(o);
13012         }
13013         if(index >= this.length){
13014             return this.add(key, o);
13015         }
13016         this.length++;
13017         this.items.splice(index, 0, o);
13018         if(typeof key != "undefined" && key != null){
13019             this.map[key] = o;
13020         }
13021         this.keys.splice(index, 0, key);
13022         this.fireEvent("add", index, o, key);
13023         return o;
13024     },
13025    
13026 /**
13027  * Removed an item from the collection.
13028  * @param {Object} o The item to remove.
13029  * @return {Object} The item removed.
13030  */
13031     remove : function(o){
13032         return this.removeAt(this.indexOf(o));
13033     },
13034    
13035 /**
13036  * Remove an item from a specified index in the collection.
13037  * @param {Number} index The index within the collection of the item to remove.
13038  */
13039     removeAt : function(index){
13040         if(index < this.length && index >= 0){
13041             this.length--;
13042             var o = this.items[index];
13043             this.items.splice(index, 1);
13044             var key = this.keys[index];
13045             if(typeof key != "undefined"){
13046                 delete this.map[key];
13047             }
13048             this.keys.splice(index, 1);
13049             this.fireEvent("remove", o, key);
13050         }
13051     },
13052    
13053 /**
13054  * Removed an item associated with the passed key fom the collection.
13055  * @param {String} key The key of the item to remove.
13056  */
13057     removeKey : function(key){
13058         return this.removeAt(this.indexOfKey(key));
13059     },
13060    
13061 /**
13062  * Returns the number of items in the collection.
13063  * @return {Number} the number of items in the collection.
13064  */
13065     getCount : function(){
13066         return this.length; 
13067     },
13068    
13069 /**
13070  * Returns index within the collection of the passed Object.
13071  * @param {Object} o The item to find the index of.
13072  * @return {Number} index of the item.
13073  */
13074     indexOf : function(o){
13075         if(!this.items.indexOf){
13076             for(var i = 0, len = this.items.length; i < len; i++){
13077                 if(this.items[i] == o) return i;
13078             }
13079             return -1;
13080         }else{
13081             return this.items.indexOf(o);
13082         }
13083     },
13084    
13085 /**
13086  * Returns index within the collection of the passed key.
13087  * @param {String} key The key to find the index of.
13088  * @return {Number} index of the key.
13089  */
13090     indexOfKey : function(key){
13091         if(!this.keys.indexOf){
13092             for(var i = 0, len = this.keys.length; i < len; i++){
13093                 if(this.keys[i] == key) return i;
13094             }
13095             return -1;
13096         }else{
13097             return this.keys.indexOf(key);
13098         }
13099     },
13100    
13101 /**
13102  * Returns the item associated with the passed key OR index. Key has priority over index.
13103  * @param {String/Number} key The key or index of the item.
13104  * @return {Object} The item associated with the passed key.
13105  */
13106     item : function(key){
13107         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13108         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13109     },
13110     
13111 /**
13112  * Returns the item at the specified index.
13113  * @param {Number} index The index of the item.
13114  * @return {Object}
13115  */
13116     itemAt : function(index){
13117         return this.items[index];
13118     },
13119     
13120 /**
13121  * Returns the item associated with the passed key.
13122  * @param {String/Number} key The key of the item.
13123  * @return {Object} The item associated with the passed key.
13124  */
13125     key : function(key){
13126         return this.map[key];
13127     },
13128    
13129 /**
13130  * Returns true if the collection contains the passed Object as an item.
13131  * @param {Object} o  The Object to look for in the collection.
13132  * @return {Boolean} True if the collection contains the Object as an item.
13133  */
13134     contains : function(o){
13135         return this.indexOf(o) != -1;
13136     },
13137    
13138 /**
13139  * Returns true if the collection contains the passed Object as a key.
13140  * @param {String} key The key to look for in the collection.
13141  * @return {Boolean} True if the collection contains the Object as a key.
13142  */
13143     containsKey : function(key){
13144         return typeof this.map[key] != "undefined";
13145     },
13146    
13147 /**
13148  * Removes all items from the collection.
13149  */
13150     clear : function(){
13151         this.length = 0;
13152         this.items = [];
13153         this.keys = [];
13154         this.map = {};
13155         this.fireEvent("clear");
13156     },
13157    
13158 /**
13159  * Returns the first item in the collection.
13160  * @return {Object} the first item in the collection..
13161  */
13162     first : function(){
13163         return this.items[0]; 
13164     },
13165    
13166 /**
13167  * Returns the last item in the collection.
13168  * @return {Object} the last item in the collection..
13169  */
13170     last : function(){
13171         return this.items[this.length-1];   
13172     },
13173     
13174     _sort : function(property, dir, fn){
13175         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13176         fn = fn || function(a, b){
13177             return a-b;
13178         };
13179         var c = [], k = this.keys, items = this.items;
13180         for(var i = 0, len = items.length; i < len; i++){
13181             c[c.length] = {key: k[i], value: items[i], index: i};
13182         }
13183         c.sort(function(a, b){
13184             var v = fn(a[property], b[property]) * dsc;
13185             if(v == 0){
13186                 v = (a.index < b.index ? -1 : 1);
13187             }
13188             return v;
13189         });
13190         for(var i = 0, len = c.length; i < len; i++){
13191             items[i] = c[i].value;
13192             k[i] = c[i].key;
13193         }
13194         this.fireEvent("sort", this);
13195     },
13196     
13197     /**
13198      * Sorts this collection with the passed comparison function
13199      * @param {String} direction (optional) "ASC" or "DESC"
13200      * @param {Function} fn (optional) comparison function
13201      */
13202     sort : function(dir, fn){
13203         this._sort("value", dir, fn);
13204     },
13205     
13206     /**
13207      * Sorts this collection by keys
13208      * @param {String} direction (optional) "ASC" or "DESC"
13209      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13210      */
13211     keySort : function(dir, fn){
13212         this._sort("key", dir, fn || function(a, b){
13213             return String(a).toUpperCase()-String(b).toUpperCase();
13214         });
13215     },
13216     
13217     /**
13218      * Returns a range of items in this collection
13219      * @param {Number} startIndex (optional) defaults to 0
13220      * @param {Number} endIndex (optional) default to the last item
13221      * @return {Array} An array of items
13222      */
13223     getRange : function(start, end){
13224         var items = this.items;
13225         if(items.length < 1){
13226             return [];
13227         }
13228         start = start || 0;
13229         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13230         var r = [];
13231         if(start <= end){
13232             for(var i = start; i <= end; i++) {
13233                     r[r.length] = items[i];
13234             }
13235         }else{
13236             for(var i = start; i >= end; i--) {
13237                     r[r.length] = items[i];
13238             }
13239         }
13240         return r;
13241     },
13242         
13243     /**
13244      * Filter the <i>objects</i> in this collection by a specific property. 
13245      * Returns a new collection that has been filtered.
13246      * @param {String} property A property on your objects
13247      * @param {String/RegExp} value Either string that the property values 
13248      * should start with or a RegExp to test against the property
13249      * @return {MixedCollection} The new filtered collection
13250      */
13251     filter : function(property, value){
13252         if(!value.exec){ // not a regex
13253             value = String(value);
13254             if(value.length == 0){
13255                 return this.clone();
13256             }
13257             value = new RegExp("^" + Roo.escapeRe(value), "i");
13258         }
13259         return this.filterBy(function(o){
13260             return o && value.test(o[property]);
13261         });
13262         },
13263     
13264     /**
13265      * Filter by a function. * Returns a new collection that has been filtered.
13266      * The passed function will be called with each 
13267      * object in the collection. If the function returns true, the value is included 
13268      * otherwise it is filtered.
13269      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13270      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13271      * @return {MixedCollection} The new filtered collection
13272      */
13273     filterBy : function(fn, scope){
13274         var r = new Roo.util.MixedCollection();
13275         r.getKey = this.getKey;
13276         var k = this.keys, it = this.items;
13277         for(var i = 0, len = it.length; i < len; i++){
13278             if(fn.call(scope||this, it[i], k[i])){
13279                                 r.add(k[i], it[i]);
13280                         }
13281         }
13282         return r;
13283     },
13284     
13285     /**
13286      * Creates a duplicate of this collection
13287      * @return {MixedCollection}
13288      */
13289     clone : function(){
13290         var r = new Roo.util.MixedCollection();
13291         var k = this.keys, it = this.items;
13292         for(var i = 0, len = it.length; i < len; i++){
13293             r.add(k[i], it[i]);
13294         }
13295         r.getKey = this.getKey;
13296         return r;
13297     }
13298 });
13299 /**
13300  * Returns the item associated with the passed key or index.
13301  * @method
13302  * @param {String/Number} key The key or index of the item.
13303  * @return {Object} The item associated with the passed key.
13304  */
13305 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13306  * Based on:
13307  * Ext JS Library 1.1.1
13308  * Copyright(c) 2006-2007, Ext JS, LLC.
13309  *
13310  * Originally Released Under LGPL - original licence link has changed is not relivant.
13311  *
13312  * Fork - LGPL
13313  * <script type="text/javascript">
13314  */
13315 /**
13316  * @class Roo.util.JSON
13317  * Modified version of Douglas Crockford"s json.js that doesn"t
13318  * mess with the Object prototype 
13319  * http://www.json.org/js.html
13320  * @singleton
13321  */
13322 Roo.util.JSON = new (function(){
13323     var useHasOwn = {}.hasOwnProperty ? true : false;
13324     
13325     // crashes Safari in some instances
13326     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13327     
13328     var pad = function(n) {
13329         return n < 10 ? "0" + n : n;
13330     };
13331     
13332     var m = {
13333         "\b": '\\b',
13334         "\t": '\\t',
13335         "\n": '\\n',
13336         "\f": '\\f',
13337         "\r": '\\r',
13338         '"' : '\\"',
13339         "\\": '\\\\'
13340     };
13341
13342     var encodeString = function(s){
13343         if (/["\\\x00-\x1f]/.test(s)) {
13344             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13345                 var c = m[b];
13346                 if(c){
13347                     return c;
13348                 }
13349                 c = b.charCodeAt();
13350                 return "\\u00" +
13351                     Math.floor(c / 16).toString(16) +
13352                     (c % 16).toString(16);
13353             }) + '"';
13354         }
13355         return '"' + s + '"';
13356     };
13357     
13358     var encodeArray = function(o){
13359         var a = ["["], b, i, l = o.length, v;
13360             for (i = 0; i < l; i += 1) {
13361                 v = o[i];
13362                 switch (typeof v) {
13363                     case "undefined":
13364                     case "function":
13365                     case "unknown":
13366                         break;
13367                     default:
13368                         if (b) {
13369                             a.push(',');
13370                         }
13371                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13372                         b = true;
13373                 }
13374             }
13375             a.push("]");
13376             return a.join("");
13377     };
13378     
13379     var encodeDate = function(o){
13380         return '"' + o.getFullYear() + "-" +
13381                 pad(o.getMonth() + 1) + "-" +
13382                 pad(o.getDate()) + "T" +
13383                 pad(o.getHours()) + ":" +
13384                 pad(o.getMinutes()) + ":" +
13385                 pad(o.getSeconds()) + '"';
13386     };
13387     
13388     /**
13389      * Encodes an Object, Array or other value
13390      * @param {Mixed} o The variable to encode
13391      * @return {String} The JSON string
13392      */
13393     this.encode = function(o)
13394     {
13395         // should this be extended to fully wrap stringify..
13396         
13397         if(typeof o == "undefined" || o === null){
13398             return "null";
13399         }else if(o instanceof Array){
13400             return encodeArray(o);
13401         }else if(o instanceof Date){
13402             return encodeDate(o);
13403         }else if(typeof o == "string"){
13404             return encodeString(o);
13405         }else if(typeof o == "number"){
13406             return isFinite(o) ? String(o) : "null";
13407         }else if(typeof o == "boolean"){
13408             return String(o);
13409         }else {
13410             var a = ["{"], b, i, v;
13411             for (i in o) {
13412                 if(!useHasOwn || o.hasOwnProperty(i)) {
13413                     v = o[i];
13414                     switch (typeof v) {
13415                     case "undefined":
13416                     case "function":
13417                     case "unknown":
13418                         break;
13419                     default:
13420                         if(b){
13421                             a.push(',');
13422                         }
13423                         a.push(this.encode(i), ":",
13424                                 v === null ? "null" : this.encode(v));
13425                         b = true;
13426                     }
13427                 }
13428             }
13429             a.push("}");
13430             return a.join("");
13431         }
13432     };
13433     
13434     /**
13435      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13436      * @param {String} json The JSON string
13437      * @return {Object} The resulting object
13438      */
13439     this.decode = function(json){
13440         
13441         return  /** eval:var:json */ eval("(" + json + ')');
13442     };
13443 })();
13444 /** 
13445  * Shorthand for {@link Roo.util.JSON#encode}
13446  * @member Roo encode 
13447  * @method */
13448 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13449 /** 
13450  * Shorthand for {@link Roo.util.JSON#decode}
13451  * @member Roo decode 
13452  * @method */
13453 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13454 /*
13455  * Based on:
13456  * Ext JS Library 1.1.1
13457  * Copyright(c) 2006-2007, Ext JS, LLC.
13458  *
13459  * Originally Released Under LGPL - original licence link has changed is not relivant.
13460  *
13461  * Fork - LGPL
13462  * <script type="text/javascript">
13463  */
13464  
13465 /**
13466  * @class Roo.util.Format
13467  * Reusable data formatting functions
13468  * @singleton
13469  */
13470 Roo.util.Format = function(){
13471     var trimRe = /^\s+|\s+$/g;
13472     return {
13473         /**
13474          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13475          * @param {String} value The string to truncate
13476          * @param {Number} length The maximum length to allow before truncating
13477          * @return {String} The converted text
13478          */
13479         ellipsis : function(value, len){
13480             if(value && value.length > len){
13481                 return value.substr(0, len-3)+"...";
13482             }
13483             return value;
13484         },
13485
13486         /**
13487          * Checks a reference and converts it to empty string if it is undefined
13488          * @param {Mixed} value Reference to check
13489          * @return {Mixed} Empty string if converted, otherwise the original value
13490          */
13491         undef : function(value){
13492             return typeof value != "undefined" ? value : "";
13493         },
13494
13495         /**
13496          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13497          * @param {String} value The string to encode
13498          * @return {String} The encoded text
13499          */
13500         htmlEncode : function(value){
13501             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13502         },
13503
13504         /**
13505          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13506          * @param {String} value The string to decode
13507          * @return {String} The decoded text
13508          */
13509         htmlDecode : function(value){
13510             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13511         },
13512
13513         /**
13514          * Trims any whitespace from either side of a string
13515          * @param {String} value The text to trim
13516          * @return {String} The trimmed text
13517          */
13518         trim : function(value){
13519             return String(value).replace(trimRe, "");
13520         },
13521
13522         /**
13523          * Returns a substring from within an original string
13524          * @param {String} value The original text
13525          * @param {Number} start The start index of the substring
13526          * @param {Number} length The length of the substring
13527          * @return {String} The substring
13528          */
13529         substr : function(value, start, length){
13530             return String(value).substr(start, length);
13531         },
13532
13533         /**
13534          * Converts a string to all lower case letters
13535          * @param {String} value The text to convert
13536          * @return {String} The converted text
13537          */
13538         lowercase : function(value){
13539             return String(value).toLowerCase();
13540         },
13541
13542         /**
13543          * Converts a string to all upper case letters
13544          * @param {String} value The text to convert
13545          * @return {String} The converted text
13546          */
13547         uppercase : function(value){
13548             return String(value).toUpperCase();
13549         },
13550
13551         /**
13552          * Converts the first character only of a string to upper case
13553          * @param {String} value The text to convert
13554          * @return {String} The converted text
13555          */
13556         capitalize : function(value){
13557             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13558         },
13559
13560         // private
13561         call : function(value, fn){
13562             if(arguments.length > 2){
13563                 var args = Array.prototype.slice.call(arguments, 2);
13564                 args.unshift(value);
13565                  
13566                 return /** eval:var:value */  eval(fn).apply(window, args);
13567             }else{
13568                 /** eval:var:value */
13569                 return /** eval:var:value */ eval(fn).call(window, value);
13570             }
13571         },
13572
13573        
13574         /**
13575          * safer version of Math.toFixed..??/
13576          * @param {Number/String} value The numeric value to format
13577          * @param {Number/String} value Decimal places 
13578          * @return {String} The formatted currency string
13579          */
13580         toFixed : function(v, n)
13581         {
13582             // why not use to fixed - precision is buggered???
13583             if (!n) {
13584                 return Math.round(v-0);
13585             }
13586             var fact = Math.pow(10,n+1);
13587             v = (Math.round((v-0)*fact))/fact;
13588             var z = (''+fact).substring(2);
13589             if (v == Math.floor(v)) {
13590                 return Math.floor(v) + '.' + z;
13591             }
13592             
13593             // now just padd decimals..
13594             var ps = String(v).split('.');
13595             var fd = (ps[1] + z);
13596             var r = fd.substring(0,n); 
13597             var rm = fd.substring(n); 
13598             if (rm < 5) {
13599                 return ps[0] + '.' + r;
13600             }
13601             r*=1; // turn it into a number;
13602             r++;
13603             if (String(r).length != n) {
13604                 ps[0]*=1;
13605                 ps[0]++;
13606                 r = String(r).substring(1); // chop the end off.
13607             }
13608             
13609             return ps[0] + '.' + r;
13610              
13611         },
13612         
13613         /**
13614          * Format a number as US currency
13615          * @param {Number/String} value The numeric value to format
13616          * @return {String} The formatted currency string
13617          */
13618         usMoney : function(v){
13619             return '$' + Roo.util.Format.number(v);
13620         },
13621         
13622         /**
13623          * Format a number
13624          * eventually this should probably emulate php's number_format
13625          * @param {Number/String} value The numeric value to format
13626          * @param {Number} decimals number of decimal places
13627          * @return {String} The formatted currency string
13628          */
13629         number : function(v,decimals)
13630         {
13631             // multiply and round.
13632             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13633             var mul = Math.pow(10, decimals);
13634             var zero = String(mul).substring(1);
13635             v = (Math.round((v-0)*mul))/mul;
13636             
13637             // if it's '0' number.. then
13638             
13639             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13640             v = String(v);
13641             var ps = v.split('.');
13642             var whole = ps[0];
13643             
13644             
13645             var r = /(\d+)(\d{3})/;
13646             // add comma's
13647             while (r.test(whole)) {
13648                 whole = whole.replace(r, '$1' + ',' + '$2');
13649             }
13650             
13651             
13652             var sub = ps[1] ?
13653                     // has decimals..
13654                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13655                     // does not have decimals
13656                     (decimals ? ('.' + zero) : '');
13657             
13658             
13659             return whole + sub ;
13660         },
13661         
13662         /**
13663          * Parse a value into a formatted date using the specified format pattern.
13664          * @param {Mixed} value The value to format
13665          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13666          * @return {String} The formatted date string
13667          */
13668         date : function(v, format){
13669             if(!v){
13670                 return "";
13671             }
13672             if(!(v instanceof Date)){
13673                 v = new Date(Date.parse(v));
13674             }
13675             return v.dateFormat(format || Roo.util.Format.defaults.date);
13676         },
13677
13678         /**
13679          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13680          * @param {String} format Any valid date format string
13681          * @return {Function} The date formatting function
13682          */
13683         dateRenderer : function(format){
13684             return function(v){
13685                 return Roo.util.Format.date(v, format);  
13686             };
13687         },
13688
13689         // private
13690         stripTagsRE : /<\/?[^>]+>/gi,
13691         
13692         /**
13693          * Strips all HTML tags
13694          * @param {Mixed} value The text from which to strip tags
13695          * @return {String} The stripped text
13696          */
13697         stripTags : function(v){
13698             return !v ? v : String(v).replace(this.stripTagsRE, "");
13699         }
13700     };
13701 }();
13702 Roo.util.Format.defaults = {
13703     date : 'd/M/Y'
13704 };/*
13705  * Based on:
13706  * Ext JS Library 1.1.1
13707  * Copyright(c) 2006-2007, Ext JS, LLC.
13708  *
13709  * Originally Released Under LGPL - original licence link has changed is not relivant.
13710  *
13711  * Fork - LGPL
13712  * <script type="text/javascript">
13713  */
13714
13715
13716  
13717
13718 /**
13719  * @class Roo.MasterTemplate
13720  * @extends Roo.Template
13721  * Provides a template that can have child templates. The syntax is:
13722 <pre><code>
13723 var t = new Roo.MasterTemplate(
13724         '&lt;select name="{name}"&gt;',
13725                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13726         '&lt;/select&gt;'
13727 );
13728 t.add('options', {value: 'foo', text: 'bar'});
13729 // or you can add multiple child elements in one shot
13730 t.addAll('options', [
13731     {value: 'foo', text: 'bar'},
13732     {value: 'foo2', text: 'bar2'},
13733     {value: 'foo3', text: 'bar3'}
13734 ]);
13735 // then append, applying the master template values
13736 t.append('my-form', {name: 'my-select'});
13737 </code></pre>
13738 * A name attribute for the child template is not required if you have only one child
13739 * template or you want to refer to them by index.
13740  */
13741 Roo.MasterTemplate = function(){
13742     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13743     this.originalHtml = this.html;
13744     var st = {};
13745     var m, re = this.subTemplateRe;
13746     re.lastIndex = 0;
13747     var subIndex = 0;
13748     while(m = re.exec(this.html)){
13749         var name = m[1], content = m[2];
13750         st[subIndex] = {
13751             name: name,
13752             index: subIndex,
13753             buffer: [],
13754             tpl : new Roo.Template(content)
13755         };
13756         if(name){
13757             st[name] = st[subIndex];
13758         }
13759         st[subIndex].tpl.compile();
13760         st[subIndex].tpl.call = this.call.createDelegate(this);
13761         subIndex++;
13762     }
13763     this.subCount = subIndex;
13764     this.subs = st;
13765 };
13766 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13767     /**
13768     * The regular expression used to match sub templates
13769     * @type RegExp
13770     * @property
13771     */
13772     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13773
13774     /**
13775      * Applies the passed values to a child template.
13776      * @param {String/Number} name (optional) The name or index of the child template
13777      * @param {Array/Object} values The values to be applied to the template
13778      * @return {MasterTemplate} this
13779      */
13780      add : function(name, values){
13781         if(arguments.length == 1){
13782             values = arguments[0];
13783             name = 0;
13784         }
13785         var s = this.subs[name];
13786         s.buffer[s.buffer.length] = s.tpl.apply(values);
13787         return this;
13788     },
13789
13790     /**
13791      * Applies all the passed values to a child template.
13792      * @param {String/Number} name (optional) The name or index of the child template
13793      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13794      * @param {Boolean} reset (optional) True to reset the template first
13795      * @return {MasterTemplate} this
13796      */
13797     fill : function(name, values, reset){
13798         var a = arguments;
13799         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13800             values = a[0];
13801             name = 0;
13802             reset = a[1];
13803         }
13804         if(reset){
13805             this.reset();
13806         }
13807         for(var i = 0, len = values.length; i < len; i++){
13808             this.add(name, values[i]);
13809         }
13810         return this;
13811     },
13812
13813     /**
13814      * Resets the template for reuse
13815      * @return {MasterTemplate} this
13816      */
13817      reset : function(){
13818         var s = this.subs;
13819         for(var i = 0; i < this.subCount; i++){
13820             s[i].buffer = [];
13821         }
13822         return this;
13823     },
13824
13825     applyTemplate : function(values){
13826         var s = this.subs;
13827         var replaceIndex = -1;
13828         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13829             return s[++replaceIndex].buffer.join("");
13830         });
13831         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13832     },
13833
13834     apply : function(){
13835         return this.applyTemplate.apply(this, arguments);
13836     },
13837
13838     compile : function(){return this;}
13839 });
13840
13841 /**
13842  * Alias for fill().
13843  * @method
13844  */
13845 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13846  /**
13847  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13848  * var tpl = Roo.MasterTemplate.from('element-id');
13849  * @param {String/HTMLElement} el
13850  * @param {Object} config
13851  * @static
13852  */
13853 Roo.MasterTemplate.from = function(el, config){
13854     el = Roo.getDom(el);
13855     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13856 };/*
13857  * Based on:
13858  * Ext JS Library 1.1.1
13859  * Copyright(c) 2006-2007, Ext JS, LLC.
13860  *
13861  * Originally Released Under LGPL - original licence link has changed is not relivant.
13862  *
13863  * Fork - LGPL
13864  * <script type="text/javascript">
13865  */
13866
13867  
13868 /**
13869  * @class Roo.util.CSS
13870  * Utility class for manipulating CSS rules
13871  * @singleton
13872  */
13873 Roo.util.CSS = function(){
13874         var rules = null;
13875         var doc = document;
13876
13877     var camelRe = /(-[a-z])/gi;
13878     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13879
13880    return {
13881    /**
13882     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13883     * tag and appended to the HEAD of the document.
13884     * @param {String|Object} cssText The text containing the css rules
13885     * @param {String} id An id to add to the stylesheet for later removal
13886     * @return {StyleSheet}
13887     */
13888     createStyleSheet : function(cssText, id){
13889         var ss;
13890         var head = doc.getElementsByTagName("head")[0];
13891         var nrules = doc.createElement("style");
13892         nrules.setAttribute("type", "text/css");
13893         if(id){
13894             nrules.setAttribute("id", id);
13895         }
13896         if (typeof(cssText) != 'string') {
13897             // support object maps..
13898             // not sure if this a good idea.. 
13899             // perhaps it should be merged with the general css handling
13900             // and handle js style props.
13901             var cssTextNew = [];
13902             for(var n in cssText) {
13903                 var citems = [];
13904                 for(var k in cssText[n]) {
13905                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13906                 }
13907                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13908                 
13909             }
13910             cssText = cssTextNew.join("\n");
13911             
13912         }
13913        
13914        
13915        if(Roo.isIE){
13916            head.appendChild(nrules);
13917            ss = nrules.styleSheet;
13918            ss.cssText = cssText;
13919        }else{
13920            try{
13921                 nrules.appendChild(doc.createTextNode(cssText));
13922            }catch(e){
13923                nrules.cssText = cssText; 
13924            }
13925            head.appendChild(nrules);
13926            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13927        }
13928        this.cacheStyleSheet(ss);
13929        return ss;
13930    },
13931
13932    /**
13933     * Removes a style or link tag by id
13934     * @param {String} id The id of the tag
13935     */
13936    removeStyleSheet : function(id){
13937        var existing = doc.getElementById(id);
13938        if(existing){
13939            existing.parentNode.removeChild(existing);
13940        }
13941    },
13942
13943    /**
13944     * Dynamically swaps an existing stylesheet reference for a new one
13945     * @param {String} id The id of an existing link tag to remove
13946     * @param {String} url The href of the new stylesheet to include
13947     */
13948    swapStyleSheet : function(id, url){
13949        this.removeStyleSheet(id);
13950        var ss = doc.createElement("link");
13951        ss.setAttribute("rel", "stylesheet");
13952        ss.setAttribute("type", "text/css");
13953        ss.setAttribute("id", id);
13954        ss.setAttribute("href", url);
13955        doc.getElementsByTagName("head")[0].appendChild(ss);
13956    },
13957    
13958    /**
13959     * Refresh the rule cache if you have dynamically added stylesheets
13960     * @return {Object} An object (hash) of rules indexed by selector
13961     */
13962    refreshCache : function(){
13963        return this.getRules(true);
13964    },
13965
13966    // private
13967    cacheStyleSheet : function(stylesheet){
13968        if(!rules){
13969            rules = {};
13970        }
13971        try{// try catch for cross domain access issue
13972            var ssRules = stylesheet.cssRules || stylesheet.rules;
13973            for(var j = ssRules.length-1; j >= 0; --j){
13974                rules[ssRules[j].selectorText] = ssRules[j];
13975            }
13976        }catch(e){}
13977    },
13978    
13979    /**
13980     * Gets all css rules for the document
13981     * @param {Boolean} refreshCache true to refresh the internal cache
13982     * @return {Object} An object (hash) of rules indexed by selector
13983     */
13984    getRules : function(refreshCache){
13985                 if(rules == null || refreshCache){
13986                         rules = {};
13987                         var ds = doc.styleSheets;
13988                         for(var i =0, len = ds.length; i < len; i++){
13989                             try{
13990                         this.cacheStyleSheet(ds[i]);
13991                     }catch(e){} 
13992                 }
13993                 }
13994                 return rules;
13995         },
13996         
13997         /**
13998     * Gets an an individual CSS rule by selector(s)
13999     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14000     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14001     * @return {CSSRule} The CSS rule or null if one is not found
14002     */
14003    getRule : function(selector, refreshCache){
14004                 var rs = this.getRules(refreshCache);
14005                 if(!(selector instanceof Array)){
14006                     return rs[selector];
14007                 }
14008                 for(var i = 0; i < selector.length; i++){
14009                         if(rs[selector[i]]){
14010                                 return rs[selector[i]];
14011                         }
14012                 }
14013                 return null;
14014         },
14015         
14016         
14017         /**
14018     * Updates a rule property
14019     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14020     * @param {String} property The css property
14021     * @param {String} value The new value for the property
14022     * @return {Boolean} true If a rule was found and updated
14023     */
14024    updateRule : function(selector, property, value){
14025                 if(!(selector instanceof Array)){
14026                         var rule = this.getRule(selector);
14027                         if(rule){
14028                                 rule.style[property.replace(camelRe, camelFn)] = value;
14029                                 return true;
14030                         }
14031                 }else{
14032                         for(var i = 0; i < selector.length; i++){
14033                                 if(this.updateRule(selector[i], property, value)){
14034                                         return true;
14035                                 }
14036                         }
14037                 }
14038                 return false;
14039         }
14040    };   
14041 }();/*
14042  * Based on:
14043  * Ext JS Library 1.1.1
14044  * Copyright(c) 2006-2007, Ext JS, LLC.
14045  *
14046  * Originally Released Under LGPL - original licence link has changed is not relivant.
14047  *
14048  * Fork - LGPL
14049  * <script type="text/javascript">
14050  */
14051
14052  
14053
14054 /**
14055  * @class Roo.util.ClickRepeater
14056  * @extends Roo.util.Observable
14057  * 
14058  * A wrapper class which can be applied to any element. Fires a "click" event while the
14059  * mouse is pressed. The interval between firings may be specified in the config but
14060  * defaults to 10 milliseconds.
14061  * 
14062  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14063  * 
14064  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14065  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14066  * Similar to an autorepeat key delay.
14067  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14068  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14069  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14070  *           "interval" and "delay" are ignored. "immediate" is honored.
14071  * @cfg {Boolean} preventDefault True to prevent the default click event
14072  * @cfg {Boolean} stopDefault True to stop the default click event
14073  * 
14074  * @history
14075  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14076  *     2007-02-02 jvs Renamed to ClickRepeater
14077  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14078  *
14079  *  @constructor
14080  * @param {String/HTMLElement/Element} el The element to listen on
14081  * @param {Object} config
14082  **/
14083 Roo.util.ClickRepeater = function(el, config)
14084 {
14085     this.el = Roo.get(el);
14086     this.el.unselectable();
14087
14088     Roo.apply(this, config);
14089
14090     this.addEvents({
14091     /**
14092      * @event mousedown
14093      * Fires when the mouse button is depressed.
14094      * @param {Roo.util.ClickRepeater} this
14095      */
14096         "mousedown" : true,
14097     /**
14098      * @event click
14099      * Fires on a specified interval during the time the element is pressed.
14100      * @param {Roo.util.ClickRepeater} this
14101      */
14102         "click" : true,
14103     /**
14104      * @event mouseup
14105      * Fires when the mouse key is released.
14106      * @param {Roo.util.ClickRepeater} this
14107      */
14108         "mouseup" : true
14109     });
14110
14111     this.el.on("mousedown", this.handleMouseDown, this);
14112     if(this.preventDefault || this.stopDefault){
14113         this.el.on("click", function(e){
14114             if(this.preventDefault){
14115                 e.preventDefault();
14116             }
14117             if(this.stopDefault){
14118                 e.stopEvent();
14119             }
14120         }, this);
14121     }
14122
14123     // allow inline handler
14124     if(this.handler){
14125         this.on("click", this.handler,  this.scope || this);
14126     }
14127
14128     Roo.util.ClickRepeater.superclass.constructor.call(this);
14129 };
14130
14131 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14132     interval : 20,
14133     delay: 250,
14134     preventDefault : true,
14135     stopDefault : false,
14136     timer : 0,
14137
14138     // private
14139     handleMouseDown : function(){
14140         clearTimeout(this.timer);
14141         this.el.blur();
14142         if(this.pressClass){
14143             this.el.addClass(this.pressClass);
14144         }
14145         this.mousedownTime = new Date();
14146
14147         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14148         this.el.on("mouseout", this.handleMouseOut, this);
14149
14150         this.fireEvent("mousedown", this);
14151         this.fireEvent("click", this);
14152         
14153         this.timer = this.click.defer(this.delay || this.interval, this);
14154     },
14155
14156     // private
14157     click : function(){
14158         this.fireEvent("click", this);
14159         this.timer = this.click.defer(this.getInterval(), this);
14160     },
14161
14162     // private
14163     getInterval: function(){
14164         if(!this.accelerate){
14165             return this.interval;
14166         }
14167         var pressTime = this.mousedownTime.getElapsed();
14168         if(pressTime < 500){
14169             return 400;
14170         }else if(pressTime < 1700){
14171             return 320;
14172         }else if(pressTime < 2600){
14173             return 250;
14174         }else if(pressTime < 3500){
14175             return 180;
14176         }else if(pressTime < 4400){
14177             return 140;
14178         }else if(pressTime < 5300){
14179             return 80;
14180         }else if(pressTime < 6200){
14181             return 50;
14182         }else{
14183             return 10;
14184         }
14185     },
14186
14187     // private
14188     handleMouseOut : function(){
14189         clearTimeout(this.timer);
14190         if(this.pressClass){
14191             this.el.removeClass(this.pressClass);
14192         }
14193         this.el.on("mouseover", this.handleMouseReturn, this);
14194     },
14195
14196     // private
14197     handleMouseReturn : function(){
14198         this.el.un("mouseover", this.handleMouseReturn);
14199         if(this.pressClass){
14200             this.el.addClass(this.pressClass);
14201         }
14202         this.click();
14203     },
14204
14205     // private
14206     handleMouseUp : function(){
14207         clearTimeout(this.timer);
14208         this.el.un("mouseover", this.handleMouseReturn);
14209         this.el.un("mouseout", this.handleMouseOut);
14210         Roo.get(document).un("mouseup", this.handleMouseUp);
14211         this.el.removeClass(this.pressClass);
14212         this.fireEvent("mouseup", this);
14213     }
14214 });/*
14215  * Based on:
14216  * Ext JS Library 1.1.1
14217  * Copyright(c) 2006-2007, Ext JS, LLC.
14218  *
14219  * Originally Released Under LGPL - original licence link has changed is not relivant.
14220  *
14221  * Fork - LGPL
14222  * <script type="text/javascript">
14223  */
14224
14225  
14226 /**
14227  * @class Roo.KeyNav
14228  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14229  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14230  * way to implement custom navigation schemes for any UI component.</p>
14231  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14232  * pageUp, pageDown, del, home, end.  Usage:</p>
14233  <pre><code>
14234 var nav = new Roo.KeyNav("my-element", {
14235     "left" : function(e){
14236         this.moveLeft(e.ctrlKey);
14237     },
14238     "right" : function(e){
14239         this.moveRight(e.ctrlKey);
14240     },
14241     "enter" : function(e){
14242         this.save();
14243     },
14244     scope : this
14245 });
14246 </code></pre>
14247  * @constructor
14248  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14249  * @param {Object} config The config
14250  */
14251 Roo.KeyNav = function(el, config){
14252     this.el = Roo.get(el);
14253     Roo.apply(this, config);
14254     if(!this.disabled){
14255         this.disabled = true;
14256         this.enable();
14257     }
14258 };
14259
14260 Roo.KeyNav.prototype = {
14261     /**
14262      * @cfg {Boolean} disabled
14263      * True to disable this KeyNav instance (defaults to false)
14264      */
14265     disabled : false,
14266     /**
14267      * @cfg {String} defaultEventAction
14268      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14269      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14270      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14271      */
14272     defaultEventAction: "stopEvent",
14273     /**
14274      * @cfg {Boolean} forceKeyDown
14275      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14276      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14277      * handle keydown instead of keypress.
14278      */
14279     forceKeyDown : false,
14280
14281     // private
14282     prepareEvent : function(e){
14283         var k = e.getKey();
14284         var h = this.keyToHandler[k];
14285         //if(h && this[h]){
14286         //    e.stopPropagation();
14287         //}
14288         if(Roo.isSafari && h && k >= 37 && k <= 40){
14289             e.stopEvent();
14290         }
14291     },
14292
14293     // private
14294     relay : function(e){
14295         var k = e.getKey();
14296         var h = this.keyToHandler[k];
14297         if(h && this[h]){
14298             if(this.doRelay(e, this[h], h) !== true){
14299                 e[this.defaultEventAction]();
14300             }
14301         }
14302     },
14303
14304     // private
14305     doRelay : function(e, h, hname){
14306         return h.call(this.scope || this, e);
14307     },
14308
14309     // possible handlers
14310     enter : false,
14311     left : false,
14312     right : false,
14313     up : false,
14314     down : false,
14315     tab : false,
14316     esc : false,
14317     pageUp : false,
14318     pageDown : false,
14319     del : false,
14320     home : false,
14321     end : false,
14322
14323     // quick lookup hash
14324     keyToHandler : {
14325         37 : "left",
14326         39 : "right",
14327         38 : "up",
14328         40 : "down",
14329         33 : "pageUp",
14330         34 : "pageDown",
14331         46 : "del",
14332         36 : "home",
14333         35 : "end",
14334         13 : "enter",
14335         27 : "esc",
14336         9  : "tab"
14337     },
14338
14339         /**
14340          * Enable this KeyNav
14341          */
14342         enable: function(){
14343                 if(this.disabled){
14344             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14345             // the EventObject will normalize Safari automatically
14346             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14347                 this.el.on("keydown", this.relay,  this);
14348             }else{
14349                 this.el.on("keydown", this.prepareEvent,  this);
14350                 this.el.on("keypress", this.relay,  this);
14351             }
14352                     this.disabled = false;
14353                 }
14354         },
14355
14356         /**
14357          * Disable this KeyNav
14358          */
14359         disable: function(){
14360                 if(!this.disabled){
14361                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14362                 this.el.un("keydown", this.relay);
14363             }else{
14364                 this.el.un("keydown", this.prepareEvent);
14365                 this.el.un("keypress", this.relay);
14366             }
14367                     this.disabled = true;
14368                 }
14369         }
14370 };/*
14371  * Based on:
14372  * Ext JS Library 1.1.1
14373  * Copyright(c) 2006-2007, Ext JS, LLC.
14374  *
14375  * Originally Released Under LGPL - original licence link has changed is not relivant.
14376  *
14377  * Fork - LGPL
14378  * <script type="text/javascript">
14379  */
14380
14381  
14382 /**
14383  * @class Roo.KeyMap
14384  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14385  * The constructor accepts the same config object as defined by {@link #addBinding}.
14386  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14387  * combination it will call the function with this signature (if the match is a multi-key
14388  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14389  * A KeyMap can also handle a string representation of keys.<br />
14390  * Usage:
14391  <pre><code>
14392 // map one key by key code
14393 var map = new Roo.KeyMap("my-element", {
14394     key: 13, // or Roo.EventObject.ENTER
14395     fn: myHandler,
14396     scope: myObject
14397 });
14398
14399 // map multiple keys to one action by string
14400 var map = new Roo.KeyMap("my-element", {
14401     key: "a\r\n\t",
14402     fn: myHandler,
14403     scope: myObject
14404 });
14405
14406 // map multiple keys to multiple actions by strings and array of codes
14407 var map = new Roo.KeyMap("my-element", [
14408     {
14409         key: [10,13],
14410         fn: function(){ alert("Return was pressed"); }
14411     }, {
14412         key: "abc",
14413         fn: function(){ alert('a, b or c was pressed'); }
14414     }, {
14415         key: "\t",
14416         ctrl:true,
14417         shift:true,
14418         fn: function(){ alert('Control + shift + tab was pressed.'); }
14419     }
14420 ]);
14421 </code></pre>
14422  * <b>Note: A KeyMap starts enabled</b>
14423  * @constructor
14424  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14425  * @param {Object} config The config (see {@link #addBinding})
14426  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14427  */
14428 Roo.KeyMap = function(el, config, eventName){
14429     this.el  = Roo.get(el);
14430     this.eventName = eventName || "keydown";
14431     this.bindings = [];
14432     if(config){
14433         this.addBinding(config);
14434     }
14435     this.enable();
14436 };
14437
14438 Roo.KeyMap.prototype = {
14439     /**
14440      * True to stop the event from bubbling and prevent the default browser action if the
14441      * key was handled by the KeyMap (defaults to false)
14442      * @type Boolean
14443      */
14444     stopEvent : false,
14445
14446     /**
14447      * Add a new binding to this KeyMap. The following config object properties are supported:
14448      * <pre>
14449 Property    Type             Description
14450 ----------  ---------------  ----------------------------------------------------------------------
14451 key         String/Array     A single keycode or an array of keycodes to handle
14452 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14453 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14454 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14455 fn          Function         The function to call when KeyMap finds the expected key combination
14456 scope       Object           The scope of the callback function
14457 </pre>
14458      *
14459      * Usage:
14460      * <pre><code>
14461 // Create a KeyMap
14462 var map = new Roo.KeyMap(document, {
14463     key: Roo.EventObject.ENTER,
14464     fn: handleKey,
14465     scope: this
14466 });
14467
14468 //Add a new binding to the existing KeyMap later
14469 map.addBinding({
14470     key: 'abc',
14471     shift: true,
14472     fn: handleKey,
14473     scope: this
14474 });
14475 </code></pre>
14476      * @param {Object/Array} config A single KeyMap config or an array of configs
14477      */
14478         addBinding : function(config){
14479         if(config instanceof Array){
14480             for(var i = 0, len = config.length; i < len; i++){
14481                 this.addBinding(config[i]);
14482             }
14483             return;
14484         }
14485         var keyCode = config.key,
14486             shift = config.shift, 
14487             ctrl = config.ctrl, 
14488             alt = config.alt,
14489             fn = config.fn,
14490             scope = config.scope;
14491         if(typeof keyCode == "string"){
14492             var ks = [];
14493             var keyString = keyCode.toUpperCase();
14494             for(var j = 0, len = keyString.length; j < len; j++){
14495                 ks.push(keyString.charCodeAt(j));
14496             }
14497             keyCode = ks;
14498         }
14499         var keyArray = keyCode instanceof Array;
14500         var handler = function(e){
14501             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14502                 var k = e.getKey();
14503                 if(keyArray){
14504                     for(var i = 0, len = keyCode.length; i < len; i++){
14505                         if(keyCode[i] == k){
14506                           if(this.stopEvent){
14507                               e.stopEvent();
14508                           }
14509                           fn.call(scope || window, k, e);
14510                           return;
14511                         }
14512                     }
14513                 }else{
14514                     if(k == keyCode){
14515                         if(this.stopEvent){
14516                            e.stopEvent();
14517                         }
14518                         fn.call(scope || window, k, e);
14519                     }
14520                 }
14521             }
14522         };
14523         this.bindings.push(handler);  
14524         },
14525
14526     /**
14527      * Shorthand for adding a single key listener
14528      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14529      * following options:
14530      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14531      * @param {Function} fn The function to call
14532      * @param {Object} scope (optional) The scope of the function
14533      */
14534     on : function(key, fn, scope){
14535         var keyCode, shift, ctrl, alt;
14536         if(typeof key == "object" && !(key instanceof Array)){
14537             keyCode = key.key;
14538             shift = key.shift;
14539             ctrl = key.ctrl;
14540             alt = key.alt;
14541         }else{
14542             keyCode = key;
14543         }
14544         this.addBinding({
14545             key: keyCode,
14546             shift: shift,
14547             ctrl: ctrl,
14548             alt: alt,
14549             fn: fn,
14550             scope: scope
14551         })
14552     },
14553
14554     // private
14555     handleKeyDown : function(e){
14556             if(this.enabled){ //just in case
14557             var b = this.bindings;
14558             for(var i = 0, len = b.length; i < len; i++){
14559                 b[i].call(this, e);
14560             }
14561             }
14562         },
14563         
14564         /**
14565          * Returns true if this KeyMap is enabled
14566          * @return {Boolean} 
14567          */
14568         isEnabled : function(){
14569             return this.enabled;  
14570         },
14571         
14572         /**
14573          * Enables this KeyMap
14574          */
14575         enable: function(){
14576                 if(!this.enabled){
14577                     this.el.on(this.eventName, this.handleKeyDown, this);
14578                     this.enabled = true;
14579                 }
14580         },
14581
14582         /**
14583          * Disable this KeyMap
14584          */
14585         disable: function(){
14586                 if(this.enabled){
14587                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14588                     this.enabled = false;
14589                 }
14590         }
14591 };/*
14592  * Based on:
14593  * Ext JS Library 1.1.1
14594  * Copyright(c) 2006-2007, Ext JS, LLC.
14595  *
14596  * Originally Released Under LGPL - original licence link has changed is not relivant.
14597  *
14598  * Fork - LGPL
14599  * <script type="text/javascript">
14600  */
14601
14602  
14603 /**
14604  * @class Roo.util.TextMetrics
14605  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14606  * wide, in pixels, a given block of text will be.
14607  * @singleton
14608  */
14609 Roo.util.TextMetrics = function(){
14610     var shared;
14611     return {
14612         /**
14613          * Measures the size of the specified text
14614          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14615          * that can affect the size of the rendered text
14616          * @param {String} text The text to measure
14617          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14618          * in order to accurately measure the text height
14619          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14620          */
14621         measure : function(el, text, fixedWidth){
14622             if(!shared){
14623                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14624             }
14625             shared.bind(el);
14626             shared.setFixedWidth(fixedWidth || 'auto');
14627             return shared.getSize(text);
14628         },
14629
14630         /**
14631          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14632          * the overhead of multiple calls to initialize the style properties on each measurement.
14633          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14634          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14635          * in order to accurately measure the text height
14636          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14637          */
14638         createInstance : function(el, fixedWidth){
14639             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14640         }
14641     };
14642 }();
14643
14644  
14645
14646 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14647     var ml = new Roo.Element(document.createElement('div'));
14648     document.body.appendChild(ml.dom);
14649     ml.position('absolute');
14650     ml.setLeftTop(-1000, -1000);
14651     ml.hide();
14652
14653     if(fixedWidth){
14654         ml.setWidth(fixedWidth);
14655     }
14656      
14657     var instance = {
14658         /**
14659          * Returns the size of the specified text based on the internal element's style and width properties
14660          * @memberOf Roo.util.TextMetrics.Instance#
14661          * @param {String} text The text to measure
14662          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14663          */
14664         getSize : function(text){
14665             ml.update(text);
14666             var s = ml.getSize();
14667             ml.update('');
14668             return s;
14669         },
14670
14671         /**
14672          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14673          * that can affect the size of the rendered text
14674          * @memberOf Roo.util.TextMetrics.Instance#
14675          * @param {String/HTMLElement} el The element, dom node or id
14676          */
14677         bind : function(el){
14678             ml.setStyle(
14679                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14680             );
14681         },
14682
14683         /**
14684          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14685          * to set a fixed width in order to accurately measure the text height.
14686          * @memberOf Roo.util.TextMetrics.Instance#
14687          * @param {Number} width The width to set on the element
14688          */
14689         setFixedWidth : function(width){
14690             ml.setWidth(width);
14691         },
14692
14693         /**
14694          * Returns the measured width of the specified text
14695          * @memberOf Roo.util.TextMetrics.Instance#
14696          * @param {String} text The text to measure
14697          * @return {Number} width The width in pixels
14698          */
14699         getWidth : function(text){
14700             ml.dom.style.width = 'auto';
14701             return this.getSize(text).width;
14702         },
14703
14704         /**
14705          * Returns the measured height of the specified text.  For multiline text, be sure to call
14706          * {@link #setFixedWidth} if necessary.
14707          * @memberOf Roo.util.TextMetrics.Instance#
14708          * @param {String} text The text to measure
14709          * @return {Number} height The height in pixels
14710          */
14711         getHeight : function(text){
14712             return this.getSize(text).height;
14713         }
14714     };
14715
14716     instance.bind(bindTo);
14717
14718     return instance;
14719 };
14720
14721 // backwards compat
14722 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14723  * Based on:
14724  * Ext JS Library 1.1.1
14725  * Copyright(c) 2006-2007, Ext JS, LLC.
14726  *
14727  * Originally Released Under LGPL - original licence link has changed is not relivant.
14728  *
14729  * Fork - LGPL
14730  * <script type="text/javascript">
14731  */
14732
14733 /**
14734  * @class Roo.state.Provider
14735  * Abstract base class for state provider implementations. This class provides methods
14736  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14737  * Provider interface.
14738  */
14739 Roo.state.Provider = function(){
14740     /**
14741      * @event statechange
14742      * Fires when a state change occurs.
14743      * @param {Provider} this This state provider
14744      * @param {String} key The state key which was changed
14745      * @param {String} value The encoded value for the state
14746      */
14747     this.addEvents({
14748         "statechange": true
14749     });
14750     this.state = {};
14751     Roo.state.Provider.superclass.constructor.call(this);
14752 };
14753 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14754     /**
14755      * Returns the current value for a key
14756      * @param {String} name The key name
14757      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14758      * @return {Mixed} The state data
14759      */
14760     get : function(name, defaultValue){
14761         return typeof this.state[name] == "undefined" ?
14762             defaultValue : this.state[name];
14763     },
14764     
14765     /**
14766      * Clears a value from the state
14767      * @param {String} name The key name
14768      */
14769     clear : function(name){
14770         delete this.state[name];
14771         this.fireEvent("statechange", this, name, null);
14772     },
14773     
14774     /**
14775      * Sets the value for a key
14776      * @param {String} name The key name
14777      * @param {Mixed} value The value to set
14778      */
14779     set : function(name, value){
14780         this.state[name] = value;
14781         this.fireEvent("statechange", this, name, value);
14782     },
14783     
14784     /**
14785      * Decodes a string previously encoded with {@link #encodeValue}.
14786      * @param {String} value The value to decode
14787      * @return {Mixed} The decoded value
14788      */
14789     decodeValue : function(cookie){
14790         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14791         var matches = re.exec(unescape(cookie));
14792         if(!matches || !matches[1]) return; // non state cookie
14793         var type = matches[1];
14794         var v = matches[2];
14795         switch(type){
14796             case "n":
14797                 return parseFloat(v);
14798             case "d":
14799                 return new Date(Date.parse(v));
14800             case "b":
14801                 return (v == "1");
14802             case "a":
14803                 var all = [];
14804                 var values = v.split("^");
14805                 for(var i = 0, len = values.length; i < len; i++){
14806                     all.push(this.decodeValue(values[i]));
14807                 }
14808                 return all;
14809            case "o":
14810                 var all = {};
14811                 var values = v.split("^");
14812                 for(var i = 0, len = values.length; i < len; i++){
14813                     var kv = values[i].split("=");
14814                     all[kv[0]] = this.decodeValue(kv[1]);
14815                 }
14816                 return all;
14817            default:
14818                 return v;
14819         }
14820     },
14821     
14822     /**
14823      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14824      * @param {Mixed} value The value to encode
14825      * @return {String} The encoded value
14826      */
14827     encodeValue : function(v){
14828         var enc;
14829         if(typeof v == "number"){
14830             enc = "n:" + v;
14831         }else if(typeof v == "boolean"){
14832             enc = "b:" + (v ? "1" : "0");
14833         }else if(v instanceof Date){
14834             enc = "d:" + v.toGMTString();
14835         }else if(v instanceof Array){
14836             var flat = "";
14837             for(var i = 0, len = v.length; i < len; i++){
14838                 flat += this.encodeValue(v[i]);
14839                 if(i != len-1) flat += "^";
14840             }
14841             enc = "a:" + flat;
14842         }else if(typeof v == "object"){
14843             var flat = "";
14844             for(var key in v){
14845                 if(typeof v[key] != "function"){
14846                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14847                 }
14848             }
14849             enc = "o:" + flat.substring(0, flat.length-1);
14850         }else{
14851             enc = "s:" + v;
14852         }
14853         return escape(enc);        
14854     }
14855 });
14856
14857 /*
14858  * Based on:
14859  * Ext JS Library 1.1.1
14860  * Copyright(c) 2006-2007, Ext JS, LLC.
14861  *
14862  * Originally Released Under LGPL - original licence link has changed is not relivant.
14863  *
14864  * Fork - LGPL
14865  * <script type="text/javascript">
14866  */
14867 /**
14868  * @class Roo.state.Manager
14869  * This is the global state manager. By default all components that are "state aware" check this class
14870  * for state information if you don't pass them a custom state provider. In order for this class
14871  * to be useful, it must be initialized with a provider when your application initializes.
14872  <pre><code>
14873 // in your initialization function
14874 init : function(){
14875    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14876    ...
14877    // supposed you have a {@link Roo.BorderLayout}
14878    var layout = new Roo.BorderLayout(...);
14879    layout.restoreState();
14880    // or a {Roo.BasicDialog}
14881    var dialog = new Roo.BasicDialog(...);
14882    dialog.restoreState();
14883  </code></pre>
14884  * @singleton
14885  */
14886 Roo.state.Manager = function(){
14887     var provider = new Roo.state.Provider();
14888     
14889     return {
14890         /**
14891          * Configures the default state provider for your application
14892          * @param {Provider} stateProvider The state provider to set
14893          */
14894         setProvider : function(stateProvider){
14895             provider = stateProvider;
14896         },
14897         
14898         /**
14899          * Returns the current value for a key
14900          * @param {String} name The key name
14901          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14902          * @return {Mixed} The state data
14903          */
14904         get : function(key, defaultValue){
14905             return provider.get(key, defaultValue);
14906         },
14907         
14908         /**
14909          * Sets the value for a key
14910          * @param {String} name The key name
14911          * @param {Mixed} value The state data
14912          */
14913          set : function(key, value){
14914             provider.set(key, value);
14915         },
14916         
14917         /**
14918          * Clears a value from the state
14919          * @param {String} name The key name
14920          */
14921         clear : function(key){
14922             provider.clear(key);
14923         },
14924         
14925         /**
14926          * Gets the currently configured state provider
14927          * @return {Provider} The state provider
14928          */
14929         getProvider : function(){
14930             return provider;
14931         }
14932     };
14933 }();
14934 /*
14935  * Based on:
14936  * Ext JS Library 1.1.1
14937  * Copyright(c) 2006-2007, Ext JS, LLC.
14938  *
14939  * Originally Released Under LGPL - original licence link has changed is not relivant.
14940  *
14941  * Fork - LGPL
14942  * <script type="text/javascript">
14943  */
14944 /**
14945  * @class Roo.state.CookieProvider
14946  * @extends Roo.state.Provider
14947  * The default Provider implementation which saves state via cookies.
14948  * <br />Usage:
14949  <pre><code>
14950    var cp = new Roo.state.CookieProvider({
14951        path: "/cgi-bin/",
14952        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14953        domain: "roojs.com"
14954    })
14955    Roo.state.Manager.setProvider(cp);
14956  </code></pre>
14957  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14958  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14959  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14960  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14961  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14962  * domain the page is running on including the 'www' like 'www.roojs.com')
14963  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14964  * @constructor
14965  * Create a new CookieProvider
14966  * @param {Object} config The configuration object
14967  */
14968 Roo.state.CookieProvider = function(config){
14969     Roo.state.CookieProvider.superclass.constructor.call(this);
14970     this.path = "/";
14971     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14972     this.domain = null;
14973     this.secure = false;
14974     Roo.apply(this, config);
14975     this.state = this.readCookies();
14976 };
14977
14978 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14979     // private
14980     set : function(name, value){
14981         if(typeof value == "undefined" || value === null){
14982             this.clear(name);
14983             return;
14984         }
14985         this.setCookie(name, value);
14986         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14987     },
14988
14989     // private
14990     clear : function(name){
14991         this.clearCookie(name);
14992         Roo.state.CookieProvider.superclass.clear.call(this, name);
14993     },
14994
14995     // private
14996     readCookies : function(){
14997         var cookies = {};
14998         var c = document.cookie + ";";
14999         var re = /\s?(.*?)=(.*?);/g;
15000         var matches;
15001         while((matches = re.exec(c)) != null){
15002             var name = matches[1];
15003             var value = matches[2];
15004             if(name && name.substring(0,3) == "ys-"){
15005                 cookies[name.substr(3)] = this.decodeValue(value);
15006             }
15007         }
15008         return cookies;
15009     },
15010
15011     // private
15012     setCookie : function(name, value){
15013         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15014            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15015            ((this.path == null) ? "" : ("; path=" + this.path)) +
15016            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15017            ((this.secure == true) ? "; secure" : "");
15018     },
15019
15020     // private
15021     clearCookie : function(name){
15022         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15023            ((this.path == null) ? "" : ("; path=" + this.path)) +
15024            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15025            ((this.secure == true) ? "; secure" : "");
15026     }
15027 });/*
15028  * Based on:
15029  * Ext JS Library 1.1.1
15030  * Copyright(c) 2006-2007, Ext JS, LLC.
15031  *
15032  * Originally Released Under LGPL - original licence link has changed is not relivant.
15033  *
15034  * Fork - LGPL
15035  * <script type="text/javascript">
15036  */
15037  
15038
15039 /**
15040  * @class Roo.ComponentMgr
15041  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15042  * @singleton
15043  */
15044 Roo.ComponentMgr = function(){
15045     var all = new Roo.util.MixedCollection();
15046
15047     return {
15048         /**
15049          * Registers a component.
15050          * @param {Roo.Component} c The component
15051          */
15052         register : function(c){
15053             all.add(c);
15054         },
15055
15056         /**
15057          * Unregisters a component.
15058          * @param {Roo.Component} c The component
15059          */
15060         unregister : function(c){
15061             all.remove(c);
15062         },
15063
15064         /**
15065          * Returns a component by id
15066          * @param {String} id The component id
15067          */
15068         get : function(id){
15069             return all.get(id);
15070         },
15071
15072         /**
15073          * Registers a function that will be called when a specified component is added to ComponentMgr
15074          * @param {String} id The component id
15075          * @param {Funtction} fn The callback function
15076          * @param {Object} scope The scope of the callback
15077          */
15078         onAvailable : function(id, fn, scope){
15079             all.on("add", function(index, o){
15080                 if(o.id == id){
15081                     fn.call(scope || o, o);
15082                     all.un("add", fn, scope);
15083                 }
15084             });
15085         }
15086     };
15087 }();/*
15088  * Based on:
15089  * Ext JS Library 1.1.1
15090  * Copyright(c) 2006-2007, Ext JS, LLC.
15091  *
15092  * Originally Released Under LGPL - original licence link has changed is not relivant.
15093  *
15094  * Fork - LGPL
15095  * <script type="text/javascript">
15096  */
15097  
15098 /**
15099  * @class Roo.Component
15100  * @extends Roo.util.Observable
15101  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15102  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15103  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15104  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15105  * All visual components (widgets) that require rendering into a layout should subclass Component.
15106  * @constructor
15107  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15108  * 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
15109  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15110  */
15111 Roo.Component = function(config){
15112     config = config || {};
15113     if(config.tagName || config.dom || typeof config == "string"){ // element object
15114         config = {el: config, id: config.id || config};
15115     }
15116     this.initialConfig = config;
15117
15118     Roo.apply(this, config);
15119     this.addEvents({
15120         /**
15121          * @event disable
15122          * Fires after the component is disabled.
15123              * @param {Roo.Component} this
15124              */
15125         disable : true,
15126         /**
15127          * @event enable
15128          * Fires after the component is enabled.
15129              * @param {Roo.Component} this
15130              */
15131         enable : true,
15132         /**
15133          * @event beforeshow
15134          * Fires before the component is shown.  Return false to stop the show.
15135              * @param {Roo.Component} this
15136              */
15137         beforeshow : true,
15138         /**
15139          * @event show
15140          * Fires after the component is shown.
15141              * @param {Roo.Component} this
15142              */
15143         show : true,
15144         /**
15145          * @event beforehide
15146          * Fires before the component is hidden. Return false to stop the hide.
15147              * @param {Roo.Component} this
15148              */
15149         beforehide : true,
15150         /**
15151          * @event hide
15152          * Fires after the component is hidden.
15153              * @param {Roo.Component} this
15154              */
15155         hide : true,
15156         /**
15157          * @event beforerender
15158          * Fires before the component is rendered. Return false to stop the render.
15159              * @param {Roo.Component} this
15160              */
15161         beforerender : true,
15162         /**
15163          * @event render
15164          * Fires after the component is rendered.
15165              * @param {Roo.Component} this
15166              */
15167         render : true,
15168         /**
15169          * @event beforedestroy
15170          * Fires before the component is destroyed. Return false to stop the destroy.
15171              * @param {Roo.Component} this
15172              */
15173         beforedestroy : true,
15174         /**
15175          * @event destroy
15176          * Fires after the component is destroyed.
15177              * @param {Roo.Component} this
15178              */
15179         destroy : true
15180     });
15181     if(!this.id){
15182         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15183     }
15184     Roo.ComponentMgr.register(this);
15185     Roo.Component.superclass.constructor.call(this);
15186     this.initComponent();
15187     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15188         this.render(this.renderTo);
15189         delete this.renderTo;
15190     }
15191 };
15192
15193 /** @private */
15194 Roo.Component.AUTO_ID = 1000;
15195
15196 Roo.extend(Roo.Component, Roo.util.Observable, {
15197     /**
15198      * @scope Roo.Component.prototype
15199      * @type {Boolean}
15200      * true if this component is hidden. Read-only.
15201      */
15202     hidden : false,
15203     /**
15204      * @type {Boolean}
15205      * true if this component is disabled. Read-only.
15206      */
15207     disabled : false,
15208     /**
15209      * @type {Boolean}
15210      * true if this component has been rendered. Read-only.
15211      */
15212     rendered : false,
15213     
15214     /** @cfg {String} disableClass
15215      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15216      */
15217     disabledClass : "x-item-disabled",
15218         /** @cfg {Boolean} allowDomMove
15219          * Whether the component can move the Dom node when rendering (defaults to true).
15220          */
15221     allowDomMove : true,
15222     /** @cfg {String} hideMode (display|visibility)
15223      * How this component should hidden. Supported values are
15224      * "visibility" (css visibility), "offsets" (negative offset position) and
15225      * "display" (css display) - defaults to "display".
15226      */
15227     hideMode: 'display',
15228
15229     /** @private */
15230     ctype : "Roo.Component",
15231
15232     /**
15233      * @cfg {String} actionMode 
15234      * which property holds the element that used for  hide() / show() / disable() / enable()
15235      * default is 'el' 
15236      */
15237     actionMode : "el",
15238
15239     /** @private */
15240     getActionEl : function(){
15241         return this[this.actionMode];
15242     },
15243
15244     initComponent : Roo.emptyFn,
15245     /**
15246      * If this is a lazy rendering component, render it to its container element.
15247      * @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.
15248      */
15249     render : function(container, position){
15250         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15251             if(!container && this.el){
15252                 this.el = Roo.get(this.el);
15253                 container = this.el.dom.parentNode;
15254                 this.allowDomMove = false;
15255             }
15256             this.container = Roo.get(container);
15257             this.rendered = true;
15258             if(position !== undefined){
15259                 if(typeof position == 'number'){
15260                     position = this.container.dom.childNodes[position];
15261                 }else{
15262                     position = Roo.getDom(position);
15263                 }
15264             }
15265             this.onRender(this.container, position || null);
15266             if(this.cls){
15267                 this.el.addClass(this.cls);
15268                 delete this.cls;
15269             }
15270             if(this.style){
15271                 this.el.applyStyles(this.style);
15272                 delete this.style;
15273             }
15274             this.fireEvent("render", this);
15275             this.afterRender(this.container);
15276             if(this.hidden){
15277                 this.hide();
15278             }
15279             if(this.disabled){
15280                 this.disable();
15281             }
15282         }
15283         return this;
15284     },
15285
15286     /** @private */
15287     // default function is not really useful
15288     onRender : function(ct, position){
15289         if(this.el){
15290             this.el = Roo.get(this.el);
15291             if(this.allowDomMove !== false){
15292                 ct.dom.insertBefore(this.el.dom, position);
15293             }
15294         }
15295     },
15296
15297     /** @private */
15298     getAutoCreate : function(){
15299         var cfg = typeof this.autoCreate == "object" ?
15300                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15301         if(this.id && !cfg.id){
15302             cfg.id = this.id;
15303         }
15304         return cfg;
15305     },
15306
15307     /** @private */
15308     afterRender : Roo.emptyFn,
15309
15310     /**
15311      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15312      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15313      */
15314     destroy : function(){
15315         if(this.fireEvent("beforedestroy", this) !== false){
15316             this.purgeListeners();
15317             this.beforeDestroy();
15318             if(this.rendered){
15319                 this.el.removeAllListeners();
15320                 this.el.remove();
15321                 if(this.actionMode == "container"){
15322                     this.container.remove();
15323                 }
15324             }
15325             this.onDestroy();
15326             Roo.ComponentMgr.unregister(this);
15327             this.fireEvent("destroy", this);
15328         }
15329     },
15330
15331         /** @private */
15332     beforeDestroy : function(){
15333
15334     },
15335
15336         /** @private */
15337         onDestroy : function(){
15338
15339     },
15340
15341     /**
15342      * Returns the underlying {@link Roo.Element}.
15343      * @return {Roo.Element} The element
15344      */
15345     getEl : function(){
15346         return this.el;
15347     },
15348
15349     /**
15350      * Returns the id of this component.
15351      * @return {String}
15352      */
15353     getId : function(){
15354         return this.id;
15355     },
15356
15357     /**
15358      * Try to focus this component.
15359      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15360      * @return {Roo.Component} this
15361      */
15362     focus : function(selectText){
15363         if(this.rendered){
15364             this.el.focus();
15365             if(selectText === true){
15366                 this.el.dom.select();
15367             }
15368         }
15369         return this;
15370     },
15371
15372     /** @private */
15373     blur : function(){
15374         if(this.rendered){
15375             this.el.blur();
15376         }
15377         return this;
15378     },
15379
15380     /**
15381      * Disable this component.
15382      * @return {Roo.Component} this
15383      */
15384     disable : function(){
15385         if(this.rendered){
15386             this.onDisable();
15387         }
15388         this.disabled = true;
15389         this.fireEvent("disable", this);
15390         return this;
15391     },
15392
15393         // private
15394     onDisable : function(){
15395         this.getActionEl().addClass(this.disabledClass);
15396         this.el.dom.disabled = true;
15397     },
15398
15399     /**
15400      * Enable this component.
15401      * @return {Roo.Component} this
15402      */
15403     enable : function(){
15404         if(this.rendered){
15405             this.onEnable();
15406         }
15407         this.disabled = false;
15408         this.fireEvent("enable", this);
15409         return this;
15410     },
15411
15412         // private
15413     onEnable : function(){
15414         this.getActionEl().removeClass(this.disabledClass);
15415         this.el.dom.disabled = false;
15416     },
15417
15418     /**
15419      * Convenience function for setting disabled/enabled by boolean.
15420      * @param {Boolean} disabled
15421      */
15422     setDisabled : function(disabled){
15423         this[disabled ? "disable" : "enable"]();
15424     },
15425
15426     /**
15427      * Show this component.
15428      * @return {Roo.Component} this
15429      */
15430     show: function(){
15431         if(this.fireEvent("beforeshow", this) !== false){
15432             this.hidden = false;
15433             if(this.rendered){
15434                 this.onShow();
15435             }
15436             this.fireEvent("show", this);
15437         }
15438         return this;
15439     },
15440
15441     // private
15442     onShow : function(){
15443         var ae = this.getActionEl();
15444         if(this.hideMode == 'visibility'){
15445             ae.dom.style.visibility = "visible";
15446         }else if(this.hideMode == 'offsets'){
15447             ae.removeClass('x-hidden');
15448         }else{
15449             ae.dom.style.display = "";
15450         }
15451     },
15452
15453     /**
15454      * Hide this component.
15455      * @return {Roo.Component} this
15456      */
15457     hide: function(){
15458         if(this.fireEvent("beforehide", this) !== false){
15459             this.hidden = true;
15460             if(this.rendered){
15461                 this.onHide();
15462             }
15463             this.fireEvent("hide", this);
15464         }
15465         return this;
15466     },
15467
15468     // private
15469     onHide : function(){
15470         var ae = this.getActionEl();
15471         if(this.hideMode == 'visibility'){
15472             ae.dom.style.visibility = "hidden";
15473         }else if(this.hideMode == 'offsets'){
15474             ae.addClass('x-hidden');
15475         }else{
15476             ae.dom.style.display = "none";
15477         }
15478     },
15479
15480     /**
15481      * Convenience function to hide or show this component by boolean.
15482      * @param {Boolean} visible True to show, false to hide
15483      * @return {Roo.Component} this
15484      */
15485     setVisible: function(visible){
15486         if(visible) {
15487             this.show();
15488         }else{
15489             this.hide();
15490         }
15491         return this;
15492     },
15493
15494     /**
15495      * Returns true if this component is visible.
15496      */
15497     isVisible : function(){
15498         return this.getActionEl().isVisible();
15499     },
15500
15501     cloneConfig : function(overrides){
15502         overrides = overrides || {};
15503         var id = overrides.id || Roo.id();
15504         var cfg = Roo.applyIf(overrides, this.initialConfig);
15505         cfg.id = id; // prevent dup id
15506         return new this.constructor(cfg);
15507     }
15508 });/*
15509  * Based on:
15510  * Ext JS Library 1.1.1
15511  * Copyright(c) 2006-2007, Ext JS, LLC.
15512  *
15513  * Originally Released Under LGPL - original licence link has changed is not relivant.
15514  *
15515  * Fork - LGPL
15516  * <script type="text/javascript">
15517  */
15518
15519 /**
15520  * @class Roo.BoxComponent
15521  * @extends Roo.Component
15522  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15523  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15524  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15525  * layout containers.
15526  * @constructor
15527  * @param {Roo.Element/String/Object} config The configuration options.
15528  */
15529 Roo.BoxComponent = function(config){
15530     Roo.Component.call(this, config);
15531     this.addEvents({
15532         /**
15533          * @event resize
15534          * Fires after the component is resized.
15535              * @param {Roo.Component} this
15536              * @param {Number} adjWidth The box-adjusted width that was set
15537              * @param {Number} adjHeight The box-adjusted height that was set
15538              * @param {Number} rawWidth The width that was originally specified
15539              * @param {Number} rawHeight The height that was originally specified
15540              */
15541         resize : true,
15542         /**
15543          * @event move
15544          * Fires after the component is moved.
15545              * @param {Roo.Component} this
15546              * @param {Number} x The new x position
15547              * @param {Number} y The new y position
15548              */
15549         move : true
15550     });
15551 };
15552
15553 Roo.extend(Roo.BoxComponent, Roo.Component, {
15554     // private, set in afterRender to signify that the component has been rendered
15555     boxReady : false,
15556     // private, used to defer height settings to subclasses
15557     deferHeight: false,
15558     /** @cfg {Number} width
15559      * width (optional) size of component
15560      */
15561      /** @cfg {Number} height
15562      * height (optional) size of component
15563      */
15564      
15565     /**
15566      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15567      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15568      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15569      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15570      * @return {Roo.BoxComponent} this
15571      */
15572     setSize : function(w, h){
15573         // support for standard size objects
15574         if(typeof w == 'object'){
15575             h = w.height;
15576             w = w.width;
15577         }
15578         // not rendered
15579         if(!this.boxReady){
15580             this.width = w;
15581             this.height = h;
15582             return this;
15583         }
15584
15585         // prevent recalcs when not needed
15586         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15587             return this;
15588         }
15589         this.lastSize = {width: w, height: h};
15590
15591         var adj = this.adjustSize(w, h);
15592         var aw = adj.width, ah = adj.height;
15593         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15594             var rz = this.getResizeEl();
15595             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15596                 rz.setSize(aw, ah);
15597             }else if(!this.deferHeight && ah !== undefined){
15598                 rz.setHeight(ah);
15599             }else if(aw !== undefined){
15600                 rz.setWidth(aw);
15601             }
15602             this.onResize(aw, ah, w, h);
15603             this.fireEvent('resize', this, aw, ah, w, h);
15604         }
15605         return this;
15606     },
15607
15608     /**
15609      * Gets the current size of the component's underlying element.
15610      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15611      */
15612     getSize : function(){
15613         return this.el.getSize();
15614     },
15615
15616     /**
15617      * Gets the current XY position of the component's underlying element.
15618      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15619      * @return {Array} The XY position of the element (e.g., [100, 200])
15620      */
15621     getPosition : function(local){
15622         if(local === true){
15623             return [this.el.getLeft(true), this.el.getTop(true)];
15624         }
15625         return this.xy || this.el.getXY();
15626     },
15627
15628     /**
15629      * Gets the current box measurements of the component's underlying element.
15630      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15631      * @returns {Object} box An object in the format {x, y, width, height}
15632      */
15633     getBox : function(local){
15634         var s = this.el.getSize();
15635         if(local){
15636             s.x = this.el.getLeft(true);
15637             s.y = this.el.getTop(true);
15638         }else{
15639             var xy = this.xy || this.el.getXY();
15640             s.x = xy[0];
15641             s.y = xy[1];
15642         }
15643         return s;
15644     },
15645
15646     /**
15647      * Sets the current box measurements of the component's underlying element.
15648      * @param {Object} box An object in the format {x, y, width, height}
15649      * @returns {Roo.BoxComponent} this
15650      */
15651     updateBox : function(box){
15652         this.setSize(box.width, box.height);
15653         this.setPagePosition(box.x, box.y);
15654         return this;
15655     },
15656
15657     // protected
15658     getResizeEl : function(){
15659         return this.resizeEl || this.el;
15660     },
15661
15662     // protected
15663     getPositionEl : function(){
15664         return this.positionEl || this.el;
15665     },
15666
15667     /**
15668      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15669      * This method fires the move event.
15670      * @param {Number} left The new left
15671      * @param {Number} top The new top
15672      * @returns {Roo.BoxComponent} this
15673      */
15674     setPosition : function(x, y){
15675         this.x = x;
15676         this.y = y;
15677         if(!this.boxReady){
15678             return this;
15679         }
15680         var adj = this.adjustPosition(x, y);
15681         var ax = adj.x, ay = adj.y;
15682
15683         var el = this.getPositionEl();
15684         if(ax !== undefined || ay !== undefined){
15685             if(ax !== undefined && ay !== undefined){
15686                 el.setLeftTop(ax, ay);
15687             }else if(ax !== undefined){
15688                 el.setLeft(ax);
15689             }else if(ay !== undefined){
15690                 el.setTop(ay);
15691             }
15692             this.onPosition(ax, ay);
15693             this.fireEvent('move', this, ax, ay);
15694         }
15695         return this;
15696     },
15697
15698     /**
15699      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15700      * This method fires the move event.
15701      * @param {Number} x The new x position
15702      * @param {Number} y The new y position
15703      * @returns {Roo.BoxComponent} this
15704      */
15705     setPagePosition : function(x, y){
15706         this.pageX = x;
15707         this.pageY = y;
15708         if(!this.boxReady){
15709             return;
15710         }
15711         if(x === undefined || y === undefined){ // cannot translate undefined points
15712             return;
15713         }
15714         var p = this.el.translatePoints(x, y);
15715         this.setPosition(p.left, p.top);
15716         return this;
15717     },
15718
15719     // private
15720     onRender : function(ct, position){
15721         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15722         if(this.resizeEl){
15723             this.resizeEl = Roo.get(this.resizeEl);
15724         }
15725         if(this.positionEl){
15726             this.positionEl = Roo.get(this.positionEl);
15727         }
15728     },
15729
15730     // private
15731     afterRender : function(){
15732         Roo.BoxComponent.superclass.afterRender.call(this);
15733         this.boxReady = true;
15734         this.setSize(this.width, this.height);
15735         if(this.x || this.y){
15736             this.setPosition(this.x, this.y);
15737         }
15738         if(this.pageX || this.pageY){
15739             this.setPagePosition(this.pageX, this.pageY);
15740         }
15741     },
15742
15743     /**
15744      * Force the component's size to recalculate based on the underlying element's current height and width.
15745      * @returns {Roo.BoxComponent} this
15746      */
15747     syncSize : function(){
15748         delete this.lastSize;
15749         this.setSize(this.el.getWidth(), this.el.getHeight());
15750         return this;
15751     },
15752
15753     /**
15754      * Called after the component is resized, this method is empty by default but can be implemented by any
15755      * subclass that needs to perform custom logic after a resize occurs.
15756      * @param {Number} adjWidth The box-adjusted width that was set
15757      * @param {Number} adjHeight The box-adjusted height that was set
15758      * @param {Number} rawWidth The width that was originally specified
15759      * @param {Number} rawHeight The height that was originally specified
15760      */
15761     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15762
15763     },
15764
15765     /**
15766      * Called after the component is moved, this method is empty by default but can be implemented by any
15767      * subclass that needs to perform custom logic after a move occurs.
15768      * @param {Number} x The new x position
15769      * @param {Number} y The new y position
15770      */
15771     onPosition : function(x, y){
15772
15773     },
15774
15775     // private
15776     adjustSize : function(w, h){
15777         if(this.autoWidth){
15778             w = 'auto';
15779         }
15780         if(this.autoHeight){
15781             h = 'auto';
15782         }
15783         return {width : w, height: h};
15784     },
15785
15786     // private
15787     adjustPosition : function(x, y){
15788         return {x : x, y: y};
15789     }
15790 });/*
15791  * Original code for Roojs - LGPL
15792  * <script type="text/javascript">
15793  */
15794  
15795 /**
15796  * @class Roo.XComponent
15797  * A delayed Element creator...
15798  * Or a way to group chunks of interface together.
15799  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15800  *  used in conjunction with XComponent.build() it will create an instance of each element,
15801  *  then call addxtype() to build the User interface.
15802  * 
15803  * Mypart.xyx = new Roo.XComponent({
15804
15805     parent : 'Mypart.xyz', // empty == document.element.!!
15806     order : '001',
15807     name : 'xxxx'
15808     region : 'xxxx'
15809     disabled : function() {} 
15810      
15811     tree : function() { // return an tree of xtype declared components
15812         var MODULE = this;
15813         return 
15814         {
15815             xtype : 'NestedLayoutPanel',
15816             // technicall
15817         }
15818      ]
15819  *})
15820  *
15821  *
15822  * It can be used to build a big heiracy, with parent etc.
15823  * or you can just use this to render a single compoent to a dom element
15824  * MYPART.render(Roo.Element | String(id) | dom_element )
15825  *
15826  *
15827  * Usage patterns.
15828  *
15829  * Classic Roo
15830  *
15831  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15832  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15833  *
15834  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15835  *
15836  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15837  * - if mulitple topModules exist, the last one is defined as the top module.
15838  *
15839  * Embeded Roo
15840  * 
15841  * When the top level or multiple modules are to embedded into a existing HTML page,
15842  * the parent element can container '#id' of the element where the module will be drawn.
15843  *
15844  * Bootstrap Roo
15845  *
15846  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15847  * it relies more on a include mechanism, where sub modules are included into an outer page.
15848  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15849  * 
15850  * Bootstrap Roo Included elements
15851  *
15852  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15853  * hence confusing the component builder as it thinks there are multiple top level elements. 
15854  *
15855  * 
15856  * 
15857  * @extends Roo.util.Observable
15858  * @constructor
15859  * @param cfg {Object} configuration of component
15860  * 
15861  */
15862 Roo.XComponent = function(cfg) {
15863     Roo.apply(this, cfg);
15864     this.addEvents({ 
15865         /**
15866              * @event built
15867              * Fires when this the componnt is built
15868              * @param {Roo.XComponent} c the component
15869              */
15870         'built' : true
15871         
15872     });
15873     this.region = this.region || 'center'; // default..
15874     Roo.XComponent.register(this);
15875     this.modules = false;
15876     this.el = false; // where the layout goes..
15877     
15878     
15879 }
15880 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15881     /**
15882      * @property el
15883      * The created element (with Roo.factory())
15884      * @type {Roo.Layout}
15885      */
15886     el  : false,
15887     
15888     /**
15889      * @property el
15890      * for BC  - use el in new code
15891      * @type {Roo.Layout}
15892      */
15893     panel : false,
15894     
15895     /**
15896      * @property layout
15897      * for BC  - use el in new code
15898      * @type {Roo.Layout}
15899      */
15900     layout : false,
15901     
15902      /**
15903      * @cfg {Function|boolean} disabled
15904      * If this module is disabled by some rule, return true from the funtion
15905      */
15906     disabled : false,
15907     
15908     /**
15909      * @cfg {String} parent 
15910      * Name of parent element which it get xtype added to..
15911      */
15912     parent: false,
15913     
15914     /**
15915      * @cfg {String} order
15916      * Used to set the order in which elements are created (usefull for multiple tabs)
15917      */
15918     
15919     order : false,
15920     /**
15921      * @cfg {String} name
15922      * String to display while loading.
15923      */
15924     name : false,
15925     /**
15926      * @cfg {String} region
15927      * Region to render component to (defaults to center)
15928      */
15929     region : 'center',
15930     
15931     /**
15932      * @cfg {Array} items
15933      * A single item array - the first element is the root of the tree..
15934      * It's done this way to stay compatible with the Xtype system...
15935      */
15936     items : false,
15937     
15938     /**
15939      * @property _tree
15940      * The method that retuns the tree of parts that make up this compoennt 
15941      * @type {function}
15942      */
15943     _tree  : false,
15944     
15945      /**
15946      * render
15947      * render element to dom or tree
15948      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15949      */
15950     
15951     render : function(el)
15952     {
15953         
15954         el = el || false;
15955         var hp = this.parent ? 1 : 0;
15956         Roo.debug &&  Roo.log(this);
15957         
15958         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15959             // if parent is a '#.....' string, then let's use that..
15960             var ename = this.parent.substr(1);
15961             this.parent = false;
15962             Roo.debug && Roo.log(ename);
15963             switch (ename) {
15964                 case 'bootstrap-body' :
15965                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15966                         this.parent = { el :  new  Roo.bootstrap.Body() };
15967                         Roo.debug && Roo.log("setting el to doc body");
15968                          
15969                     } else {
15970                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15971                     }
15972                     break;
15973                 case 'bootstrap':
15974                     this.parent = { el : true};
15975                     // fall through
15976                 default:
15977                     el = Roo.get(ename);
15978                     break;
15979             }
15980                 
15981             
15982             if (!el && !this.parent) {
15983                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15984                 return;
15985             }
15986         }
15987         Roo.debug && Roo.log("EL:");
15988         Roo.debug && Roo.log(el);
15989         Roo.debug && Roo.log("this.parent.el:");
15990         Roo.debug && Roo.log(this.parent.el);
15991         
15992         var tree = this._tree ? this._tree() : this.tree();
15993
15994         // altertive root elements ??? - we need a better way to indicate these.
15995         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15996                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15997         
15998         if (!this.parent && is_alt) {
15999             //el = Roo.get(document.body);
16000             this.parent = { el : true };
16001         }
16002             
16003             
16004         
16005         if (!this.parent) {
16006             
16007             Roo.debug && Roo.log("no parent - creating one");
16008             
16009             el = el ? Roo.get(el) : false;      
16010             
16011             // it's a top level one..
16012             this.parent =  {
16013                 el : new Roo.BorderLayout(el || document.body, {
16014                 
16015                      center: {
16016                          titlebar: false,
16017                          autoScroll:false,
16018                          closeOnTab: true,
16019                          tabPosition: 'top',
16020                           //resizeTabs: true,
16021                          alwaysShowTabs: el && hp? false :  true,
16022                          hideTabs: el || !hp ? true :  false,
16023                          minTabWidth: 140
16024                      }
16025                  })
16026             }
16027         }
16028         
16029         if (!this.parent.el) {
16030                 // probably an old style ctor, which has been disabled.
16031                 return;
16032
16033         }
16034                 // The 'tree' method is  '_tree now' 
16035             
16036         tree.region = tree.region || this.region;
16037         
16038         if (this.parent.el === true) {
16039             // bootstrap... - body..
16040             this.parent.el = Roo.factory(tree);
16041         }
16042         
16043         this.el = this.parent.el.addxtype(tree);
16044         this.fireEvent('built', this);
16045         
16046         this.panel = this.el;
16047         this.layout = this.panel.layout;
16048         this.parentLayout = this.parent.layout  || false;  
16049          
16050     }
16051     
16052 });
16053
16054 Roo.apply(Roo.XComponent, {
16055     /**
16056      * @property  hideProgress
16057      * true to disable the building progress bar.. usefull on single page renders.
16058      * @type Boolean
16059      */
16060     hideProgress : false,
16061     /**
16062      * @property  buildCompleted
16063      * True when the builder has completed building the interface.
16064      * @type Boolean
16065      */
16066     buildCompleted : false,
16067      
16068     /**
16069      * @property  topModule
16070      * the upper most module - uses document.element as it's constructor.
16071      * @type Object
16072      */
16073      
16074     topModule  : false,
16075       
16076     /**
16077      * @property  modules
16078      * array of modules to be created by registration system.
16079      * @type {Array} of Roo.XComponent
16080      */
16081     
16082     modules : [],
16083     /**
16084      * @property  elmodules
16085      * array of modules to be created by which use #ID 
16086      * @type {Array} of Roo.XComponent
16087      */
16088      
16089     elmodules : [],
16090
16091      /**
16092      * @property  build_from_html
16093      * Build elements from html - used by bootstrap HTML stuff 
16094      *    - this is cleared after build is completed
16095      * @type {boolean} true  (default false)
16096      */
16097      
16098     build_from_html : false,
16099
16100     /**
16101      * Register components to be built later.
16102      *
16103      * This solves the following issues
16104      * - Building is not done on page load, but after an authentication process has occured.
16105      * - Interface elements are registered on page load
16106      * - Parent Interface elements may not be loaded before child, so this handles that..
16107      * 
16108      *
16109      * example:
16110      * 
16111      * MyApp.register({
16112           order : '000001',
16113           module : 'Pman.Tab.projectMgr',
16114           region : 'center',
16115           parent : 'Pman.layout',
16116           disabled : false,  // or use a function..
16117         })
16118      
16119      * * @param {Object} details about module
16120      */
16121     register : function(obj) {
16122                 
16123         Roo.XComponent.event.fireEvent('register', obj);
16124         switch(typeof(obj.disabled) ) {
16125                 
16126             case 'undefined':
16127                 break;
16128             
16129             case 'function':
16130                 if ( obj.disabled() ) {
16131                         return;
16132                 }
16133                 break;
16134             
16135             default:
16136                 if (obj.disabled) {
16137                         return;
16138                 }
16139                 break;
16140         }
16141                 
16142         this.modules.push(obj);
16143          
16144     },
16145     /**
16146      * convert a string to an object..
16147      * eg. 'AAA.BBB' -> finds AAA.BBB
16148
16149      */
16150     
16151     toObject : function(str)
16152     {
16153         if (!str || typeof(str) == 'object') {
16154             return str;
16155         }
16156         if (str.substring(0,1) == '#') {
16157             return str;
16158         }
16159
16160         var ar = str.split('.');
16161         var rt, o;
16162         rt = ar.shift();
16163             /** eval:var:o */
16164         try {
16165             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16166         } catch (e) {
16167             throw "Module not found : " + str;
16168         }
16169         
16170         if (o === false) {
16171             throw "Module not found : " + str;
16172         }
16173         Roo.each(ar, function(e) {
16174             if (typeof(o[e]) == 'undefined') {
16175                 throw "Module not found : " + str;
16176             }
16177             o = o[e];
16178         });
16179         
16180         return o;
16181         
16182     },
16183     
16184     
16185     /**
16186      * move modules into their correct place in the tree..
16187      * 
16188      */
16189     preBuild : function ()
16190     {
16191         var _t = this;
16192         Roo.each(this.modules , function (obj)
16193         {
16194             Roo.XComponent.event.fireEvent('beforebuild', obj);
16195             
16196             var opar = obj.parent;
16197             try { 
16198                 obj.parent = this.toObject(opar);
16199             } catch(e) {
16200                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16201                 return;
16202             }
16203             
16204             if (!obj.parent) {
16205                 Roo.debug && Roo.log("GOT top level module");
16206                 Roo.debug && Roo.log(obj);
16207                 obj.modules = new Roo.util.MixedCollection(false, 
16208                     function(o) { return o.order + '' }
16209                 );
16210                 this.topModule = obj;
16211                 return;
16212             }
16213                         // parent is a string (usually a dom element name..)
16214             if (typeof(obj.parent) == 'string') {
16215                 this.elmodules.push(obj);
16216                 return;
16217             }
16218             if (obj.parent.constructor != Roo.XComponent) {
16219                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16220             }
16221             if (!obj.parent.modules) {
16222                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16223                     function(o) { return o.order + '' }
16224                 );
16225             }
16226             if (obj.parent.disabled) {
16227                 obj.disabled = true;
16228             }
16229             obj.parent.modules.add(obj);
16230         }, this);
16231     },
16232     
16233      /**
16234      * make a list of modules to build.
16235      * @return {Array} list of modules. 
16236      */ 
16237     
16238     buildOrder : function()
16239     {
16240         var _this = this;
16241         var cmp = function(a,b) {   
16242             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16243         };
16244         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16245             throw "No top level modules to build";
16246         }
16247         
16248         // make a flat list in order of modules to build.
16249         var mods = this.topModule ? [ this.topModule ] : [];
16250                 
16251         
16252         // elmodules (is a list of DOM based modules )
16253         Roo.each(this.elmodules, function(e) {
16254             mods.push(e);
16255             if (!this.topModule &&
16256                 typeof(e.parent) == 'string' &&
16257                 e.parent.substring(0,1) == '#' &&
16258                 Roo.get(e.parent.substr(1))
16259                ) {
16260                 
16261                 _this.topModule = e;
16262             }
16263             
16264         });
16265
16266         
16267         // add modules to their parents..
16268         var addMod = function(m) {
16269             Roo.debug && Roo.log("build Order: add: " + m.name);
16270                 
16271             mods.push(m);
16272             if (m.modules && !m.disabled) {
16273                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16274                 m.modules.keySort('ASC',  cmp );
16275                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16276     
16277                 m.modules.each(addMod);
16278             } else {
16279                 Roo.debug && Roo.log("build Order: no child modules");
16280             }
16281             // not sure if this is used any more..
16282             if (m.finalize) {
16283                 m.finalize.name = m.name + " (clean up) ";
16284                 mods.push(m.finalize);
16285             }
16286             
16287         }
16288         if (this.topModule && this.topModule.modules) { 
16289             this.topModule.modules.keySort('ASC',  cmp );
16290             this.topModule.modules.each(addMod);
16291         } 
16292         return mods;
16293     },
16294     
16295      /**
16296      * Build the registered modules.
16297      * @param {Object} parent element.
16298      * @param {Function} optional method to call after module has been added.
16299      * 
16300      */ 
16301    
16302     build : function(opts) 
16303     {
16304         
16305         if (typeof(opts) != 'undefined') {
16306             Roo.apply(this,opts);
16307         }
16308         
16309         this.preBuild();
16310         var mods = this.buildOrder();
16311       
16312         //this.allmods = mods;
16313         //Roo.debug && Roo.log(mods);
16314         //return;
16315         if (!mods.length) { // should not happen
16316             throw "NO modules!!!";
16317         }
16318         
16319         
16320         var msg = "Building Interface...";
16321         // flash it up as modal - so we store the mask!?
16322         if (!this.hideProgress && Roo.MessageBox) {
16323             Roo.MessageBox.show({ title: 'loading' });
16324             Roo.MessageBox.show({
16325                title: "Please wait...",
16326                msg: msg,
16327                width:450,
16328                progress:true,
16329                closable:false,
16330                modal: false
16331               
16332             });
16333         }
16334         var total = mods.length;
16335         
16336         var _this = this;
16337         var progressRun = function() {
16338             if (!mods.length) {
16339                 Roo.debug && Roo.log('hide?');
16340                 if (!this.hideProgress && Roo.MessageBox) {
16341                     Roo.MessageBox.hide();
16342                 }
16343                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16344                 
16345                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16346                 
16347                 // THE END...
16348                 return false;   
16349             }
16350             
16351             var m = mods.shift();
16352             
16353             
16354             Roo.debug && Roo.log(m);
16355             // not sure if this is supported any more.. - modules that are are just function
16356             if (typeof(m) == 'function') { 
16357                 m.call(this);
16358                 return progressRun.defer(10, _this);
16359             } 
16360             
16361             
16362             msg = "Building Interface " + (total  - mods.length) + 
16363                     " of " + total + 
16364                     (m.name ? (' - ' + m.name) : '');
16365                         Roo.debug && Roo.log(msg);
16366             if (!this.hideProgress &&  Roo.MessageBox) { 
16367                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16368             }
16369             
16370          
16371             // is the module disabled?
16372             var disabled = (typeof(m.disabled) == 'function') ?
16373                 m.disabled.call(m.module.disabled) : m.disabled;    
16374             
16375             
16376             if (disabled) {
16377                 return progressRun(); // we do not update the display!
16378             }
16379             
16380             // now build 
16381             
16382                         
16383                         
16384             m.render();
16385             // it's 10 on top level, and 1 on others??? why...
16386             return progressRun.defer(10, _this);
16387              
16388         }
16389         progressRun.defer(1, _this);
16390      
16391         
16392         
16393     },
16394         
16395         
16396         /**
16397          * Event Object.
16398          *
16399          *
16400          */
16401         event: false, 
16402     /**
16403          * wrapper for event.on - aliased later..  
16404          * Typically use to register a event handler for register:
16405          *
16406          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16407          *
16408          */
16409     on : false
16410    
16411     
16412     
16413 });
16414
16415 Roo.XComponent.event = new Roo.util.Observable({
16416                 events : { 
16417                         /**
16418                          * @event register
16419                          * Fires when an Component is registered,
16420                          * set the disable property on the Component to stop registration.
16421                          * @param {Roo.XComponent} c the component being registerd.
16422                          * 
16423                          */
16424                         'register' : true,
16425             /**
16426                          * @event beforebuild
16427                          * Fires before each Component is built
16428                          * can be used to apply permissions.
16429                          * @param {Roo.XComponent} c the component being registerd.
16430                          * 
16431                          */
16432                         'beforebuild' : true,
16433                         /**
16434                          * @event buildcomplete
16435                          * Fires on the top level element when all elements have been built
16436                          * @param {Roo.XComponent} the top level component.
16437                          */
16438                         'buildcomplete' : true
16439                         
16440                 }
16441 });
16442
16443 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16444  /*
16445  * Based on:
16446  * Ext JS Library 1.1.1
16447  * Copyright(c) 2006-2007, Ext JS, LLC.
16448  *
16449  * Originally Released Under LGPL - original licence link has changed is not relivant.
16450  *
16451  * Fork - LGPL
16452  * <script type="text/javascript">
16453  */
16454
16455
16456
16457 /*
16458  * These classes are derivatives of the similarly named classes in the YUI Library.
16459  * The original license:
16460  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16461  * Code licensed under the BSD License:
16462  * http://developer.yahoo.net/yui/license.txt
16463  */
16464
16465 (function() {
16466
16467 var Event=Roo.EventManager;
16468 var Dom=Roo.lib.Dom;
16469
16470 /**
16471  * @class Roo.dd.DragDrop
16472  * @extends Roo.util.Observable
16473  * Defines the interface and base operation of items that that can be
16474  * dragged or can be drop targets.  It was designed to be extended, overriding
16475  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16476  * Up to three html elements can be associated with a DragDrop instance:
16477  * <ul>
16478  * <li>linked element: the element that is passed into the constructor.
16479  * This is the element which defines the boundaries for interaction with
16480  * other DragDrop objects.</li>
16481  * <li>handle element(s): The drag operation only occurs if the element that
16482  * was clicked matches a handle element.  By default this is the linked
16483  * element, but there are times that you will want only a portion of the
16484  * linked element to initiate the drag operation, and the setHandleElId()
16485  * method provides a way to define this.</li>
16486  * <li>drag element: this represents the element that would be moved along
16487  * with the cursor during a drag operation.  By default, this is the linked
16488  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16489  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16490  * </li>
16491  * </ul>
16492  * This class should not be instantiated until the onload event to ensure that
16493  * the associated elements are available.
16494  * The following would define a DragDrop obj that would interact with any
16495  * other DragDrop obj in the "group1" group:
16496  * <pre>
16497  *  dd = new Roo.dd.DragDrop("div1", "group1");
16498  * </pre>
16499  * Since none of the event handlers have been implemented, nothing would
16500  * actually happen if you were to run the code above.  Normally you would
16501  * override this class or one of the default implementations, but you can
16502  * also override the methods you want on an instance of the class...
16503  * <pre>
16504  *  dd.onDragDrop = function(e, id) {
16505  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16506  *  }
16507  * </pre>
16508  * @constructor
16509  * @param {String} id of the element that is linked to this instance
16510  * @param {String} sGroup the group of related DragDrop objects
16511  * @param {object} config an object containing configurable attributes
16512  *                Valid properties for DragDrop:
16513  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16514  */
16515 Roo.dd.DragDrop = function(id, sGroup, config) {
16516     if (id) {
16517         this.init(id, sGroup, config);
16518     }
16519     
16520 };
16521
16522 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16523
16524     /**
16525      * The id of the element associated with this object.  This is what we
16526      * refer to as the "linked element" because the size and position of
16527      * this element is used to determine when the drag and drop objects have
16528      * interacted.
16529      * @property id
16530      * @type String
16531      */
16532     id: null,
16533
16534     /**
16535      * Configuration attributes passed into the constructor
16536      * @property config
16537      * @type object
16538      */
16539     config: null,
16540
16541     /**
16542      * The id of the element that will be dragged.  By default this is same
16543      * as the linked element , but could be changed to another element. Ex:
16544      * Roo.dd.DDProxy
16545      * @property dragElId
16546      * @type String
16547      * @private
16548      */
16549     dragElId: null,
16550
16551     /**
16552      * the id of the element that initiates the drag operation.  By default
16553      * this is the linked element, but could be changed to be a child of this
16554      * element.  This lets us do things like only starting the drag when the
16555      * header element within the linked html element is clicked.
16556      * @property handleElId
16557      * @type String
16558      * @private
16559      */
16560     handleElId: null,
16561
16562     /**
16563      * An associative array of HTML tags that will be ignored if clicked.
16564      * @property invalidHandleTypes
16565      * @type {string: string}
16566      */
16567     invalidHandleTypes: null,
16568
16569     /**
16570      * An associative array of ids for elements that will be ignored if clicked
16571      * @property invalidHandleIds
16572      * @type {string: string}
16573      */
16574     invalidHandleIds: null,
16575
16576     /**
16577      * An indexted array of css class names for elements that will be ignored
16578      * if clicked.
16579      * @property invalidHandleClasses
16580      * @type string[]
16581      */
16582     invalidHandleClasses: null,
16583
16584     /**
16585      * The linked element's absolute X position at the time the drag was
16586      * started
16587      * @property startPageX
16588      * @type int
16589      * @private
16590      */
16591     startPageX: 0,
16592
16593     /**
16594      * The linked element's absolute X position at the time the drag was
16595      * started
16596      * @property startPageY
16597      * @type int
16598      * @private
16599      */
16600     startPageY: 0,
16601
16602     /**
16603      * The group defines a logical collection of DragDrop objects that are
16604      * related.  Instances only get events when interacting with other
16605      * DragDrop object in the same group.  This lets us define multiple
16606      * groups using a single DragDrop subclass if we want.
16607      * @property groups
16608      * @type {string: string}
16609      */
16610     groups: null,
16611
16612     /**
16613      * Individual drag/drop instances can be locked.  This will prevent
16614      * onmousedown start drag.
16615      * @property locked
16616      * @type boolean
16617      * @private
16618      */
16619     locked: false,
16620
16621     /**
16622      * Lock this instance
16623      * @method lock
16624      */
16625     lock: function() { this.locked = true; },
16626
16627     /**
16628      * Unlock this instace
16629      * @method unlock
16630      */
16631     unlock: function() { this.locked = false; },
16632
16633     /**
16634      * By default, all insances can be a drop target.  This can be disabled by
16635      * setting isTarget to false.
16636      * @method isTarget
16637      * @type boolean
16638      */
16639     isTarget: true,
16640
16641     /**
16642      * The padding configured for this drag and drop object for calculating
16643      * the drop zone intersection with this object.
16644      * @method padding
16645      * @type int[]
16646      */
16647     padding: null,
16648
16649     /**
16650      * Cached reference to the linked element
16651      * @property _domRef
16652      * @private
16653      */
16654     _domRef: null,
16655
16656     /**
16657      * Internal typeof flag
16658      * @property __ygDragDrop
16659      * @private
16660      */
16661     __ygDragDrop: true,
16662
16663     /**
16664      * Set to true when horizontal contraints are applied
16665      * @property constrainX
16666      * @type boolean
16667      * @private
16668      */
16669     constrainX: false,
16670
16671     /**
16672      * Set to true when vertical contraints are applied
16673      * @property constrainY
16674      * @type boolean
16675      * @private
16676      */
16677     constrainY: false,
16678
16679     /**
16680      * The left constraint
16681      * @property minX
16682      * @type int
16683      * @private
16684      */
16685     minX: 0,
16686
16687     /**
16688      * The right constraint
16689      * @property maxX
16690      * @type int
16691      * @private
16692      */
16693     maxX: 0,
16694
16695     /**
16696      * The up constraint
16697      * @property minY
16698      * @type int
16699      * @type int
16700      * @private
16701      */
16702     minY: 0,
16703
16704     /**
16705      * The down constraint
16706      * @property maxY
16707      * @type int
16708      * @private
16709      */
16710     maxY: 0,
16711
16712     /**
16713      * Maintain offsets when we resetconstraints.  Set to true when you want
16714      * the position of the element relative to its parent to stay the same
16715      * when the page changes
16716      *
16717      * @property maintainOffset
16718      * @type boolean
16719      */
16720     maintainOffset: false,
16721
16722     /**
16723      * Array of pixel locations the element will snap to if we specified a
16724      * horizontal graduation/interval.  This array is generated automatically
16725      * when you define a tick interval.
16726      * @property xTicks
16727      * @type int[]
16728      */
16729     xTicks: null,
16730
16731     /**
16732      * Array of pixel locations the element will snap to if we specified a
16733      * vertical graduation/interval.  This array is generated automatically
16734      * when you define a tick interval.
16735      * @property yTicks
16736      * @type int[]
16737      */
16738     yTicks: null,
16739
16740     /**
16741      * By default the drag and drop instance will only respond to the primary
16742      * button click (left button for a right-handed mouse).  Set to true to
16743      * allow drag and drop to start with any mouse click that is propogated
16744      * by the browser
16745      * @property primaryButtonOnly
16746      * @type boolean
16747      */
16748     primaryButtonOnly: true,
16749
16750     /**
16751      * The availabe property is false until the linked dom element is accessible.
16752      * @property available
16753      * @type boolean
16754      */
16755     available: false,
16756
16757     /**
16758      * By default, drags can only be initiated if the mousedown occurs in the
16759      * region the linked element is.  This is done in part to work around a
16760      * bug in some browsers that mis-report the mousedown if the previous
16761      * mouseup happened outside of the window.  This property is set to true
16762      * if outer handles are defined.
16763      *
16764      * @property hasOuterHandles
16765      * @type boolean
16766      * @default false
16767      */
16768     hasOuterHandles: false,
16769
16770     /**
16771      * Code that executes immediately before the startDrag event
16772      * @method b4StartDrag
16773      * @private
16774      */
16775     b4StartDrag: function(x, y) { },
16776
16777     /**
16778      * Abstract method called after a drag/drop object is clicked
16779      * and the drag or mousedown time thresholds have beeen met.
16780      * @method startDrag
16781      * @param {int} X click location
16782      * @param {int} Y click location
16783      */
16784     startDrag: function(x, y) { /* override this */ },
16785
16786     /**
16787      * Code that executes immediately before the onDrag event
16788      * @method b4Drag
16789      * @private
16790      */
16791     b4Drag: function(e) { },
16792
16793     /**
16794      * Abstract method called during the onMouseMove event while dragging an
16795      * object.
16796      * @method onDrag
16797      * @param {Event} e the mousemove event
16798      */
16799     onDrag: function(e) { /* override this */ },
16800
16801     /**
16802      * Abstract method called when this element fist begins hovering over
16803      * another DragDrop obj
16804      * @method onDragEnter
16805      * @param {Event} e the mousemove event
16806      * @param {String|DragDrop[]} id In POINT mode, the element
16807      * id this is hovering over.  In INTERSECT mode, an array of one or more
16808      * dragdrop items being hovered over.
16809      */
16810     onDragEnter: function(e, id) { /* override this */ },
16811
16812     /**
16813      * Code that executes immediately before the onDragOver event
16814      * @method b4DragOver
16815      * @private
16816      */
16817     b4DragOver: function(e) { },
16818
16819     /**
16820      * Abstract method called when this element is hovering over another
16821      * DragDrop obj
16822      * @method onDragOver
16823      * @param {Event} e the mousemove event
16824      * @param {String|DragDrop[]} id In POINT mode, the element
16825      * id this is hovering over.  In INTERSECT mode, an array of dd items
16826      * being hovered over.
16827      */
16828     onDragOver: function(e, id) { /* override this */ },
16829
16830     /**
16831      * Code that executes immediately before the onDragOut event
16832      * @method b4DragOut
16833      * @private
16834      */
16835     b4DragOut: function(e) { },
16836
16837     /**
16838      * Abstract method called when we are no longer hovering over an element
16839      * @method onDragOut
16840      * @param {Event} e the mousemove event
16841      * @param {String|DragDrop[]} id In POINT mode, the element
16842      * id this was hovering over.  In INTERSECT mode, an array of dd items
16843      * that the mouse is no longer over.
16844      */
16845     onDragOut: function(e, id) { /* override this */ },
16846
16847     /**
16848      * Code that executes immediately before the onDragDrop event
16849      * @method b4DragDrop
16850      * @private
16851      */
16852     b4DragDrop: function(e) { },
16853
16854     /**
16855      * Abstract method called when this item is dropped on another DragDrop
16856      * obj
16857      * @method onDragDrop
16858      * @param {Event} e the mouseup event
16859      * @param {String|DragDrop[]} id In POINT mode, the element
16860      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16861      * was dropped on.
16862      */
16863     onDragDrop: function(e, id) { /* override this */ },
16864
16865     /**
16866      * Abstract method called when this item is dropped on an area with no
16867      * drop target
16868      * @method onInvalidDrop
16869      * @param {Event} e the mouseup event
16870      */
16871     onInvalidDrop: function(e) { /* override this */ },
16872
16873     /**
16874      * Code that executes immediately before the endDrag event
16875      * @method b4EndDrag
16876      * @private
16877      */
16878     b4EndDrag: function(e) { },
16879
16880     /**
16881      * Fired when we are done dragging the object
16882      * @method endDrag
16883      * @param {Event} e the mouseup event
16884      */
16885     endDrag: function(e) { /* override this */ },
16886
16887     /**
16888      * Code executed immediately before the onMouseDown event
16889      * @method b4MouseDown
16890      * @param {Event} e the mousedown event
16891      * @private
16892      */
16893     b4MouseDown: function(e) {  },
16894
16895     /**
16896      * Event handler that fires when a drag/drop obj gets a mousedown
16897      * @method onMouseDown
16898      * @param {Event} e the mousedown event
16899      */
16900     onMouseDown: function(e) { /* override this */ },
16901
16902     /**
16903      * Event handler that fires when a drag/drop obj gets a mouseup
16904      * @method onMouseUp
16905      * @param {Event} e the mouseup event
16906      */
16907     onMouseUp: function(e) { /* override this */ },
16908
16909     /**
16910      * Override the onAvailable method to do what is needed after the initial
16911      * position was determined.
16912      * @method onAvailable
16913      */
16914     onAvailable: function () {
16915     },
16916
16917     /*
16918      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16919      * @type Object
16920      */
16921     defaultPadding : {left:0, right:0, top:0, bottom:0},
16922
16923     /*
16924      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16925  *
16926  * Usage:
16927  <pre><code>
16928  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16929                 { dragElId: "existingProxyDiv" });
16930  dd.startDrag = function(){
16931      this.constrainTo("parent-id");
16932  };
16933  </code></pre>
16934  * Or you can initalize it using the {@link Roo.Element} object:
16935  <pre><code>
16936  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16937      startDrag : function(){
16938          this.constrainTo("parent-id");
16939      }
16940  });
16941  </code></pre>
16942      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16943      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16944      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16945      * an object containing the sides to pad. For example: {right:10, bottom:10}
16946      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16947      */
16948     constrainTo : function(constrainTo, pad, inContent){
16949         if(typeof pad == "number"){
16950             pad = {left: pad, right:pad, top:pad, bottom:pad};
16951         }
16952         pad = pad || this.defaultPadding;
16953         var b = Roo.get(this.getEl()).getBox();
16954         var ce = Roo.get(constrainTo);
16955         var s = ce.getScroll();
16956         var c, cd = ce.dom;
16957         if(cd == document.body){
16958             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16959         }else{
16960             xy = ce.getXY();
16961             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16962         }
16963
16964
16965         var topSpace = b.y - c.y;
16966         var leftSpace = b.x - c.x;
16967
16968         this.resetConstraints();
16969         this.setXConstraint(leftSpace - (pad.left||0), // left
16970                 c.width - leftSpace - b.width - (pad.right||0) //right
16971         );
16972         this.setYConstraint(topSpace - (pad.top||0), //top
16973                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16974         );
16975     },
16976
16977     /**
16978      * Returns a reference to the linked element
16979      * @method getEl
16980      * @return {HTMLElement} the html element
16981      */
16982     getEl: function() {
16983         if (!this._domRef) {
16984             this._domRef = Roo.getDom(this.id);
16985         }
16986
16987         return this._domRef;
16988     },
16989
16990     /**
16991      * Returns a reference to the actual element to drag.  By default this is
16992      * the same as the html element, but it can be assigned to another
16993      * element. An example of this can be found in Roo.dd.DDProxy
16994      * @method getDragEl
16995      * @return {HTMLElement} the html element
16996      */
16997     getDragEl: function() {
16998         return Roo.getDom(this.dragElId);
16999     },
17000
17001     /**
17002      * Sets up the DragDrop object.  Must be called in the constructor of any
17003      * Roo.dd.DragDrop subclass
17004      * @method init
17005      * @param id the id of the linked element
17006      * @param {String} sGroup the group of related items
17007      * @param {object} config configuration attributes
17008      */
17009     init: function(id, sGroup, config) {
17010         this.initTarget(id, sGroup, config);
17011         if (!Roo.isTouch) {
17012             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17013         }
17014         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17015         // Event.on(this.id, "selectstart", Event.preventDefault);
17016     },
17017
17018     /**
17019      * Initializes Targeting functionality only... the object does not
17020      * get a mousedown handler.
17021      * @method initTarget
17022      * @param id the id of the linked element
17023      * @param {String} sGroup the group of related items
17024      * @param {object} config configuration attributes
17025      */
17026     initTarget: function(id, sGroup, config) {
17027
17028         // configuration attributes
17029         this.config = config || {};
17030
17031         // create a local reference to the drag and drop manager
17032         this.DDM = Roo.dd.DDM;
17033         // initialize the groups array
17034         this.groups = {};
17035
17036         // assume that we have an element reference instead of an id if the
17037         // parameter is not a string
17038         if (typeof id !== "string") {
17039             id = Roo.id(id);
17040         }
17041
17042         // set the id
17043         this.id = id;
17044
17045         // add to an interaction group
17046         this.addToGroup((sGroup) ? sGroup : "default");
17047
17048         // We don't want to register this as the handle with the manager
17049         // so we just set the id rather than calling the setter.
17050         this.handleElId = id;
17051
17052         // the linked element is the element that gets dragged by default
17053         this.setDragElId(id);
17054
17055         // by default, clicked anchors will not start drag operations.
17056         this.invalidHandleTypes = { A: "A" };
17057         this.invalidHandleIds = {};
17058         this.invalidHandleClasses = [];
17059
17060         this.applyConfig();
17061
17062         this.handleOnAvailable();
17063     },
17064
17065     /**
17066      * Applies the configuration parameters that were passed into the constructor.
17067      * This is supposed to happen at each level through the inheritance chain.  So
17068      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17069      * DragDrop in order to get all of the parameters that are available in
17070      * each object.
17071      * @method applyConfig
17072      */
17073     applyConfig: function() {
17074
17075         // configurable properties:
17076         //    padding, isTarget, maintainOffset, primaryButtonOnly
17077         this.padding           = this.config.padding || [0, 0, 0, 0];
17078         this.isTarget          = (this.config.isTarget !== false);
17079         this.maintainOffset    = (this.config.maintainOffset);
17080         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17081
17082     },
17083
17084     /**
17085      * Executed when the linked element is available
17086      * @method handleOnAvailable
17087      * @private
17088      */
17089     handleOnAvailable: function() {
17090         this.available = true;
17091         this.resetConstraints();
17092         this.onAvailable();
17093     },
17094
17095      /**
17096      * Configures the padding for the target zone in px.  Effectively expands
17097      * (or reduces) the virtual object size for targeting calculations.
17098      * Supports css-style shorthand; if only one parameter is passed, all sides
17099      * will have that padding, and if only two are passed, the top and bottom
17100      * will have the first param, the left and right the second.
17101      * @method setPadding
17102      * @param {int} iTop    Top pad
17103      * @param {int} iRight  Right pad
17104      * @param {int} iBot    Bot pad
17105      * @param {int} iLeft   Left pad
17106      */
17107     setPadding: function(iTop, iRight, iBot, iLeft) {
17108         // this.padding = [iLeft, iRight, iTop, iBot];
17109         if (!iRight && 0 !== iRight) {
17110             this.padding = [iTop, iTop, iTop, iTop];
17111         } else if (!iBot && 0 !== iBot) {
17112             this.padding = [iTop, iRight, iTop, iRight];
17113         } else {
17114             this.padding = [iTop, iRight, iBot, iLeft];
17115         }
17116     },
17117
17118     /**
17119      * Stores the initial placement of the linked element.
17120      * @method setInitialPosition
17121      * @param {int} diffX   the X offset, default 0
17122      * @param {int} diffY   the Y offset, default 0
17123      */
17124     setInitPosition: function(diffX, diffY) {
17125         var el = this.getEl();
17126
17127         if (!this.DDM.verifyEl(el)) {
17128             return;
17129         }
17130
17131         var dx = diffX || 0;
17132         var dy = diffY || 0;
17133
17134         var p = Dom.getXY( el );
17135
17136         this.initPageX = p[0] - dx;
17137         this.initPageY = p[1] - dy;
17138
17139         this.lastPageX = p[0];
17140         this.lastPageY = p[1];
17141
17142
17143         this.setStartPosition(p);
17144     },
17145
17146     /**
17147      * Sets the start position of the element.  This is set when the obj
17148      * is initialized, the reset when a drag is started.
17149      * @method setStartPosition
17150      * @param pos current position (from previous lookup)
17151      * @private
17152      */
17153     setStartPosition: function(pos) {
17154         var p = pos || Dom.getXY( this.getEl() );
17155         this.deltaSetXY = null;
17156
17157         this.startPageX = p[0];
17158         this.startPageY = p[1];
17159     },
17160
17161     /**
17162      * Add this instance to a group of related drag/drop objects.  All
17163      * instances belong to at least one group, and can belong to as many
17164      * groups as needed.
17165      * @method addToGroup
17166      * @param sGroup {string} the name of the group
17167      */
17168     addToGroup: function(sGroup) {
17169         this.groups[sGroup] = true;
17170         this.DDM.regDragDrop(this, sGroup);
17171     },
17172
17173     /**
17174      * Remove's this instance from the supplied interaction group
17175      * @method removeFromGroup
17176      * @param {string}  sGroup  The group to drop
17177      */
17178     removeFromGroup: function(sGroup) {
17179         if (this.groups[sGroup]) {
17180             delete this.groups[sGroup];
17181         }
17182
17183         this.DDM.removeDDFromGroup(this, sGroup);
17184     },
17185
17186     /**
17187      * Allows you to specify that an element other than the linked element
17188      * will be moved with the cursor during a drag
17189      * @method setDragElId
17190      * @param id {string} the id of the element that will be used to initiate the drag
17191      */
17192     setDragElId: function(id) {
17193         this.dragElId = id;
17194     },
17195
17196     /**
17197      * Allows you to specify a child of the linked element that should be
17198      * used to initiate the drag operation.  An example of this would be if
17199      * you have a content div with text and links.  Clicking anywhere in the
17200      * content area would normally start the drag operation.  Use this method
17201      * to specify that an element inside of the content div is the element
17202      * that starts the drag operation.
17203      * @method setHandleElId
17204      * @param id {string} the id of the element that will be used to
17205      * initiate the drag.
17206      */
17207     setHandleElId: function(id) {
17208         if (typeof id !== "string") {
17209             id = Roo.id(id);
17210         }
17211         this.handleElId = id;
17212         this.DDM.regHandle(this.id, id);
17213     },
17214
17215     /**
17216      * Allows you to set an element outside of the linked element as a drag
17217      * handle
17218      * @method setOuterHandleElId
17219      * @param id the id of the element that will be used to initiate the drag
17220      */
17221     setOuterHandleElId: function(id) {
17222         if (typeof id !== "string") {
17223             id = Roo.id(id);
17224         }
17225         Event.on(id, "mousedown",
17226                 this.handleMouseDown, this);
17227         this.setHandleElId(id);
17228
17229         this.hasOuterHandles = true;
17230     },
17231
17232     /**
17233      * Remove all drag and drop hooks for this element
17234      * @method unreg
17235      */
17236     unreg: function() {
17237         Event.un(this.id, "mousedown",
17238                 this.handleMouseDown);
17239         Event.un(this.id, "touchstart",
17240                 this.handleMouseDown);
17241         this._domRef = null;
17242         this.DDM._remove(this);
17243     },
17244
17245     destroy : function(){
17246         this.unreg();
17247     },
17248
17249     /**
17250      * Returns true if this instance is locked, or the drag drop mgr is locked
17251      * (meaning that all drag/drop is disabled on the page.)
17252      * @method isLocked
17253      * @return {boolean} true if this obj or all drag/drop is locked, else
17254      * false
17255      */
17256     isLocked: function() {
17257         return (this.DDM.isLocked() || this.locked);
17258     },
17259
17260     /**
17261      * Fired when this object is clicked
17262      * @method handleMouseDown
17263      * @param {Event} e
17264      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17265      * @private
17266      */
17267     handleMouseDown: function(e, oDD){
17268      
17269         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17270             //Roo.log('not touch/ button !=0');
17271             return;
17272         }
17273         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17274             return; // double touch..
17275         }
17276         
17277
17278         if (this.isLocked()) {
17279             //Roo.log('locked');
17280             return;
17281         }
17282
17283         this.DDM.refreshCache(this.groups);
17284 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17285         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17286         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17287             //Roo.log('no outer handes or not over target');
17288                 // do nothing.
17289         } else {
17290 //            Roo.log('check validator');
17291             if (this.clickValidator(e)) {
17292 //                Roo.log('validate success');
17293                 // set the initial element position
17294                 this.setStartPosition();
17295
17296
17297                 this.b4MouseDown(e);
17298                 this.onMouseDown(e);
17299
17300                 this.DDM.handleMouseDown(e, this);
17301
17302                 this.DDM.stopEvent(e);
17303             } else {
17304
17305
17306             }
17307         }
17308     },
17309
17310     clickValidator: function(e) {
17311         var target = e.getTarget();
17312         return ( this.isValidHandleChild(target) &&
17313                     (this.id == this.handleElId ||
17314                         this.DDM.handleWasClicked(target, this.id)) );
17315     },
17316
17317     /**
17318      * Allows you to specify a tag name that should not start a drag operation
17319      * when clicked.  This is designed to facilitate embedding links within a
17320      * drag handle that do something other than start the drag.
17321      * @method addInvalidHandleType
17322      * @param {string} tagName the type of element to exclude
17323      */
17324     addInvalidHandleType: function(tagName) {
17325         var type = tagName.toUpperCase();
17326         this.invalidHandleTypes[type] = type;
17327     },
17328
17329     /**
17330      * Lets you to specify an element id for a child of a drag handle
17331      * that should not initiate a drag
17332      * @method addInvalidHandleId
17333      * @param {string} id the element id of the element you wish to ignore
17334      */
17335     addInvalidHandleId: function(id) {
17336         if (typeof id !== "string") {
17337             id = Roo.id(id);
17338         }
17339         this.invalidHandleIds[id] = id;
17340     },
17341
17342     /**
17343      * Lets you specify a css class of elements that will not initiate a drag
17344      * @method addInvalidHandleClass
17345      * @param {string} cssClass the class of the elements you wish to ignore
17346      */
17347     addInvalidHandleClass: function(cssClass) {
17348         this.invalidHandleClasses.push(cssClass);
17349     },
17350
17351     /**
17352      * Unsets an excluded tag name set by addInvalidHandleType
17353      * @method removeInvalidHandleType
17354      * @param {string} tagName the type of element to unexclude
17355      */
17356     removeInvalidHandleType: function(tagName) {
17357         var type = tagName.toUpperCase();
17358         // this.invalidHandleTypes[type] = null;
17359         delete this.invalidHandleTypes[type];
17360     },
17361
17362     /**
17363      * Unsets an invalid handle id
17364      * @method removeInvalidHandleId
17365      * @param {string} id the id of the element to re-enable
17366      */
17367     removeInvalidHandleId: function(id) {
17368         if (typeof id !== "string") {
17369             id = Roo.id(id);
17370         }
17371         delete this.invalidHandleIds[id];
17372     },
17373
17374     /**
17375      * Unsets an invalid css class
17376      * @method removeInvalidHandleClass
17377      * @param {string} cssClass the class of the element(s) you wish to
17378      * re-enable
17379      */
17380     removeInvalidHandleClass: function(cssClass) {
17381         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17382             if (this.invalidHandleClasses[i] == cssClass) {
17383                 delete this.invalidHandleClasses[i];
17384             }
17385         }
17386     },
17387
17388     /**
17389      * Checks the tag exclusion list to see if this click should be ignored
17390      * @method isValidHandleChild
17391      * @param {HTMLElement} node the HTMLElement to evaluate
17392      * @return {boolean} true if this is a valid tag type, false if not
17393      */
17394     isValidHandleChild: function(node) {
17395
17396         var valid = true;
17397         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17398         var nodeName;
17399         try {
17400             nodeName = node.nodeName.toUpperCase();
17401         } catch(e) {
17402             nodeName = node.nodeName;
17403         }
17404         valid = valid && !this.invalidHandleTypes[nodeName];
17405         valid = valid && !this.invalidHandleIds[node.id];
17406
17407         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17408             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17409         }
17410
17411
17412         return valid;
17413
17414     },
17415
17416     /**
17417      * Create the array of horizontal tick marks if an interval was specified
17418      * in setXConstraint().
17419      * @method setXTicks
17420      * @private
17421      */
17422     setXTicks: function(iStartX, iTickSize) {
17423         this.xTicks = [];
17424         this.xTickSize = iTickSize;
17425
17426         var tickMap = {};
17427
17428         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17429             if (!tickMap[i]) {
17430                 this.xTicks[this.xTicks.length] = i;
17431                 tickMap[i] = true;
17432             }
17433         }
17434
17435         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17436             if (!tickMap[i]) {
17437                 this.xTicks[this.xTicks.length] = i;
17438                 tickMap[i] = true;
17439             }
17440         }
17441
17442         this.xTicks.sort(this.DDM.numericSort) ;
17443     },
17444
17445     /**
17446      * Create the array of vertical tick marks if an interval was specified in
17447      * setYConstraint().
17448      * @method setYTicks
17449      * @private
17450      */
17451     setYTicks: function(iStartY, iTickSize) {
17452         this.yTicks = [];
17453         this.yTickSize = iTickSize;
17454
17455         var tickMap = {};
17456
17457         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17458             if (!tickMap[i]) {
17459                 this.yTicks[this.yTicks.length] = i;
17460                 tickMap[i] = true;
17461             }
17462         }
17463
17464         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17465             if (!tickMap[i]) {
17466                 this.yTicks[this.yTicks.length] = i;
17467                 tickMap[i] = true;
17468             }
17469         }
17470
17471         this.yTicks.sort(this.DDM.numericSort) ;
17472     },
17473
17474     /**
17475      * By default, the element can be dragged any place on the screen.  Use
17476      * this method to limit the horizontal travel of the element.  Pass in
17477      * 0,0 for the parameters if you want to lock the drag to the y axis.
17478      * @method setXConstraint
17479      * @param {int} iLeft the number of pixels the element can move to the left
17480      * @param {int} iRight the number of pixels the element can move to the
17481      * right
17482      * @param {int} iTickSize optional parameter for specifying that the
17483      * element
17484      * should move iTickSize pixels at a time.
17485      */
17486     setXConstraint: function(iLeft, iRight, iTickSize) {
17487         this.leftConstraint = iLeft;
17488         this.rightConstraint = iRight;
17489
17490         this.minX = this.initPageX - iLeft;
17491         this.maxX = this.initPageX + iRight;
17492         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17493
17494         this.constrainX = true;
17495     },
17496
17497     /**
17498      * Clears any constraints applied to this instance.  Also clears ticks
17499      * since they can't exist independent of a constraint at this time.
17500      * @method clearConstraints
17501      */
17502     clearConstraints: function() {
17503         this.constrainX = false;
17504         this.constrainY = false;
17505         this.clearTicks();
17506     },
17507
17508     /**
17509      * Clears any tick interval defined for this instance
17510      * @method clearTicks
17511      */
17512     clearTicks: function() {
17513         this.xTicks = null;
17514         this.yTicks = null;
17515         this.xTickSize = 0;
17516         this.yTickSize = 0;
17517     },
17518
17519     /**
17520      * By default, the element can be dragged any place on the screen.  Set
17521      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17522      * parameters if you want to lock the drag to the x axis.
17523      * @method setYConstraint
17524      * @param {int} iUp the number of pixels the element can move up
17525      * @param {int} iDown the number of pixels the element can move down
17526      * @param {int} iTickSize optional parameter for specifying that the
17527      * element should move iTickSize pixels at a time.
17528      */
17529     setYConstraint: function(iUp, iDown, iTickSize) {
17530         this.topConstraint = iUp;
17531         this.bottomConstraint = iDown;
17532
17533         this.minY = this.initPageY - iUp;
17534         this.maxY = this.initPageY + iDown;
17535         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17536
17537         this.constrainY = true;
17538
17539     },
17540
17541     /**
17542      * resetConstraints must be called if you manually reposition a dd element.
17543      * @method resetConstraints
17544      * @param {boolean} maintainOffset
17545      */
17546     resetConstraints: function() {
17547
17548
17549         // Maintain offsets if necessary
17550         if (this.initPageX || this.initPageX === 0) {
17551             // figure out how much this thing has moved
17552             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17553             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17554
17555             this.setInitPosition(dx, dy);
17556
17557         // This is the first time we have detected the element's position
17558         } else {
17559             this.setInitPosition();
17560         }
17561
17562         if (this.constrainX) {
17563             this.setXConstraint( this.leftConstraint,
17564                                  this.rightConstraint,
17565                                  this.xTickSize        );
17566         }
17567
17568         if (this.constrainY) {
17569             this.setYConstraint( this.topConstraint,
17570                                  this.bottomConstraint,
17571                                  this.yTickSize         );
17572         }
17573     },
17574
17575     /**
17576      * Normally the drag element is moved pixel by pixel, but we can specify
17577      * that it move a number of pixels at a time.  This method resolves the
17578      * location when we have it set up like this.
17579      * @method getTick
17580      * @param {int} val where we want to place the object
17581      * @param {int[]} tickArray sorted array of valid points
17582      * @return {int} the closest tick
17583      * @private
17584      */
17585     getTick: function(val, tickArray) {
17586
17587         if (!tickArray) {
17588             // If tick interval is not defined, it is effectively 1 pixel,
17589             // so we return the value passed to us.
17590             return val;
17591         } else if (tickArray[0] >= val) {
17592             // The value is lower than the first tick, so we return the first
17593             // tick.
17594             return tickArray[0];
17595         } else {
17596             for (var i=0, len=tickArray.length; i<len; ++i) {
17597                 var next = i + 1;
17598                 if (tickArray[next] && tickArray[next] >= val) {
17599                     var diff1 = val - tickArray[i];
17600                     var diff2 = tickArray[next] - val;
17601                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17602                 }
17603             }
17604
17605             // The value is larger than the last tick, so we return the last
17606             // tick.
17607             return tickArray[tickArray.length - 1];
17608         }
17609     },
17610
17611     /**
17612      * toString method
17613      * @method toString
17614      * @return {string} string representation of the dd obj
17615      */
17616     toString: function() {
17617         return ("DragDrop " + this.id);
17618     }
17619
17620 });
17621
17622 })();
17623 /*
17624  * Based on:
17625  * Ext JS Library 1.1.1
17626  * Copyright(c) 2006-2007, Ext JS, LLC.
17627  *
17628  * Originally Released Under LGPL - original licence link has changed is not relivant.
17629  *
17630  * Fork - LGPL
17631  * <script type="text/javascript">
17632  */
17633
17634
17635 /**
17636  * The drag and drop utility provides a framework for building drag and drop
17637  * applications.  In addition to enabling drag and drop for specific elements,
17638  * the drag and drop elements are tracked by the manager class, and the
17639  * interactions between the various elements are tracked during the drag and
17640  * the implementing code is notified about these important moments.
17641  */
17642
17643 // Only load the library once.  Rewriting the manager class would orphan
17644 // existing drag and drop instances.
17645 if (!Roo.dd.DragDropMgr) {
17646
17647 /**
17648  * @class Roo.dd.DragDropMgr
17649  * DragDropMgr is a singleton that tracks the element interaction for
17650  * all DragDrop items in the window.  Generally, you will not call
17651  * this class directly, but it does have helper methods that could
17652  * be useful in your DragDrop implementations.
17653  * @singleton
17654  */
17655 Roo.dd.DragDropMgr = function() {
17656
17657     var Event = Roo.EventManager;
17658
17659     return {
17660
17661         /**
17662          * Two dimensional Array of registered DragDrop objects.  The first
17663          * dimension is the DragDrop item group, the second the DragDrop
17664          * object.
17665          * @property ids
17666          * @type {string: string}
17667          * @private
17668          * @static
17669          */
17670         ids: {},
17671
17672         /**
17673          * Array of element ids defined as drag handles.  Used to determine
17674          * if the element that generated the mousedown event is actually the
17675          * handle and not the html element itself.
17676          * @property handleIds
17677          * @type {string: string}
17678          * @private
17679          * @static
17680          */
17681         handleIds: {},
17682
17683         /**
17684          * the DragDrop object that is currently being dragged
17685          * @property dragCurrent
17686          * @type DragDrop
17687          * @private
17688          * @static
17689          **/
17690         dragCurrent: null,
17691
17692         /**
17693          * the DragDrop object(s) that are being hovered over
17694          * @property dragOvers
17695          * @type Array
17696          * @private
17697          * @static
17698          */
17699         dragOvers: {},
17700
17701         /**
17702          * the X distance between the cursor and the object being dragged
17703          * @property deltaX
17704          * @type int
17705          * @private
17706          * @static
17707          */
17708         deltaX: 0,
17709
17710         /**
17711          * the Y distance between the cursor and the object being dragged
17712          * @property deltaY
17713          * @type int
17714          * @private
17715          * @static
17716          */
17717         deltaY: 0,
17718
17719         /**
17720          * Flag to determine if we should prevent the default behavior of the
17721          * events we define. By default this is true, but this can be set to
17722          * false if you need the default behavior (not recommended)
17723          * @property preventDefault
17724          * @type boolean
17725          * @static
17726          */
17727         preventDefault: true,
17728
17729         /**
17730          * Flag to determine if we should stop the propagation of the events
17731          * we generate. This is true by default but you may want to set it to
17732          * false if the html element contains other features that require the
17733          * mouse click.
17734          * @property stopPropagation
17735          * @type boolean
17736          * @static
17737          */
17738         stopPropagation: true,
17739
17740         /**
17741          * Internal flag that is set to true when drag and drop has been
17742          * intialized
17743          * @property initialized
17744          * @private
17745          * @static
17746          */
17747         initalized: false,
17748
17749         /**
17750          * All drag and drop can be disabled.
17751          * @property locked
17752          * @private
17753          * @static
17754          */
17755         locked: false,
17756
17757         /**
17758          * Called the first time an element is registered.
17759          * @method init
17760          * @private
17761          * @static
17762          */
17763         init: function() {
17764             this.initialized = true;
17765         },
17766
17767         /**
17768          * In point mode, drag and drop interaction is defined by the
17769          * location of the cursor during the drag/drop
17770          * @property POINT
17771          * @type int
17772          * @static
17773          */
17774         POINT: 0,
17775
17776         /**
17777          * In intersect mode, drag and drop interactio nis defined by the
17778          * overlap of two or more drag and drop objects.
17779          * @property INTERSECT
17780          * @type int
17781          * @static
17782          */
17783         INTERSECT: 1,
17784
17785         /**
17786          * The current drag and drop mode.  Default: POINT
17787          * @property mode
17788          * @type int
17789          * @static
17790          */
17791         mode: 0,
17792
17793         /**
17794          * Runs method on all drag and drop objects
17795          * @method _execOnAll
17796          * @private
17797          * @static
17798          */
17799         _execOnAll: function(sMethod, args) {
17800             for (var i in this.ids) {
17801                 for (var j in this.ids[i]) {
17802                     var oDD = this.ids[i][j];
17803                     if (! this.isTypeOfDD(oDD)) {
17804                         continue;
17805                     }
17806                     oDD[sMethod].apply(oDD, args);
17807                 }
17808             }
17809         },
17810
17811         /**
17812          * Drag and drop initialization.  Sets up the global event handlers
17813          * @method _onLoad
17814          * @private
17815          * @static
17816          */
17817         _onLoad: function() {
17818
17819             this.init();
17820
17821             if (!Roo.isTouch) {
17822                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17823                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17824             }
17825             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17826             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17827             
17828             Event.on(window,   "unload",    this._onUnload, this, true);
17829             Event.on(window,   "resize",    this._onResize, this, true);
17830             // Event.on(window,   "mouseout",    this._test);
17831
17832         },
17833
17834         /**
17835          * Reset constraints on all drag and drop objs
17836          * @method _onResize
17837          * @private
17838          * @static
17839          */
17840         _onResize: function(e) {
17841             this._execOnAll("resetConstraints", []);
17842         },
17843
17844         /**
17845          * Lock all drag and drop functionality
17846          * @method lock
17847          * @static
17848          */
17849         lock: function() { this.locked = true; },
17850
17851         /**
17852          * Unlock all drag and drop functionality
17853          * @method unlock
17854          * @static
17855          */
17856         unlock: function() { this.locked = false; },
17857
17858         /**
17859          * Is drag and drop locked?
17860          * @method isLocked
17861          * @return {boolean} True if drag and drop is locked, false otherwise.
17862          * @static
17863          */
17864         isLocked: function() { return this.locked; },
17865
17866         /**
17867          * Location cache that is set for all drag drop objects when a drag is
17868          * initiated, cleared when the drag is finished.
17869          * @property locationCache
17870          * @private
17871          * @static
17872          */
17873         locationCache: {},
17874
17875         /**
17876          * Set useCache to false if you want to force object the lookup of each
17877          * drag and drop linked element constantly during a drag.
17878          * @property useCache
17879          * @type boolean
17880          * @static
17881          */
17882         useCache: true,
17883
17884         /**
17885          * The number of pixels that the mouse needs to move after the
17886          * mousedown before the drag is initiated.  Default=3;
17887          * @property clickPixelThresh
17888          * @type int
17889          * @static
17890          */
17891         clickPixelThresh: 3,
17892
17893         /**
17894          * The number of milliseconds after the mousedown event to initiate the
17895          * drag if we don't get a mouseup event. Default=1000
17896          * @property clickTimeThresh
17897          * @type int
17898          * @static
17899          */
17900         clickTimeThresh: 350,
17901
17902         /**
17903          * Flag that indicates that either the drag pixel threshold or the
17904          * mousdown time threshold has been met
17905          * @property dragThreshMet
17906          * @type boolean
17907          * @private
17908          * @static
17909          */
17910         dragThreshMet: false,
17911
17912         /**
17913          * Timeout used for the click time threshold
17914          * @property clickTimeout
17915          * @type Object
17916          * @private
17917          * @static
17918          */
17919         clickTimeout: null,
17920
17921         /**
17922          * The X position of the mousedown event stored for later use when a
17923          * drag threshold is met.
17924          * @property startX
17925          * @type int
17926          * @private
17927          * @static
17928          */
17929         startX: 0,
17930
17931         /**
17932          * The Y position of the mousedown event stored for later use when a
17933          * drag threshold is met.
17934          * @property startY
17935          * @type int
17936          * @private
17937          * @static
17938          */
17939         startY: 0,
17940
17941         /**
17942          * Each DragDrop instance must be registered with the DragDropMgr.
17943          * This is executed in DragDrop.init()
17944          * @method regDragDrop
17945          * @param {DragDrop} oDD the DragDrop object to register
17946          * @param {String} sGroup the name of the group this element belongs to
17947          * @static
17948          */
17949         regDragDrop: function(oDD, sGroup) {
17950             if (!this.initialized) { this.init(); }
17951
17952             if (!this.ids[sGroup]) {
17953                 this.ids[sGroup] = {};
17954             }
17955             this.ids[sGroup][oDD.id] = oDD;
17956         },
17957
17958         /**
17959          * Removes the supplied dd instance from the supplied group. Executed
17960          * by DragDrop.removeFromGroup, so don't call this function directly.
17961          * @method removeDDFromGroup
17962          * @private
17963          * @static
17964          */
17965         removeDDFromGroup: function(oDD, sGroup) {
17966             if (!this.ids[sGroup]) {
17967                 this.ids[sGroup] = {};
17968             }
17969
17970             var obj = this.ids[sGroup];
17971             if (obj && obj[oDD.id]) {
17972                 delete obj[oDD.id];
17973             }
17974         },
17975
17976         /**
17977          * Unregisters a drag and drop item.  This is executed in
17978          * DragDrop.unreg, use that method instead of calling this directly.
17979          * @method _remove
17980          * @private
17981          * @static
17982          */
17983         _remove: function(oDD) {
17984             for (var g in oDD.groups) {
17985                 if (g && this.ids[g][oDD.id]) {
17986                     delete this.ids[g][oDD.id];
17987                 }
17988             }
17989             delete this.handleIds[oDD.id];
17990         },
17991
17992         /**
17993          * Each DragDrop handle element must be registered.  This is done
17994          * automatically when executing DragDrop.setHandleElId()
17995          * @method regHandle
17996          * @param {String} sDDId the DragDrop id this element is a handle for
17997          * @param {String} sHandleId the id of the element that is the drag
17998          * handle
17999          * @static
18000          */
18001         regHandle: function(sDDId, sHandleId) {
18002             if (!this.handleIds[sDDId]) {
18003                 this.handleIds[sDDId] = {};
18004             }
18005             this.handleIds[sDDId][sHandleId] = sHandleId;
18006         },
18007
18008         /**
18009          * Utility function to determine if a given element has been
18010          * registered as a drag drop item.
18011          * @method isDragDrop
18012          * @param {String} id the element id to check
18013          * @return {boolean} true if this element is a DragDrop item,
18014          * false otherwise
18015          * @static
18016          */
18017         isDragDrop: function(id) {
18018             return ( this.getDDById(id) ) ? true : false;
18019         },
18020
18021         /**
18022          * Returns the drag and drop instances that are in all groups the
18023          * passed in instance belongs to.
18024          * @method getRelated
18025          * @param {DragDrop} p_oDD the obj to get related data for
18026          * @param {boolean} bTargetsOnly if true, only return targetable objs
18027          * @return {DragDrop[]} the related instances
18028          * @static
18029          */
18030         getRelated: function(p_oDD, bTargetsOnly) {
18031             var oDDs = [];
18032             for (var i in p_oDD.groups) {
18033                 for (j in this.ids[i]) {
18034                     var dd = this.ids[i][j];
18035                     if (! this.isTypeOfDD(dd)) {
18036                         continue;
18037                     }
18038                     if (!bTargetsOnly || dd.isTarget) {
18039                         oDDs[oDDs.length] = dd;
18040                     }
18041                 }
18042             }
18043
18044             return oDDs;
18045         },
18046
18047         /**
18048          * Returns true if the specified dd target is a legal target for
18049          * the specifice drag obj
18050          * @method isLegalTarget
18051          * @param {DragDrop} the drag obj
18052          * @param {DragDrop} the target
18053          * @return {boolean} true if the target is a legal target for the
18054          * dd obj
18055          * @static
18056          */
18057         isLegalTarget: function (oDD, oTargetDD) {
18058             var targets = this.getRelated(oDD, true);
18059             for (var i=0, len=targets.length;i<len;++i) {
18060                 if (targets[i].id == oTargetDD.id) {
18061                     return true;
18062                 }
18063             }
18064
18065             return false;
18066         },
18067
18068         /**
18069          * My goal is to be able to transparently determine if an object is
18070          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18071          * returns "object", oDD.constructor.toString() always returns
18072          * "DragDrop" and not the name of the subclass.  So for now it just
18073          * evaluates a well-known variable in DragDrop.
18074          * @method isTypeOfDD
18075          * @param {Object} the object to evaluate
18076          * @return {boolean} true if typeof oDD = DragDrop
18077          * @static
18078          */
18079         isTypeOfDD: function (oDD) {
18080             return (oDD && oDD.__ygDragDrop);
18081         },
18082
18083         /**
18084          * Utility function to determine if a given element has been
18085          * registered as a drag drop handle for the given Drag Drop object.
18086          * @method isHandle
18087          * @param {String} id the element id to check
18088          * @return {boolean} true if this element is a DragDrop handle, false
18089          * otherwise
18090          * @static
18091          */
18092         isHandle: function(sDDId, sHandleId) {
18093             return ( this.handleIds[sDDId] &&
18094                             this.handleIds[sDDId][sHandleId] );
18095         },
18096
18097         /**
18098          * Returns the DragDrop instance for a given id
18099          * @method getDDById
18100          * @param {String} id the id of the DragDrop object
18101          * @return {DragDrop} the drag drop object, null if it is not found
18102          * @static
18103          */
18104         getDDById: function(id) {
18105             for (var i in this.ids) {
18106                 if (this.ids[i][id]) {
18107                     return this.ids[i][id];
18108                 }
18109             }
18110             return null;
18111         },
18112
18113         /**
18114          * Fired after a registered DragDrop object gets the mousedown event.
18115          * Sets up the events required to track the object being dragged
18116          * @method handleMouseDown
18117          * @param {Event} e the event
18118          * @param oDD the DragDrop object being dragged
18119          * @private
18120          * @static
18121          */
18122         handleMouseDown: function(e, oDD) {
18123             if(Roo.QuickTips){
18124                 Roo.QuickTips.disable();
18125             }
18126             this.currentTarget = e.getTarget();
18127
18128             this.dragCurrent = oDD;
18129
18130             var el = oDD.getEl();
18131
18132             // track start position
18133             this.startX = e.getPageX();
18134             this.startY = e.getPageY();
18135
18136             this.deltaX = this.startX - el.offsetLeft;
18137             this.deltaY = this.startY - el.offsetTop;
18138
18139             this.dragThreshMet = false;
18140
18141             this.clickTimeout = setTimeout(
18142                     function() {
18143                         var DDM = Roo.dd.DDM;
18144                         DDM.startDrag(DDM.startX, DDM.startY);
18145                     },
18146                     this.clickTimeThresh );
18147         },
18148
18149         /**
18150          * Fired when either the drag pixel threshol or the mousedown hold
18151          * time threshold has been met.
18152          * @method startDrag
18153          * @param x {int} the X position of the original mousedown
18154          * @param y {int} the Y position of the original mousedown
18155          * @static
18156          */
18157         startDrag: function(x, y) {
18158             clearTimeout(this.clickTimeout);
18159             if (this.dragCurrent) {
18160                 this.dragCurrent.b4StartDrag(x, y);
18161                 this.dragCurrent.startDrag(x, y);
18162             }
18163             this.dragThreshMet = true;
18164         },
18165
18166         /**
18167          * Internal function to handle the mouseup event.  Will be invoked
18168          * from the context of the document.
18169          * @method handleMouseUp
18170          * @param {Event} e the event
18171          * @private
18172          * @static
18173          */
18174         handleMouseUp: function(e) {
18175
18176             if(Roo.QuickTips){
18177                 Roo.QuickTips.enable();
18178             }
18179             if (! this.dragCurrent) {
18180                 return;
18181             }
18182
18183             clearTimeout(this.clickTimeout);
18184
18185             if (this.dragThreshMet) {
18186                 this.fireEvents(e, true);
18187             } else {
18188             }
18189
18190             this.stopDrag(e);
18191
18192             this.stopEvent(e);
18193         },
18194
18195         /**
18196          * Utility to stop event propagation and event default, if these
18197          * features are turned on.
18198          * @method stopEvent
18199          * @param {Event} e the event as returned by this.getEvent()
18200          * @static
18201          */
18202         stopEvent: function(e){
18203             if(this.stopPropagation) {
18204                 e.stopPropagation();
18205             }
18206
18207             if (this.preventDefault) {
18208                 e.preventDefault();
18209             }
18210         },
18211
18212         /**
18213          * Internal function to clean up event handlers after the drag
18214          * operation is complete
18215          * @method stopDrag
18216          * @param {Event} e the event
18217          * @private
18218          * @static
18219          */
18220         stopDrag: function(e) {
18221             // Fire the drag end event for the item that was dragged
18222             if (this.dragCurrent) {
18223                 if (this.dragThreshMet) {
18224                     this.dragCurrent.b4EndDrag(e);
18225                     this.dragCurrent.endDrag(e);
18226                 }
18227
18228                 this.dragCurrent.onMouseUp(e);
18229             }
18230
18231             this.dragCurrent = null;
18232             this.dragOvers = {};
18233         },
18234
18235         /**
18236          * Internal function to handle the mousemove event.  Will be invoked
18237          * from the context of the html element.
18238          *
18239          * @TODO figure out what we can do about mouse events lost when the
18240          * user drags objects beyond the window boundary.  Currently we can
18241          * detect this in internet explorer by verifying that the mouse is
18242          * down during the mousemove event.  Firefox doesn't give us the
18243          * button state on the mousemove event.
18244          * @method handleMouseMove
18245          * @param {Event} e the event
18246          * @private
18247          * @static
18248          */
18249         handleMouseMove: function(e) {
18250             if (! this.dragCurrent) {
18251                 return true;
18252             }
18253
18254             // var button = e.which || e.button;
18255
18256             // check for IE mouseup outside of page boundary
18257             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18258                 this.stopEvent(e);
18259                 return this.handleMouseUp(e);
18260             }
18261
18262             if (!this.dragThreshMet) {
18263                 var diffX = Math.abs(this.startX - e.getPageX());
18264                 var diffY = Math.abs(this.startY - e.getPageY());
18265                 if (diffX > this.clickPixelThresh ||
18266                             diffY > this.clickPixelThresh) {
18267                     this.startDrag(this.startX, this.startY);
18268                 }
18269             }
18270
18271             if (this.dragThreshMet) {
18272                 this.dragCurrent.b4Drag(e);
18273                 this.dragCurrent.onDrag(e);
18274                 if(!this.dragCurrent.moveOnly){
18275                     this.fireEvents(e, false);
18276                 }
18277             }
18278
18279             this.stopEvent(e);
18280
18281             return true;
18282         },
18283
18284         /**
18285          * Iterates over all of the DragDrop elements to find ones we are
18286          * hovering over or dropping on
18287          * @method fireEvents
18288          * @param {Event} e the event
18289          * @param {boolean} isDrop is this a drop op or a mouseover op?
18290          * @private
18291          * @static
18292          */
18293         fireEvents: function(e, isDrop) {
18294             var dc = this.dragCurrent;
18295
18296             // If the user did the mouse up outside of the window, we could
18297             // get here even though we have ended the drag.
18298             if (!dc || dc.isLocked()) {
18299                 return;
18300             }
18301
18302             var pt = e.getPoint();
18303
18304             // cache the previous dragOver array
18305             var oldOvers = [];
18306
18307             var outEvts   = [];
18308             var overEvts  = [];
18309             var dropEvts  = [];
18310             var enterEvts = [];
18311
18312             // Check to see if the object(s) we were hovering over is no longer
18313             // being hovered over so we can fire the onDragOut event
18314             for (var i in this.dragOvers) {
18315
18316                 var ddo = this.dragOvers[i];
18317
18318                 if (! this.isTypeOfDD(ddo)) {
18319                     continue;
18320                 }
18321
18322                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18323                     outEvts.push( ddo );
18324                 }
18325
18326                 oldOvers[i] = true;
18327                 delete this.dragOvers[i];
18328             }
18329
18330             for (var sGroup in dc.groups) {
18331
18332                 if ("string" != typeof sGroup) {
18333                     continue;
18334                 }
18335
18336                 for (i in this.ids[sGroup]) {
18337                     var oDD = this.ids[sGroup][i];
18338                     if (! this.isTypeOfDD(oDD)) {
18339                         continue;
18340                     }
18341
18342                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18343                         if (this.isOverTarget(pt, oDD, this.mode)) {
18344                             // look for drop interactions
18345                             if (isDrop) {
18346                                 dropEvts.push( oDD );
18347                             // look for drag enter and drag over interactions
18348                             } else {
18349
18350                                 // initial drag over: dragEnter fires
18351                                 if (!oldOvers[oDD.id]) {
18352                                     enterEvts.push( oDD );
18353                                 // subsequent drag overs: dragOver fires
18354                                 } else {
18355                                     overEvts.push( oDD );
18356                                 }
18357
18358                                 this.dragOvers[oDD.id] = oDD;
18359                             }
18360                         }
18361                     }
18362                 }
18363             }
18364
18365             if (this.mode) {
18366                 if (outEvts.length) {
18367                     dc.b4DragOut(e, outEvts);
18368                     dc.onDragOut(e, outEvts);
18369                 }
18370
18371                 if (enterEvts.length) {
18372                     dc.onDragEnter(e, enterEvts);
18373                 }
18374
18375                 if (overEvts.length) {
18376                     dc.b4DragOver(e, overEvts);
18377                     dc.onDragOver(e, overEvts);
18378                 }
18379
18380                 if (dropEvts.length) {
18381                     dc.b4DragDrop(e, dropEvts);
18382                     dc.onDragDrop(e, dropEvts);
18383                 }
18384
18385             } else {
18386                 // fire dragout events
18387                 var len = 0;
18388                 for (i=0, len=outEvts.length; i<len; ++i) {
18389                     dc.b4DragOut(e, outEvts[i].id);
18390                     dc.onDragOut(e, outEvts[i].id);
18391                 }
18392
18393                 // fire enter events
18394                 for (i=0,len=enterEvts.length; i<len; ++i) {
18395                     // dc.b4DragEnter(e, oDD.id);
18396                     dc.onDragEnter(e, enterEvts[i].id);
18397                 }
18398
18399                 // fire over events
18400                 for (i=0,len=overEvts.length; i<len; ++i) {
18401                     dc.b4DragOver(e, overEvts[i].id);
18402                     dc.onDragOver(e, overEvts[i].id);
18403                 }
18404
18405                 // fire drop events
18406                 for (i=0, len=dropEvts.length; i<len; ++i) {
18407                     dc.b4DragDrop(e, dropEvts[i].id);
18408                     dc.onDragDrop(e, dropEvts[i].id);
18409                 }
18410
18411             }
18412
18413             // notify about a drop that did not find a target
18414             if (isDrop && !dropEvts.length) {
18415                 dc.onInvalidDrop(e);
18416             }
18417
18418         },
18419
18420         /**
18421          * Helper function for getting the best match from the list of drag
18422          * and drop objects returned by the drag and drop events when we are
18423          * in INTERSECT mode.  It returns either the first object that the
18424          * cursor is over, or the object that has the greatest overlap with
18425          * the dragged element.
18426          * @method getBestMatch
18427          * @param  {DragDrop[]} dds The array of drag and drop objects
18428          * targeted
18429          * @return {DragDrop}       The best single match
18430          * @static
18431          */
18432         getBestMatch: function(dds) {
18433             var winner = null;
18434             // Return null if the input is not what we expect
18435             //if (!dds || !dds.length || dds.length == 0) {
18436                // winner = null;
18437             // If there is only one item, it wins
18438             //} else if (dds.length == 1) {
18439
18440             var len = dds.length;
18441
18442             if (len == 1) {
18443                 winner = dds[0];
18444             } else {
18445                 // Loop through the targeted items
18446                 for (var i=0; i<len; ++i) {
18447                     var dd = dds[i];
18448                     // If the cursor is over the object, it wins.  If the
18449                     // cursor is over multiple matches, the first one we come
18450                     // to wins.
18451                     if (dd.cursorIsOver) {
18452                         winner = dd;
18453                         break;
18454                     // Otherwise the object with the most overlap wins
18455                     } else {
18456                         if (!winner ||
18457                             winner.overlap.getArea() < dd.overlap.getArea()) {
18458                             winner = dd;
18459                         }
18460                     }
18461                 }
18462             }
18463
18464             return winner;
18465         },
18466
18467         /**
18468          * Refreshes the cache of the top-left and bottom-right points of the
18469          * drag and drop objects in the specified group(s).  This is in the
18470          * format that is stored in the drag and drop instance, so typical
18471          * usage is:
18472          * <code>
18473          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18474          * </code>
18475          * Alternatively:
18476          * <code>
18477          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18478          * </code>
18479          * @TODO this really should be an indexed array.  Alternatively this
18480          * method could accept both.
18481          * @method refreshCache
18482          * @param {Object} groups an associative array of groups to refresh
18483          * @static
18484          */
18485         refreshCache: function(groups) {
18486             for (var sGroup in groups) {
18487                 if ("string" != typeof sGroup) {
18488                     continue;
18489                 }
18490                 for (var i in this.ids[sGroup]) {
18491                     var oDD = this.ids[sGroup][i];
18492
18493                     if (this.isTypeOfDD(oDD)) {
18494                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18495                         var loc = this.getLocation(oDD);
18496                         if (loc) {
18497                             this.locationCache[oDD.id] = loc;
18498                         } else {
18499                             delete this.locationCache[oDD.id];
18500                             // this will unregister the drag and drop object if
18501                             // the element is not in a usable state
18502                             // oDD.unreg();
18503                         }
18504                     }
18505                 }
18506             }
18507         },
18508
18509         /**
18510          * This checks to make sure an element exists and is in the DOM.  The
18511          * main purpose is to handle cases where innerHTML is used to remove
18512          * drag and drop objects from the DOM.  IE provides an 'unspecified
18513          * error' when trying to access the offsetParent of such an element
18514          * @method verifyEl
18515          * @param {HTMLElement} el the element to check
18516          * @return {boolean} true if the element looks usable
18517          * @static
18518          */
18519         verifyEl: function(el) {
18520             if (el) {
18521                 var parent;
18522                 if(Roo.isIE){
18523                     try{
18524                         parent = el.offsetParent;
18525                     }catch(e){}
18526                 }else{
18527                     parent = el.offsetParent;
18528                 }
18529                 if (parent) {
18530                     return true;
18531                 }
18532             }
18533
18534             return false;
18535         },
18536
18537         /**
18538          * Returns a Region object containing the drag and drop element's position
18539          * and size, including the padding configured for it
18540          * @method getLocation
18541          * @param {DragDrop} oDD the drag and drop object to get the
18542          *                       location for
18543          * @return {Roo.lib.Region} a Region object representing the total area
18544          *                             the element occupies, including any padding
18545          *                             the instance is configured for.
18546          * @static
18547          */
18548         getLocation: function(oDD) {
18549             if (! this.isTypeOfDD(oDD)) {
18550                 return null;
18551             }
18552
18553             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18554
18555             try {
18556                 pos= Roo.lib.Dom.getXY(el);
18557             } catch (e) { }
18558
18559             if (!pos) {
18560                 return null;
18561             }
18562
18563             x1 = pos[0];
18564             x2 = x1 + el.offsetWidth;
18565             y1 = pos[1];
18566             y2 = y1 + el.offsetHeight;
18567
18568             t = y1 - oDD.padding[0];
18569             r = x2 + oDD.padding[1];
18570             b = y2 + oDD.padding[2];
18571             l = x1 - oDD.padding[3];
18572
18573             return new Roo.lib.Region( t, r, b, l );
18574         },
18575
18576         /**
18577          * Checks the cursor location to see if it over the target
18578          * @method isOverTarget
18579          * @param {Roo.lib.Point} pt The point to evaluate
18580          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18581          * @return {boolean} true if the mouse is over the target
18582          * @private
18583          * @static
18584          */
18585         isOverTarget: function(pt, oTarget, intersect) {
18586             // use cache if available
18587             var loc = this.locationCache[oTarget.id];
18588             if (!loc || !this.useCache) {
18589                 loc = this.getLocation(oTarget);
18590                 this.locationCache[oTarget.id] = loc;
18591
18592             }
18593
18594             if (!loc) {
18595                 return false;
18596             }
18597
18598             oTarget.cursorIsOver = loc.contains( pt );
18599
18600             // DragDrop is using this as a sanity check for the initial mousedown
18601             // in this case we are done.  In POINT mode, if the drag obj has no
18602             // contraints, we are also done. Otherwise we need to evaluate the
18603             // location of the target as related to the actual location of the
18604             // dragged element.
18605             var dc = this.dragCurrent;
18606             if (!dc || !dc.getTargetCoord ||
18607                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18608                 return oTarget.cursorIsOver;
18609             }
18610
18611             oTarget.overlap = null;
18612
18613             // Get the current location of the drag element, this is the
18614             // location of the mouse event less the delta that represents
18615             // where the original mousedown happened on the element.  We
18616             // need to consider constraints and ticks as well.
18617             var pos = dc.getTargetCoord(pt.x, pt.y);
18618
18619             var el = dc.getDragEl();
18620             var curRegion = new Roo.lib.Region( pos.y,
18621                                                    pos.x + el.offsetWidth,
18622                                                    pos.y + el.offsetHeight,
18623                                                    pos.x );
18624
18625             var overlap = curRegion.intersect(loc);
18626
18627             if (overlap) {
18628                 oTarget.overlap = overlap;
18629                 return (intersect) ? true : oTarget.cursorIsOver;
18630             } else {
18631                 return false;
18632             }
18633         },
18634
18635         /**
18636          * unload event handler
18637          * @method _onUnload
18638          * @private
18639          * @static
18640          */
18641         _onUnload: function(e, me) {
18642             Roo.dd.DragDropMgr.unregAll();
18643         },
18644
18645         /**
18646          * Cleans up the drag and drop events and objects.
18647          * @method unregAll
18648          * @private
18649          * @static
18650          */
18651         unregAll: function() {
18652
18653             if (this.dragCurrent) {
18654                 this.stopDrag();
18655                 this.dragCurrent = null;
18656             }
18657
18658             this._execOnAll("unreg", []);
18659
18660             for (i in this.elementCache) {
18661                 delete this.elementCache[i];
18662             }
18663
18664             this.elementCache = {};
18665             this.ids = {};
18666         },
18667
18668         /**
18669          * A cache of DOM elements
18670          * @property elementCache
18671          * @private
18672          * @static
18673          */
18674         elementCache: {},
18675
18676         /**
18677          * Get the wrapper for the DOM element specified
18678          * @method getElWrapper
18679          * @param {String} id the id of the element to get
18680          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18681          * @private
18682          * @deprecated This wrapper isn't that useful
18683          * @static
18684          */
18685         getElWrapper: function(id) {
18686             var oWrapper = this.elementCache[id];
18687             if (!oWrapper || !oWrapper.el) {
18688                 oWrapper = this.elementCache[id] =
18689                     new this.ElementWrapper(Roo.getDom(id));
18690             }
18691             return oWrapper;
18692         },
18693
18694         /**
18695          * Returns the actual DOM element
18696          * @method getElement
18697          * @param {String} id the id of the elment to get
18698          * @return {Object} The element
18699          * @deprecated use Roo.getDom instead
18700          * @static
18701          */
18702         getElement: function(id) {
18703             return Roo.getDom(id);
18704         },
18705
18706         /**
18707          * Returns the style property for the DOM element (i.e.,
18708          * document.getElById(id).style)
18709          * @method getCss
18710          * @param {String} id the id of the elment to get
18711          * @return {Object} The style property of the element
18712          * @deprecated use Roo.getDom instead
18713          * @static
18714          */
18715         getCss: function(id) {
18716             var el = Roo.getDom(id);
18717             return (el) ? el.style : null;
18718         },
18719
18720         /**
18721          * Inner class for cached elements
18722          * @class DragDropMgr.ElementWrapper
18723          * @for DragDropMgr
18724          * @private
18725          * @deprecated
18726          */
18727         ElementWrapper: function(el) {
18728                 /**
18729                  * The element
18730                  * @property el
18731                  */
18732                 this.el = el || null;
18733                 /**
18734                  * The element id
18735                  * @property id
18736                  */
18737                 this.id = this.el && el.id;
18738                 /**
18739                  * A reference to the style property
18740                  * @property css
18741                  */
18742                 this.css = this.el && el.style;
18743             },
18744
18745         /**
18746          * Returns the X position of an html element
18747          * @method getPosX
18748          * @param el the element for which to get the position
18749          * @return {int} the X coordinate
18750          * @for DragDropMgr
18751          * @deprecated use Roo.lib.Dom.getX instead
18752          * @static
18753          */
18754         getPosX: function(el) {
18755             return Roo.lib.Dom.getX(el);
18756         },
18757
18758         /**
18759          * Returns the Y position of an html element
18760          * @method getPosY
18761          * @param el the element for which to get the position
18762          * @return {int} the Y coordinate
18763          * @deprecated use Roo.lib.Dom.getY instead
18764          * @static
18765          */
18766         getPosY: function(el) {
18767             return Roo.lib.Dom.getY(el);
18768         },
18769
18770         /**
18771          * Swap two nodes.  In IE, we use the native method, for others we
18772          * emulate the IE behavior
18773          * @method swapNode
18774          * @param n1 the first node to swap
18775          * @param n2 the other node to swap
18776          * @static
18777          */
18778         swapNode: function(n1, n2) {
18779             if (n1.swapNode) {
18780                 n1.swapNode(n2);
18781             } else {
18782                 var p = n2.parentNode;
18783                 var s = n2.nextSibling;
18784
18785                 if (s == n1) {
18786                     p.insertBefore(n1, n2);
18787                 } else if (n2 == n1.nextSibling) {
18788                     p.insertBefore(n2, n1);
18789                 } else {
18790                     n1.parentNode.replaceChild(n2, n1);
18791                     p.insertBefore(n1, s);
18792                 }
18793             }
18794         },
18795
18796         /**
18797          * Returns the current scroll position
18798          * @method getScroll
18799          * @private
18800          * @static
18801          */
18802         getScroll: function () {
18803             var t, l, dde=document.documentElement, db=document.body;
18804             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18805                 t = dde.scrollTop;
18806                 l = dde.scrollLeft;
18807             } else if (db) {
18808                 t = db.scrollTop;
18809                 l = db.scrollLeft;
18810             } else {
18811
18812             }
18813             return { top: t, left: l };
18814         },
18815
18816         /**
18817          * Returns the specified element style property
18818          * @method getStyle
18819          * @param {HTMLElement} el          the element
18820          * @param {string}      styleProp   the style property
18821          * @return {string} The value of the style property
18822          * @deprecated use Roo.lib.Dom.getStyle
18823          * @static
18824          */
18825         getStyle: function(el, styleProp) {
18826             return Roo.fly(el).getStyle(styleProp);
18827         },
18828
18829         /**
18830          * Gets the scrollTop
18831          * @method getScrollTop
18832          * @return {int} the document's scrollTop
18833          * @static
18834          */
18835         getScrollTop: function () { return this.getScroll().top; },
18836
18837         /**
18838          * Gets the scrollLeft
18839          * @method getScrollLeft
18840          * @return {int} the document's scrollTop
18841          * @static
18842          */
18843         getScrollLeft: function () { return this.getScroll().left; },
18844
18845         /**
18846          * Sets the x/y position of an element to the location of the
18847          * target element.
18848          * @method moveToEl
18849          * @param {HTMLElement} moveEl      The element to move
18850          * @param {HTMLElement} targetEl    The position reference element
18851          * @static
18852          */
18853         moveToEl: function (moveEl, targetEl) {
18854             var aCoord = Roo.lib.Dom.getXY(targetEl);
18855             Roo.lib.Dom.setXY(moveEl, aCoord);
18856         },
18857
18858         /**
18859          * Numeric array sort function
18860          * @method numericSort
18861          * @static
18862          */
18863         numericSort: function(a, b) { return (a - b); },
18864
18865         /**
18866          * Internal counter
18867          * @property _timeoutCount
18868          * @private
18869          * @static
18870          */
18871         _timeoutCount: 0,
18872
18873         /**
18874          * Trying to make the load order less important.  Without this we get
18875          * an error if this file is loaded before the Event Utility.
18876          * @method _addListeners
18877          * @private
18878          * @static
18879          */
18880         _addListeners: function() {
18881             var DDM = Roo.dd.DDM;
18882             if ( Roo.lib.Event && document ) {
18883                 DDM._onLoad();
18884             } else {
18885                 if (DDM._timeoutCount > 2000) {
18886                 } else {
18887                     setTimeout(DDM._addListeners, 10);
18888                     if (document && document.body) {
18889                         DDM._timeoutCount += 1;
18890                     }
18891                 }
18892             }
18893         },
18894
18895         /**
18896          * Recursively searches the immediate parent and all child nodes for
18897          * the handle element in order to determine wheter or not it was
18898          * clicked.
18899          * @method handleWasClicked
18900          * @param node the html element to inspect
18901          * @static
18902          */
18903         handleWasClicked: function(node, id) {
18904             if (this.isHandle(id, node.id)) {
18905                 return true;
18906             } else {
18907                 // check to see if this is a text node child of the one we want
18908                 var p = node.parentNode;
18909
18910                 while (p) {
18911                     if (this.isHandle(id, p.id)) {
18912                         return true;
18913                     } else {
18914                         p = p.parentNode;
18915                     }
18916                 }
18917             }
18918
18919             return false;
18920         }
18921
18922     };
18923
18924 }();
18925
18926 // shorter alias, save a few bytes
18927 Roo.dd.DDM = Roo.dd.DragDropMgr;
18928 Roo.dd.DDM._addListeners();
18929
18930 }/*
18931  * Based on:
18932  * Ext JS Library 1.1.1
18933  * Copyright(c) 2006-2007, Ext JS, LLC.
18934  *
18935  * Originally Released Under LGPL - original licence link has changed is not relivant.
18936  *
18937  * Fork - LGPL
18938  * <script type="text/javascript">
18939  */
18940
18941 /**
18942  * @class Roo.dd.DD
18943  * A DragDrop implementation where the linked element follows the
18944  * mouse cursor during a drag.
18945  * @extends Roo.dd.DragDrop
18946  * @constructor
18947  * @param {String} id the id of the linked element
18948  * @param {String} sGroup the group of related DragDrop items
18949  * @param {object} config an object containing configurable attributes
18950  *                Valid properties for DD:
18951  *                    scroll
18952  */
18953 Roo.dd.DD = function(id, sGroup, config) {
18954     if (id) {
18955         this.init(id, sGroup, config);
18956     }
18957 };
18958
18959 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18960
18961     /**
18962      * When set to true, the utility automatically tries to scroll the browser
18963      * window wehn a drag and drop element is dragged near the viewport boundary.
18964      * Defaults to true.
18965      * @property scroll
18966      * @type boolean
18967      */
18968     scroll: true,
18969
18970     /**
18971      * Sets the pointer offset to the distance between the linked element's top
18972      * left corner and the location the element was clicked
18973      * @method autoOffset
18974      * @param {int} iPageX the X coordinate of the click
18975      * @param {int} iPageY the Y coordinate of the click
18976      */
18977     autoOffset: function(iPageX, iPageY) {
18978         var x = iPageX - this.startPageX;
18979         var y = iPageY - this.startPageY;
18980         this.setDelta(x, y);
18981     },
18982
18983     /**
18984      * Sets the pointer offset.  You can call this directly to force the
18985      * offset to be in a particular location (e.g., pass in 0,0 to set it
18986      * to the center of the object)
18987      * @method setDelta
18988      * @param {int} iDeltaX the distance from the left
18989      * @param {int} iDeltaY the distance from the top
18990      */
18991     setDelta: function(iDeltaX, iDeltaY) {
18992         this.deltaX = iDeltaX;
18993         this.deltaY = iDeltaY;
18994     },
18995
18996     /**
18997      * Sets the drag element to the location of the mousedown or click event,
18998      * maintaining the cursor location relative to the location on the element
18999      * that was clicked.  Override this if you want to place the element in a
19000      * location other than where the cursor is.
19001      * @method setDragElPos
19002      * @param {int} iPageX the X coordinate of the mousedown or drag event
19003      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19004      */
19005     setDragElPos: function(iPageX, iPageY) {
19006         // the first time we do this, we are going to check to make sure
19007         // the element has css positioning
19008
19009         var el = this.getDragEl();
19010         this.alignElWithMouse(el, iPageX, iPageY);
19011     },
19012
19013     /**
19014      * Sets the element to the location of the mousedown or click event,
19015      * maintaining the cursor location relative to the location on the element
19016      * that was clicked.  Override this if you want to place the element in a
19017      * location other than where the cursor is.
19018      * @method alignElWithMouse
19019      * @param {HTMLElement} el the element to move
19020      * @param {int} iPageX the X coordinate of the mousedown or drag event
19021      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19022      */
19023     alignElWithMouse: function(el, iPageX, iPageY) {
19024         var oCoord = this.getTargetCoord(iPageX, iPageY);
19025         var fly = el.dom ? el : Roo.fly(el);
19026         if (!this.deltaSetXY) {
19027             var aCoord = [oCoord.x, oCoord.y];
19028             fly.setXY(aCoord);
19029             var newLeft = fly.getLeft(true);
19030             var newTop  = fly.getTop(true);
19031             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19032         } else {
19033             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19034         }
19035
19036         this.cachePosition(oCoord.x, oCoord.y);
19037         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19038         return oCoord;
19039     },
19040
19041     /**
19042      * Saves the most recent position so that we can reset the constraints and
19043      * tick marks on-demand.  We need to know this so that we can calculate the
19044      * number of pixels the element is offset from its original position.
19045      * @method cachePosition
19046      * @param iPageX the current x position (optional, this just makes it so we
19047      * don't have to look it up again)
19048      * @param iPageY the current y position (optional, this just makes it so we
19049      * don't have to look it up again)
19050      */
19051     cachePosition: function(iPageX, iPageY) {
19052         if (iPageX) {
19053             this.lastPageX = iPageX;
19054             this.lastPageY = iPageY;
19055         } else {
19056             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19057             this.lastPageX = aCoord[0];
19058             this.lastPageY = aCoord[1];
19059         }
19060     },
19061
19062     /**
19063      * Auto-scroll the window if the dragged object has been moved beyond the
19064      * visible window boundary.
19065      * @method autoScroll
19066      * @param {int} x the drag element's x position
19067      * @param {int} y the drag element's y position
19068      * @param {int} h the height of the drag element
19069      * @param {int} w the width of the drag element
19070      * @private
19071      */
19072     autoScroll: function(x, y, h, w) {
19073
19074         if (this.scroll) {
19075             // The client height
19076             var clientH = Roo.lib.Dom.getViewWidth();
19077
19078             // The client width
19079             var clientW = Roo.lib.Dom.getViewHeight();
19080
19081             // The amt scrolled down
19082             var st = this.DDM.getScrollTop();
19083
19084             // The amt scrolled right
19085             var sl = this.DDM.getScrollLeft();
19086
19087             // Location of the bottom of the element
19088             var bot = h + y;
19089
19090             // Location of the right of the element
19091             var right = w + x;
19092
19093             // The distance from the cursor to the bottom of the visible area,
19094             // adjusted so that we don't scroll if the cursor is beyond the
19095             // element drag constraints
19096             var toBot = (clientH + st - y - this.deltaY);
19097
19098             // The distance from the cursor to the right of the visible area
19099             var toRight = (clientW + sl - x - this.deltaX);
19100
19101
19102             // How close to the edge the cursor must be before we scroll
19103             // var thresh = (document.all) ? 100 : 40;
19104             var thresh = 40;
19105
19106             // How many pixels to scroll per autoscroll op.  This helps to reduce
19107             // clunky scrolling. IE is more sensitive about this ... it needs this
19108             // value to be higher.
19109             var scrAmt = (document.all) ? 80 : 30;
19110
19111             // Scroll down if we are near the bottom of the visible page and the
19112             // obj extends below the crease
19113             if ( bot > clientH && toBot < thresh ) {
19114                 window.scrollTo(sl, st + scrAmt);
19115             }
19116
19117             // Scroll up if the window is scrolled down and the top of the object
19118             // goes above the top border
19119             if ( y < st && st > 0 && y - st < thresh ) {
19120                 window.scrollTo(sl, st - scrAmt);
19121             }
19122
19123             // Scroll right if the obj is beyond the right border and the cursor is
19124             // near the border.
19125             if ( right > clientW && toRight < thresh ) {
19126                 window.scrollTo(sl + scrAmt, st);
19127             }
19128
19129             // Scroll left if the window has been scrolled to the right and the obj
19130             // extends past the left border
19131             if ( x < sl && sl > 0 && x - sl < thresh ) {
19132                 window.scrollTo(sl - scrAmt, st);
19133             }
19134         }
19135     },
19136
19137     /**
19138      * Finds the location the element should be placed if we want to move
19139      * it to where the mouse location less the click offset would place us.
19140      * @method getTargetCoord
19141      * @param {int} iPageX the X coordinate of the click
19142      * @param {int} iPageY the Y coordinate of the click
19143      * @return an object that contains the coordinates (Object.x and Object.y)
19144      * @private
19145      */
19146     getTargetCoord: function(iPageX, iPageY) {
19147
19148
19149         var x = iPageX - this.deltaX;
19150         var y = iPageY - this.deltaY;
19151
19152         if (this.constrainX) {
19153             if (x < this.minX) { x = this.minX; }
19154             if (x > this.maxX) { x = this.maxX; }
19155         }
19156
19157         if (this.constrainY) {
19158             if (y < this.minY) { y = this.minY; }
19159             if (y > this.maxY) { y = this.maxY; }
19160         }
19161
19162         x = this.getTick(x, this.xTicks);
19163         y = this.getTick(y, this.yTicks);
19164
19165
19166         return {x:x, y:y};
19167     },
19168
19169     /*
19170      * Sets up config options specific to this class. Overrides
19171      * Roo.dd.DragDrop, but all versions of this method through the
19172      * inheritance chain are called
19173      */
19174     applyConfig: function() {
19175         Roo.dd.DD.superclass.applyConfig.call(this);
19176         this.scroll = (this.config.scroll !== false);
19177     },
19178
19179     /*
19180      * Event that fires prior to the onMouseDown event.  Overrides
19181      * Roo.dd.DragDrop.
19182      */
19183     b4MouseDown: function(e) {
19184         // this.resetConstraints();
19185         this.autoOffset(e.getPageX(),
19186                             e.getPageY());
19187     },
19188
19189     /*
19190      * Event that fires prior to the onDrag event.  Overrides
19191      * Roo.dd.DragDrop.
19192      */
19193     b4Drag: function(e) {
19194         this.setDragElPos(e.getPageX(),
19195                             e.getPageY());
19196     },
19197
19198     toString: function() {
19199         return ("DD " + this.id);
19200     }
19201
19202     //////////////////////////////////////////////////////////////////////////
19203     // Debugging ygDragDrop events that can be overridden
19204     //////////////////////////////////////////////////////////////////////////
19205     /*
19206     startDrag: function(x, y) {
19207     },
19208
19209     onDrag: function(e) {
19210     },
19211
19212     onDragEnter: function(e, id) {
19213     },
19214
19215     onDragOver: function(e, id) {
19216     },
19217
19218     onDragOut: function(e, id) {
19219     },
19220
19221     onDragDrop: function(e, id) {
19222     },
19223
19224     endDrag: function(e) {
19225     }
19226
19227     */
19228
19229 });/*
19230  * Based on:
19231  * Ext JS Library 1.1.1
19232  * Copyright(c) 2006-2007, Ext JS, LLC.
19233  *
19234  * Originally Released Under LGPL - original licence link has changed is not relivant.
19235  *
19236  * Fork - LGPL
19237  * <script type="text/javascript">
19238  */
19239
19240 /**
19241  * @class Roo.dd.DDProxy
19242  * A DragDrop implementation that inserts an empty, bordered div into
19243  * the document that follows the cursor during drag operations.  At the time of
19244  * the click, the frame div is resized to the dimensions of the linked html
19245  * element, and moved to the exact location of the linked element.
19246  *
19247  * References to the "frame" element refer to the single proxy element that
19248  * was created to be dragged in place of all DDProxy elements on the
19249  * page.
19250  *
19251  * @extends Roo.dd.DD
19252  * @constructor
19253  * @param {String} id the id of the linked html element
19254  * @param {String} sGroup the group of related DragDrop objects
19255  * @param {object} config an object containing configurable attributes
19256  *                Valid properties for DDProxy in addition to those in DragDrop:
19257  *                   resizeFrame, centerFrame, dragElId
19258  */
19259 Roo.dd.DDProxy = function(id, sGroup, config) {
19260     if (id) {
19261         this.init(id, sGroup, config);
19262         this.initFrame();
19263     }
19264 };
19265
19266 /**
19267  * The default drag frame div id
19268  * @property Roo.dd.DDProxy.dragElId
19269  * @type String
19270  * @static
19271  */
19272 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19273
19274 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19275
19276     /**
19277      * By default we resize the drag frame to be the same size as the element
19278      * we want to drag (this is to get the frame effect).  We can turn it off
19279      * if we want a different behavior.
19280      * @property resizeFrame
19281      * @type boolean
19282      */
19283     resizeFrame: true,
19284
19285     /**
19286      * By default the frame is positioned exactly where the drag element is, so
19287      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19288      * you do not have constraints on the obj is to have the drag frame centered
19289      * around the cursor.  Set centerFrame to true for this effect.
19290      * @property centerFrame
19291      * @type boolean
19292      */
19293     centerFrame: false,
19294
19295     /**
19296      * Creates the proxy element if it does not yet exist
19297      * @method createFrame
19298      */
19299     createFrame: function() {
19300         var self = this;
19301         var body = document.body;
19302
19303         if (!body || !body.firstChild) {
19304             setTimeout( function() { self.createFrame(); }, 50 );
19305             return;
19306         }
19307
19308         var div = this.getDragEl();
19309
19310         if (!div) {
19311             div    = document.createElement("div");
19312             div.id = this.dragElId;
19313             var s  = div.style;
19314
19315             s.position   = "absolute";
19316             s.visibility = "hidden";
19317             s.cursor     = "move";
19318             s.border     = "2px solid #aaa";
19319             s.zIndex     = 999;
19320
19321             // appendChild can blow up IE if invoked prior to the window load event
19322             // while rendering a table.  It is possible there are other scenarios
19323             // that would cause this to happen as well.
19324             body.insertBefore(div, body.firstChild);
19325         }
19326     },
19327
19328     /**
19329      * Initialization for the drag frame element.  Must be called in the
19330      * constructor of all subclasses
19331      * @method initFrame
19332      */
19333     initFrame: function() {
19334         this.createFrame();
19335     },
19336
19337     applyConfig: function() {
19338         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19339
19340         this.resizeFrame = (this.config.resizeFrame !== false);
19341         this.centerFrame = (this.config.centerFrame);
19342         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19343     },
19344
19345     /**
19346      * Resizes the drag frame to the dimensions of the clicked object, positions
19347      * it over the object, and finally displays it
19348      * @method showFrame
19349      * @param {int} iPageX X click position
19350      * @param {int} iPageY Y click position
19351      * @private
19352      */
19353     showFrame: function(iPageX, iPageY) {
19354         var el = this.getEl();
19355         var dragEl = this.getDragEl();
19356         var s = dragEl.style;
19357
19358         this._resizeProxy();
19359
19360         if (this.centerFrame) {
19361             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19362                            Math.round(parseInt(s.height, 10)/2) );
19363         }
19364
19365         this.setDragElPos(iPageX, iPageY);
19366
19367         Roo.fly(dragEl).show();
19368     },
19369
19370     /**
19371      * The proxy is automatically resized to the dimensions of the linked
19372      * element when a drag is initiated, unless resizeFrame is set to false
19373      * @method _resizeProxy
19374      * @private
19375      */
19376     _resizeProxy: function() {
19377         if (this.resizeFrame) {
19378             var el = this.getEl();
19379             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19380         }
19381     },
19382
19383     // overrides Roo.dd.DragDrop
19384     b4MouseDown: function(e) {
19385         var x = e.getPageX();
19386         var y = e.getPageY();
19387         this.autoOffset(x, y);
19388         this.setDragElPos(x, y);
19389     },
19390
19391     // overrides Roo.dd.DragDrop
19392     b4StartDrag: function(x, y) {
19393         // show the drag frame
19394         this.showFrame(x, y);
19395     },
19396
19397     // overrides Roo.dd.DragDrop
19398     b4EndDrag: function(e) {
19399         Roo.fly(this.getDragEl()).hide();
19400     },
19401
19402     // overrides Roo.dd.DragDrop
19403     // By default we try to move the element to the last location of the frame.
19404     // This is so that the default behavior mirrors that of Roo.dd.DD.
19405     endDrag: function(e) {
19406
19407         var lel = this.getEl();
19408         var del = this.getDragEl();
19409
19410         // Show the drag frame briefly so we can get its position
19411         del.style.visibility = "";
19412
19413         this.beforeMove();
19414         // Hide the linked element before the move to get around a Safari
19415         // rendering bug.
19416         lel.style.visibility = "hidden";
19417         Roo.dd.DDM.moveToEl(lel, del);
19418         del.style.visibility = "hidden";
19419         lel.style.visibility = "";
19420
19421         this.afterDrag();
19422     },
19423
19424     beforeMove : function(){
19425
19426     },
19427
19428     afterDrag : function(){
19429
19430     },
19431
19432     toString: function() {
19433         return ("DDProxy " + this.id);
19434     }
19435
19436 });
19437 /*
19438  * Based on:
19439  * Ext JS Library 1.1.1
19440  * Copyright(c) 2006-2007, Ext JS, LLC.
19441  *
19442  * Originally Released Under LGPL - original licence link has changed is not relivant.
19443  *
19444  * Fork - LGPL
19445  * <script type="text/javascript">
19446  */
19447
19448  /**
19449  * @class Roo.dd.DDTarget
19450  * A DragDrop implementation that does not move, but can be a drop
19451  * target.  You would get the same result by simply omitting implementation
19452  * for the event callbacks, but this way we reduce the processing cost of the
19453  * event listener and the callbacks.
19454  * @extends Roo.dd.DragDrop
19455  * @constructor
19456  * @param {String} id the id of the element that is a drop target
19457  * @param {String} sGroup the group of related DragDrop objects
19458  * @param {object} config an object containing configurable attributes
19459  *                 Valid properties for DDTarget in addition to those in
19460  *                 DragDrop:
19461  *                    none
19462  */
19463 Roo.dd.DDTarget = function(id, sGroup, config) {
19464     if (id) {
19465         this.initTarget(id, sGroup, config);
19466     }
19467     if (config.listeners || config.events) { 
19468        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19469             listeners : config.listeners || {}, 
19470             events : config.events || {} 
19471         });    
19472     }
19473 };
19474
19475 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19476 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19477     toString: function() {
19478         return ("DDTarget " + this.id);
19479     }
19480 });
19481 /*
19482  * Based on:
19483  * Ext JS Library 1.1.1
19484  * Copyright(c) 2006-2007, Ext JS, LLC.
19485  *
19486  * Originally Released Under LGPL - original licence link has changed is not relivant.
19487  *
19488  * Fork - LGPL
19489  * <script type="text/javascript">
19490  */
19491  
19492
19493 /**
19494  * @class Roo.dd.ScrollManager
19495  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19496  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19497  * @singleton
19498  */
19499 Roo.dd.ScrollManager = function(){
19500     var ddm = Roo.dd.DragDropMgr;
19501     var els = {};
19502     var dragEl = null;
19503     var proc = {};
19504     
19505     
19506     
19507     var onStop = function(e){
19508         dragEl = null;
19509         clearProc();
19510     };
19511     
19512     var triggerRefresh = function(){
19513         if(ddm.dragCurrent){
19514              ddm.refreshCache(ddm.dragCurrent.groups);
19515         }
19516     };
19517     
19518     var doScroll = function(){
19519         if(ddm.dragCurrent){
19520             var dds = Roo.dd.ScrollManager;
19521             if(!dds.animate){
19522                 if(proc.el.scroll(proc.dir, dds.increment)){
19523                     triggerRefresh();
19524                 }
19525             }else{
19526                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19527             }
19528         }
19529     };
19530     
19531     var clearProc = function(){
19532         if(proc.id){
19533             clearInterval(proc.id);
19534         }
19535         proc.id = 0;
19536         proc.el = null;
19537         proc.dir = "";
19538     };
19539     
19540     var startProc = function(el, dir){
19541          Roo.log('scroll startproc');
19542         clearProc();
19543         proc.el = el;
19544         proc.dir = dir;
19545         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19546     };
19547     
19548     var onFire = function(e, isDrop){
19549        
19550         if(isDrop || !ddm.dragCurrent){ return; }
19551         var dds = Roo.dd.ScrollManager;
19552         if(!dragEl || dragEl != ddm.dragCurrent){
19553             dragEl = ddm.dragCurrent;
19554             // refresh regions on drag start
19555             dds.refreshCache();
19556         }
19557         
19558         var xy = Roo.lib.Event.getXY(e);
19559         var pt = new Roo.lib.Point(xy[0], xy[1]);
19560         for(var id in els){
19561             var el = els[id], r = el._region;
19562             if(r && r.contains(pt) && el.isScrollable()){
19563                 if(r.bottom - pt.y <= dds.thresh){
19564                     if(proc.el != el){
19565                         startProc(el, "down");
19566                     }
19567                     return;
19568                 }else if(r.right - pt.x <= dds.thresh){
19569                     if(proc.el != el){
19570                         startProc(el, "left");
19571                     }
19572                     return;
19573                 }else if(pt.y - r.top <= dds.thresh){
19574                     if(proc.el != el){
19575                         startProc(el, "up");
19576                     }
19577                     return;
19578                 }else if(pt.x - r.left <= dds.thresh){
19579                     if(proc.el != el){
19580                         startProc(el, "right");
19581                     }
19582                     return;
19583                 }
19584             }
19585         }
19586         clearProc();
19587     };
19588     
19589     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19590     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19591     
19592     return {
19593         /**
19594          * Registers new overflow element(s) to auto scroll
19595          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19596          */
19597         register : function(el){
19598             if(el instanceof Array){
19599                 for(var i = 0, len = el.length; i < len; i++) {
19600                         this.register(el[i]);
19601                 }
19602             }else{
19603                 el = Roo.get(el);
19604                 els[el.id] = el;
19605             }
19606             Roo.dd.ScrollManager.els = els;
19607         },
19608         
19609         /**
19610          * Unregisters overflow element(s) so they are no longer scrolled
19611          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19612          */
19613         unregister : function(el){
19614             if(el instanceof Array){
19615                 for(var i = 0, len = el.length; i < len; i++) {
19616                         this.unregister(el[i]);
19617                 }
19618             }else{
19619                 el = Roo.get(el);
19620                 delete els[el.id];
19621             }
19622         },
19623         
19624         /**
19625          * The number of pixels from the edge of a container the pointer needs to be to 
19626          * trigger scrolling (defaults to 25)
19627          * @type Number
19628          */
19629         thresh : 25,
19630         
19631         /**
19632          * The number of pixels to scroll in each scroll increment (defaults to 50)
19633          * @type Number
19634          */
19635         increment : 100,
19636         
19637         /**
19638          * The frequency of scrolls in milliseconds (defaults to 500)
19639          * @type Number
19640          */
19641         frequency : 500,
19642         
19643         /**
19644          * True to animate the scroll (defaults to true)
19645          * @type Boolean
19646          */
19647         animate: true,
19648         
19649         /**
19650          * The animation duration in seconds - 
19651          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19652          * @type Number
19653          */
19654         animDuration: .4,
19655         
19656         /**
19657          * Manually trigger a cache refresh.
19658          */
19659         refreshCache : function(){
19660             for(var id in els){
19661                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19662                     els[id]._region = els[id].getRegion();
19663                 }
19664             }
19665         }
19666     };
19667 }();/*
19668  * Based on:
19669  * Ext JS Library 1.1.1
19670  * Copyright(c) 2006-2007, Ext JS, LLC.
19671  *
19672  * Originally Released Under LGPL - original licence link has changed is not relivant.
19673  *
19674  * Fork - LGPL
19675  * <script type="text/javascript">
19676  */
19677  
19678
19679 /**
19680  * @class Roo.dd.Registry
19681  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19682  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19683  * @singleton
19684  */
19685 Roo.dd.Registry = function(){
19686     var elements = {}; 
19687     var handles = {}; 
19688     var autoIdSeed = 0;
19689
19690     var getId = function(el, autogen){
19691         if(typeof el == "string"){
19692             return el;
19693         }
19694         var id = el.id;
19695         if(!id && autogen !== false){
19696             id = "roodd-" + (++autoIdSeed);
19697             el.id = id;
19698         }
19699         return id;
19700     };
19701     
19702     return {
19703     /**
19704      * Register a drag drop element
19705      * @param {String|HTMLElement} element The id or DOM node to register
19706      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19707      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19708      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19709      * populated in the data object (if applicable):
19710      * <pre>
19711 Value      Description<br />
19712 ---------  ------------------------------------------<br />
19713 handles    Array of DOM nodes that trigger dragging<br />
19714            for the element being registered<br />
19715 isHandle   True if the element passed in triggers<br />
19716            dragging itself, else false
19717 </pre>
19718      */
19719         register : function(el, data){
19720             data = data || {};
19721             if(typeof el == "string"){
19722                 el = document.getElementById(el);
19723             }
19724             data.ddel = el;
19725             elements[getId(el)] = data;
19726             if(data.isHandle !== false){
19727                 handles[data.ddel.id] = data;
19728             }
19729             if(data.handles){
19730                 var hs = data.handles;
19731                 for(var i = 0, len = hs.length; i < len; i++){
19732                         handles[getId(hs[i])] = data;
19733                 }
19734             }
19735         },
19736
19737     /**
19738      * Unregister a drag drop element
19739      * @param {String|HTMLElement}  element The id or DOM node to unregister
19740      */
19741         unregister : function(el){
19742             var id = getId(el, false);
19743             var data = elements[id];
19744             if(data){
19745                 delete elements[id];
19746                 if(data.handles){
19747                     var hs = data.handles;
19748                     for(var i = 0, len = hs.length; i < len; i++){
19749                         delete handles[getId(hs[i], false)];
19750                     }
19751                 }
19752             }
19753         },
19754
19755     /**
19756      * Returns the handle registered for a DOM Node by id
19757      * @param {String|HTMLElement} id The DOM node or id to look up
19758      * @return {Object} handle The custom handle data
19759      */
19760         getHandle : function(id){
19761             if(typeof id != "string"){ // must be element?
19762                 id = id.id;
19763             }
19764             return handles[id];
19765         },
19766
19767     /**
19768      * Returns the handle that is registered for the DOM node that is the target of the event
19769      * @param {Event} e The event
19770      * @return {Object} handle The custom handle data
19771      */
19772         getHandleFromEvent : function(e){
19773             var t = Roo.lib.Event.getTarget(e);
19774             return t ? handles[t.id] : null;
19775         },
19776
19777     /**
19778      * Returns a custom data object that is registered for a DOM node by id
19779      * @param {String|HTMLElement} id The DOM node or id to look up
19780      * @return {Object} data The custom data
19781      */
19782         getTarget : function(id){
19783             if(typeof id != "string"){ // must be element?
19784                 id = id.id;
19785             }
19786             return elements[id];
19787         },
19788
19789     /**
19790      * Returns a custom data object that is registered for the DOM node that is the target of the event
19791      * @param {Event} e The event
19792      * @return {Object} data The custom data
19793      */
19794         getTargetFromEvent : function(e){
19795             var t = Roo.lib.Event.getTarget(e);
19796             return t ? elements[t.id] || handles[t.id] : null;
19797         }
19798     };
19799 }();/*
19800  * Based on:
19801  * Ext JS Library 1.1.1
19802  * Copyright(c) 2006-2007, Ext JS, LLC.
19803  *
19804  * Originally Released Under LGPL - original licence link has changed is not relivant.
19805  *
19806  * Fork - LGPL
19807  * <script type="text/javascript">
19808  */
19809  
19810
19811 /**
19812  * @class Roo.dd.StatusProxy
19813  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19814  * default drag proxy used by all Roo.dd components.
19815  * @constructor
19816  * @param {Object} config
19817  */
19818 Roo.dd.StatusProxy = function(config){
19819     Roo.apply(this, config);
19820     this.id = this.id || Roo.id();
19821     this.el = new Roo.Layer({
19822         dh: {
19823             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19824                 {tag: "div", cls: "x-dd-drop-icon"},
19825                 {tag: "div", cls: "x-dd-drag-ghost"}
19826             ]
19827         }, 
19828         shadow: !config || config.shadow !== false
19829     });
19830     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19831     this.dropStatus = this.dropNotAllowed;
19832 };
19833
19834 Roo.dd.StatusProxy.prototype = {
19835     /**
19836      * @cfg {String} dropAllowed
19837      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19838      */
19839     dropAllowed : "x-dd-drop-ok",
19840     /**
19841      * @cfg {String} dropNotAllowed
19842      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19843      */
19844     dropNotAllowed : "x-dd-drop-nodrop",
19845
19846     /**
19847      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19848      * over the current target element.
19849      * @param {String} cssClass The css class for the new drop status indicator image
19850      */
19851     setStatus : function(cssClass){
19852         cssClass = cssClass || this.dropNotAllowed;
19853         if(this.dropStatus != cssClass){
19854             this.el.replaceClass(this.dropStatus, cssClass);
19855             this.dropStatus = cssClass;
19856         }
19857     },
19858
19859     /**
19860      * Resets the status indicator to the default dropNotAllowed value
19861      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19862      */
19863     reset : function(clearGhost){
19864         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19865         this.dropStatus = this.dropNotAllowed;
19866         if(clearGhost){
19867             this.ghost.update("");
19868         }
19869     },
19870
19871     /**
19872      * Updates the contents of the ghost element
19873      * @param {String} html The html that will replace the current innerHTML of the ghost element
19874      */
19875     update : function(html){
19876         if(typeof html == "string"){
19877             this.ghost.update(html);
19878         }else{
19879             this.ghost.update("");
19880             html.style.margin = "0";
19881             this.ghost.dom.appendChild(html);
19882         }
19883         // ensure float = none set?? cant remember why though.
19884         var el = this.ghost.dom.firstChild;
19885                 if(el){
19886                         Roo.fly(el).setStyle('float', 'none');
19887                 }
19888     },
19889     
19890     /**
19891      * Returns the underlying proxy {@link Roo.Layer}
19892      * @return {Roo.Layer} el
19893     */
19894     getEl : function(){
19895         return this.el;
19896     },
19897
19898     /**
19899      * Returns the ghost element
19900      * @return {Roo.Element} el
19901      */
19902     getGhost : function(){
19903         return this.ghost;
19904     },
19905
19906     /**
19907      * Hides the proxy
19908      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19909      */
19910     hide : function(clear){
19911         this.el.hide();
19912         if(clear){
19913             this.reset(true);
19914         }
19915     },
19916
19917     /**
19918      * Stops the repair animation if it's currently running
19919      */
19920     stop : function(){
19921         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19922             this.anim.stop();
19923         }
19924     },
19925
19926     /**
19927      * Displays this proxy
19928      */
19929     show : function(){
19930         this.el.show();
19931     },
19932
19933     /**
19934      * Force the Layer to sync its shadow and shim positions to the element
19935      */
19936     sync : function(){
19937         this.el.sync();
19938     },
19939
19940     /**
19941      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19942      * invalid drop operation by the item being dragged.
19943      * @param {Array} xy The XY position of the element ([x, y])
19944      * @param {Function} callback The function to call after the repair is complete
19945      * @param {Object} scope The scope in which to execute the callback
19946      */
19947     repair : function(xy, callback, scope){
19948         this.callback = callback;
19949         this.scope = scope;
19950         if(xy && this.animRepair !== false){
19951             this.el.addClass("x-dd-drag-repair");
19952             this.el.hideUnders(true);
19953             this.anim = this.el.shift({
19954                 duration: this.repairDuration || .5,
19955                 easing: 'easeOut',
19956                 xy: xy,
19957                 stopFx: true,
19958                 callback: this.afterRepair,
19959                 scope: this
19960             });
19961         }else{
19962             this.afterRepair();
19963         }
19964     },
19965
19966     // private
19967     afterRepair : function(){
19968         this.hide(true);
19969         if(typeof this.callback == "function"){
19970             this.callback.call(this.scope || this);
19971         }
19972         this.callback = null;
19973         this.scope = null;
19974     }
19975 };/*
19976  * Based on:
19977  * Ext JS Library 1.1.1
19978  * Copyright(c) 2006-2007, Ext JS, LLC.
19979  *
19980  * Originally Released Under LGPL - original licence link has changed is not relivant.
19981  *
19982  * Fork - LGPL
19983  * <script type="text/javascript">
19984  */
19985
19986 /**
19987  * @class Roo.dd.DragSource
19988  * @extends Roo.dd.DDProxy
19989  * A simple class that provides the basic implementation needed to make any element draggable.
19990  * @constructor
19991  * @param {String/HTMLElement/Element} el The container element
19992  * @param {Object} config
19993  */
19994 Roo.dd.DragSource = function(el, config){
19995     this.el = Roo.get(el);
19996     this.dragData = {};
19997     
19998     Roo.apply(this, config);
19999     
20000     if(!this.proxy){
20001         this.proxy = new Roo.dd.StatusProxy();
20002     }
20003
20004     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20005           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20006     
20007     this.dragging = false;
20008 };
20009
20010 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20011     /**
20012      * @cfg {String} dropAllowed
20013      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20014      */
20015     dropAllowed : "x-dd-drop-ok",
20016     /**
20017      * @cfg {String} dropNotAllowed
20018      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20019      */
20020     dropNotAllowed : "x-dd-drop-nodrop",
20021
20022     /**
20023      * Returns the data object associated with this drag source
20024      * @return {Object} data An object containing arbitrary data
20025      */
20026     getDragData : function(e){
20027         return this.dragData;
20028     },
20029
20030     // private
20031     onDragEnter : function(e, id){
20032         var target = Roo.dd.DragDropMgr.getDDById(id);
20033         this.cachedTarget = target;
20034         if(this.beforeDragEnter(target, e, id) !== false){
20035             if(target.isNotifyTarget){
20036                 var status = target.notifyEnter(this, e, this.dragData);
20037                 this.proxy.setStatus(status);
20038             }else{
20039                 this.proxy.setStatus(this.dropAllowed);
20040             }
20041             
20042             if(this.afterDragEnter){
20043                 /**
20044                  * An empty function by default, but provided so that you can perform a custom action
20045                  * when the dragged item enters the drop target by providing an implementation.
20046                  * @param {Roo.dd.DragDrop} target The drop target
20047                  * @param {Event} e The event object
20048                  * @param {String} id The id of the dragged element
20049                  * @method afterDragEnter
20050                  */
20051                 this.afterDragEnter(target, e, id);
20052             }
20053         }
20054     },
20055
20056     /**
20057      * An empty function by default, but provided so that you can perform a custom action
20058      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20059      * @param {Roo.dd.DragDrop} target The drop target
20060      * @param {Event} e The event object
20061      * @param {String} id The id of the dragged element
20062      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20063      */
20064     beforeDragEnter : function(target, e, id){
20065         return true;
20066     },
20067
20068     // private
20069     alignElWithMouse: function() {
20070         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20071         this.proxy.sync();
20072     },
20073
20074     // private
20075     onDragOver : function(e, id){
20076         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20077         if(this.beforeDragOver(target, e, id) !== false){
20078             if(target.isNotifyTarget){
20079                 var status = target.notifyOver(this, e, this.dragData);
20080                 this.proxy.setStatus(status);
20081             }
20082
20083             if(this.afterDragOver){
20084                 /**
20085                  * An empty function by default, but provided so that you can perform a custom action
20086                  * while the dragged item is over the drop target by providing an implementation.
20087                  * @param {Roo.dd.DragDrop} target The drop target
20088                  * @param {Event} e The event object
20089                  * @param {String} id The id of the dragged element
20090                  * @method afterDragOver
20091                  */
20092                 this.afterDragOver(target, e, id);
20093             }
20094         }
20095     },
20096
20097     /**
20098      * An empty function by default, but provided so that you can perform a custom action
20099      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20100      * @param {Roo.dd.DragDrop} target The drop target
20101      * @param {Event} e The event object
20102      * @param {String} id The id of the dragged element
20103      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20104      */
20105     beforeDragOver : function(target, e, id){
20106         return true;
20107     },
20108
20109     // private
20110     onDragOut : function(e, id){
20111         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20112         if(this.beforeDragOut(target, e, id) !== false){
20113             if(target.isNotifyTarget){
20114                 target.notifyOut(this, e, this.dragData);
20115             }
20116             this.proxy.reset();
20117             if(this.afterDragOut){
20118                 /**
20119                  * An empty function by default, but provided so that you can perform a custom action
20120                  * after the dragged item is dragged out of the target without dropping.
20121                  * @param {Roo.dd.DragDrop} target The drop target
20122                  * @param {Event} e The event object
20123                  * @param {String} id The id of the dragged element
20124                  * @method afterDragOut
20125                  */
20126                 this.afterDragOut(target, e, id);
20127             }
20128         }
20129         this.cachedTarget = null;
20130     },
20131
20132     /**
20133      * An empty function by default, but provided so that you can perform a custom action before the dragged
20134      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20135      * @param {Roo.dd.DragDrop} target The drop target
20136      * @param {Event} e The event object
20137      * @param {String} id The id of the dragged element
20138      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20139      */
20140     beforeDragOut : function(target, e, id){
20141         return true;
20142     },
20143     
20144     // private
20145     onDragDrop : function(e, id){
20146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20147         if(this.beforeDragDrop(target, e, id) !== false){
20148             if(target.isNotifyTarget){
20149                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20150                     this.onValidDrop(target, e, id);
20151                 }else{
20152                     this.onInvalidDrop(target, e, id);
20153                 }
20154             }else{
20155                 this.onValidDrop(target, e, id);
20156             }
20157             
20158             if(this.afterDragDrop){
20159                 /**
20160                  * An empty function by default, but provided so that you can perform a custom action
20161                  * after a valid drag drop has occurred by providing an implementation.
20162                  * @param {Roo.dd.DragDrop} target The drop target
20163                  * @param {Event} e The event object
20164                  * @param {String} id The id of the dropped element
20165                  * @method afterDragDrop
20166                  */
20167                 this.afterDragDrop(target, e, id);
20168             }
20169         }
20170         delete this.cachedTarget;
20171     },
20172
20173     /**
20174      * An empty function by default, but provided so that you can perform a custom action before the dragged
20175      * item is dropped onto the target and optionally cancel the onDragDrop.
20176      * @param {Roo.dd.DragDrop} target The drop target
20177      * @param {Event} e The event object
20178      * @param {String} id The id of the dragged element
20179      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20180      */
20181     beforeDragDrop : function(target, e, id){
20182         return true;
20183     },
20184
20185     // private
20186     onValidDrop : function(target, e, id){
20187         this.hideProxy();
20188         if(this.afterValidDrop){
20189             /**
20190              * An empty function by default, but provided so that you can perform a custom action
20191              * after a valid drop has occurred by providing an implementation.
20192              * @param {Object} target The target DD 
20193              * @param {Event} e The event object
20194              * @param {String} id The id of the dropped element
20195              * @method afterInvalidDrop
20196              */
20197             this.afterValidDrop(target, e, id);
20198         }
20199     },
20200
20201     // private
20202     getRepairXY : function(e, data){
20203         return this.el.getXY();  
20204     },
20205
20206     // private
20207     onInvalidDrop : function(target, e, id){
20208         this.beforeInvalidDrop(target, e, id);
20209         if(this.cachedTarget){
20210             if(this.cachedTarget.isNotifyTarget){
20211                 this.cachedTarget.notifyOut(this, e, this.dragData);
20212             }
20213             this.cacheTarget = null;
20214         }
20215         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20216
20217         if(this.afterInvalidDrop){
20218             /**
20219              * An empty function by default, but provided so that you can perform a custom action
20220              * after an invalid drop has occurred by providing an implementation.
20221              * @param {Event} e The event object
20222              * @param {String} id The id of the dropped element
20223              * @method afterInvalidDrop
20224              */
20225             this.afterInvalidDrop(e, id);
20226         }
20227     },
20228
20229     // private
20230     afterRepair : function(){
20231         if(Roo.enableFx){
20232             this.el.highlight(this.hlColor || "c3daf9");
20233         }
20234         this.dragging = false;
20235     },
20236
20237     /**
20238      * An empty function by default, but provided so that you can perform a custom action after an invalid
20239      * drop has occurred.
20240      * @param {Roo.dd.DragDrop} target The drop target
20241      * @param {Event} e The event object
20242      * @param {String} id The id of the dragged element
20243      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20244      */
20245     beforeInvalidDrop : function(target, e, id){
20246         return true;
20247     },
20248
20249     // private
20250     handleMouseDown : function(e){
20251         if(this.dragging) {
20252             return;
20253         }
20254         var data = this.getDragData(e);
20255         if(data && this.onBeforeDrag(data, e) !== false){
20256             this.dragData = data;
20257             this.proxy.stop();
20258             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20259         } 
20260     },
20261
20262     /**
20263      * An empty function by default, but provided so that you can perform a custom action before the initial
20264      * drag event begins and optionally cancel it.
20265      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20266      * @param {Event} e The event object
20267      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20268      */
20269     onBeforeDrag : function(data, e){
20270         return true;
20271     },
20272
20273     /**
20274      * An empty function by default, but provided so that you can perform a custom action once the initial
20275      * drag event has begun.  The drag cannot be canceled from this function.
20276      * @param {Number} x The x position of the click on the dragged object
20277      * @param {Number} y The y position of the click on the dragged object
20278      */
20279     onStartDrag : Roo.emptyFn,
20280
20281     // private - YUI override
20282     startDrag : function(x, y){
20283         this.proxy.reset();
20284         this.dragging = true;
20285         this.proxy.update("");
20286         this.onInitDrag(x, y);
20287         this.proxy.show();
20288     },
20289
20290     // private
20291     onInitDrag : function(x, y){
20292         var clone = this.el.dom.cloneNode(true);
20293         clone.id = Roo.id(); // prevent duplicate ids
20294         this.proxy.update(clone);
20295         this.onStartDrag(x, y);
20296         return true;
20297     },
20298
20299     /**
20300      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20301      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20302      */
20303     getProxy : function(){
20304         return this.proxy;  
20305     },
20306
20307     /**
20308      * Hides the drag source's {@link Roo.dd.StatusProxy}
20309      */
20310     hideProxy : function(){
20311         this.proxy.hide();  
20312         this.proxy.reset(true);
20313         this.dragging = false;
20314     },
20315
20316     // private
20317     triggerCacheRefresh : function(){
20318         Roo.dd.DDM.refreshCache(this.groups);
20319     },
20320
20321     // private - override to prevent hiding
20322     b4EndDrag: function(e) {
20323     },
20324
20325     // private - override to prevent moving
20326     endDrag : function(e){
20327         this.onEndDrag(this.dragData, e);
20328     },
20329
20330     // private
20331     onEndDrag : function(data, e){
20332     },
20333     
20334     // private - pin to cursor
20335     autoOffset : function(x, y) {
20336         this.setDelta(-12, -20);
20337     }    
20338 });/*
20339  * Based on:
20340  * Ext JS Library 1.1.1
20341  * Copyright(c) 2006-2007, Ext JS, LLC.
20342  *
20343  * Originally Released Under LGPL - original licence link has changed is not relivant.
20344  *
20345  * Fork - LGPL
20346  * <script type="text/javascript">
20347  */
20348
20349
20350 /**
20351  * @class Roo.dd.DropTarget
20352  * @extends Roo.dd.DDTarget
20353  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20354  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20355  * @constructor
20356  * @param {String/HTMLElement/Element} el The container element
20357  * @param {Object} config
20358  */
20359 Roo.dd.DropTarget = function(el, config){
20360     this.el = Roo.get(el);
20361     
20362     var listeners = false; ;
20363     if (config && config.listeners) {
20364         listeners= config.listeners;
20365         delete config.listeners;
20366     }
20367     Roo.apply(this, config);
20368     
20369     if(this.containerScroll){
20370         Roo.dd.ScrollManager.register(this.el);
20371     }
20372     this.addEvents( {
20373          /**
20374          * @scope Roo.dd.DropTarget
20375          */
20376          
20377          /**
20378          * @event enter
20379          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20380          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20381          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20382          * 
20383          * IMPORTANT : it should set this.overClass and this.dropAllowed
20384          * 
20385          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20386          * @param {Event} e The event
20387          * @param {Object} data An object containing arbitrary data supplied by the drag source
20388          */
20389         "enter" : true,
20390         
20391          /**
20392          * @event over
20393          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20394          * This method will be called on every mouse movement while the drag source is over the drop target.
20395          * This default implementation simply returns the dropAllowed config value.
20396          * 
20397          * IMPORTANT : it should set this.dropAllowed
20398          * 
20399          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20400          * @param {Event} e The event
20401          * @param {Object} data An object containing arbitrary data supplied by the drag source
20402          
20403          */
20404         "over" : true,
20405         /**
20406          * @event out
20407          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20408          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20409          * overClass (if any) from the drop element.
20410          * 
20411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20412          * @param {Event} e The event
20413          * @param {Object} data An object containing arbitrary data supplied by the drag source
20414          */
20415          "out" : true,
20416          
20417         /**
20418          * @event drop
20419          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20420          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20421          * implementation that does something to process the drop event and returns true so that the drag source's
20422          * repair action does not run.
20423          * 
20424          * IMPORTANT : it should set this.success
20425          * 
20426          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20427          * @param {Event} e The event
20428          * @param {Object} data An object containing arbitrary data supplied by the drag source
20429         */
20430          "drop" : true
20431     });
20432             
20433      
20434     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20435         this.el.dom, 
20436         this.ddGroup || this.group,
20437         {
20438             isTarget: true,
20439             listeners : listeners || {} 
20440            
20441         
20442         }
20443     );
20444
20445 };
20446
20447 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20448     /**
20449      * @cfg {String} overClass
20450      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20451      */
20452      /**
20453      * @cfg {String} ddGroup
20454      * The drag drop group to handle drop events for
20455      */
20456      
20457     /**
20458      * @cfg {String} dropAllowed
20459      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20460      */
20461     dropAllowed : "x-dd-drop-ok",
20462     /**
20463      * @cfg {String} dropNotAllowed
20464      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20465      */
20466     dropNotAllowed : "x-dd-drop-nodrop",
20467     /**
20468      * @cfg {boolean} success
20469      * set this after drop listener.. 
20470      */
20471     success : false,
20472     /**
20473      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20474      * if the drop point is valid for over/enter..
20475      */
20476     valid : false,
20477     // private
20478     isTarget : true,
20479
20480     // private
20481     isNotifyTarget : true,
20482     
20483     /**
20484      * @hide
20485      */
20486     notifyEnter : function(dd, e, data)
20487     {
20488         this.valid = true;
20489         this.fireEvent('enter', dd, e, data);
20490         if(this.overClass){
20491             this.el.addClass(this.overClass);
20492         }
20493         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20494             this.valid ? this.dropAllowed : this.dropNotAllowed
20495         );
20496     },
20497
20498     /**
20499      * @hide
20500      */
20501     notifyOver : function(dd, e, data)
20502     {
20503         this.valid = true;
20504         this.fireEvent('over', dd, e, data);
20505         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20506             this.valid ? this.dropAllowed : this.dropNotAllowed
20507         );
20508     },
20509
20510     /**
20511      * @hide
20512      */
20513     notifyOut : function(dd, e, data)
20514     {
20515         this.fireEvent('out', dd, e, data);
20516         if(this.overClass){
20517             this.el.removeClass(this.overClass);
20518         }
20519     },
20520
20521     /**
20522      * @hide
20523      */
20524     notifyDrop : function(dd, e, data)
20525     {
20526         this.success = false;
20527         this.fireEvent('drop', dd, e, data);
20528         return this.success;
20529     }
20530 });/*
20531  * Based on:
20532  * Ext JS Library 1.1.1
20533  * Copyright(c) 2006-2007, Ext JS, LLC.
20534  *
20535  * Originally Released Under LGPL - original licence link has changed is not relivant.
20536  *
20537  * Fork - LGPL
20538  * <script type="text/javascript">
20539  */
20540
20541
20542 /**
20543  * @class Roo.dd.DragZone
20544  * @extends Roo.dd.DragSource
20545  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20546  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20547  * @constructor
20548  * @param {String/HTMLElement/Element} el The container element
20549  * @param {Object} config
20550  */
20551 Roo.dd.DragZone = function(el, config){
20552     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20553     if(this.containerScroll){
20554         Roo.dd.ScrollManager.register(this.el);
20555     }
20556 };
20557
20558 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20559     /**
20560      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20561      * for auto scrolling during drag operations.
20562      */
20563     /**
20564      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20565      * method after a failed drop (defaults to "c3daf9" - light blue)
20566      */
20567
20568     /**
20569      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20570      * for a valid target to drag based on the mouse down. Override this method
20571      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20572      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20573      * @param {EventObject} e The mouse down event
20574      * @return {Object} The dragData
20575      */
20576     getDragData : function(e){
20577         return Roo.dd.Registry.getHandleFromEvent(e);
20578     },
20579     
20580     /**
20581      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20582      * this.dragData.ddel
20583      * @param {Number} x The x position of the click on the dragged object
20584      * @param {Number} y The y position of the click on the dragged object
20585      * @return {Boolean} true to continue the drag, false to cancel
20586      */
20587     onInitDrag : function(x, y){
20588         this.proxy.update(this.dragData.ddel.cloneNode(true));
20589         this.onStartDrag(x, y);
20590         return true;
20591     },
20592     
20593     /**
20594      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20595      */
20596     afterRepair : function(){
20597         if(Roo.enableFx){
20598             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20599         }
20600         this.dragging = false;
20601     },
20602
20603     /**
20604      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20605      * the XY of this.dragData.ddel
20606      * @param {EventObject} e The mouse up event
20607      * @return {Array} The xy location (e.g. [100, 200])
20608      */
20609     getRepairXY : function(e){
20610         return Roo.Element.fly(this.dragData.ddel).getXY();  
20611     }
20612 });/*
20613  * Based on:
20614  * Ext JS Library 1.1.1
20615  * Copyright(c) 2006-2007, Ext JS, LLC.
20616  *
20617  * Originally Released Under LGPL - original licence link has changed is not relivant.
20618  *
20619  * Fork - LGPL
20620  * <script type="text/javascript">
20621  */
20622 /**
20623  * @class Roo.dd.DropZone
20624  * @extends Roo.dd.DropTarget
20625  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20626  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20627  * @constructor
20628  * @param {String/HTMLElement/Element} el The container element
20629  * @param {Object} config
20630  */
20631 Roo.dd.DropZone = function(el, config){
20632     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20633 };
20634
20635 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20636     /**
20637      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20638      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20639      * provide your own custom lookup.
20640      * @param {Event} e The event
20641      * @return {Object} data The custom data
20642      */
20643     getTargetFromEvent : function(e){
20644         return Roo.dd.Registry.getTargetFromEvent(e);
20645     },
20646
20647     /**
20648      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20649      * that it has registered.  This method has no default implementation and should be overridden to provide
20650      * node-specific processing if necessary.
20651      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20652      * {@link #getTargetFromEvent} for this node)
20653      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20654      * @param {Event} e The event
20655      * @param {Object} data An object containing arbitrary data supplied by the drag source
20656      */
20657     onNodeEnter : function(n, dd, e, data){
20658         
20659     },
20660
20661     /**
20662      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20663      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20664      * overridden to provide the proper feedback.
20665      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20666      * {@link #getTargetFromEvent} for this node)
20667      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20668      * @param {Event} e The event
20669      * @param {Object} data An object containing arbitrary data supplied by the drag source
20670      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20671      * underlying {@link Roo.dd.StatusProxy} can be updated
20672      */
20673     onNodeOver : function(n, dd, e, data){
20674         return this.dropAllowed;
20675     },
20676
20677     /**
20678      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20679      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20680      * node-specific processing if necessary.
20681      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20682      * {@link #getTargetFromEvent} for this node)
20683      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20684      * @param {Event} e The event
20685      * @param {Object} data An object containing arbitrary data supplied by the drag source
20686      */
20687     onNodeOut : function(n, dd, e, data){
20688         
20689     },
20690
20691     /**
20692      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20693      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20694      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20695      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20696      * {@link #getTargetFromEvent} for this node)
20697      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20698      * @param {Event} e The event
20699      * @param {Object} data An object containing arbitrary data supplied by the drag source
20700      * @return {Boolean} True if the drop was valid, else false
20701      */
20702     onNodeDrop : function(n, dd, e, data){
20703         return false;
20704     },
20705
20706     /**
20707      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20708      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20709      * it should be overridden to provide the proper feedback if necessary.
20710      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20711      * @param {Event} e The event
20712      * @param {Object} data An object containing arbitrary data supplied by the drag source
20713      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20714      * underlying {@link Roo.dd.StatusProxy} can be updated
20715      */
20716     onContainerOver : function(dd, e, data){
20717         return this.dropNotAllowed;
20718     },
20719
20720     /**
20721      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20722      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20723      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20724      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20725      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20726      * @param {Event} e The event
20727      * @param {Object} data An object containing arbitrary data supplied by the drag source
20728      * @return {Boolean} True if the drop was valid, else false
20729      */
20730     onContainerDrop : function(dd, e, data){
20731         return false;
20732     },
20733
20734     /**
20735      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20736      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20737      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20738      * you should override this method and provide a custom implementation.
20739      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20740      * @param {Event} e The event
20741      * @param {Object} data An object containing arbitrary data supplied by the drag source
20742      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20743      * underlying {@link Roo.dd.StatusProxy} can be updated
20744      */
20745     notifyEnter : function(dd, e, data){
20746         return this.dropNotAllowed;
20747     },
20748
20749     /**
20750      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20751      * This method will be called on every mouse movement while the drag source is over the drop zone.
20752      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20753      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20754      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20755      * registered node, it will call {@link #onContainerOver}.
20756      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20757      * @param {Event} e The event
20758      * @param {Object} data An object containing arbitrary data supplied by the drag source
20759      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20760      * underlying {@link Roo.dd.StatusProxy} can be updated
20761      */
20762     notifyOver : function(dd, e, data){
20763         var n = this.getTargetFromEvent(e);
20764         if(!n){ // not over valid drop target
20765             if(this.lastOverNode){
20766                 this.onNodeOut(this.lastOverNode, dd, e, data);
20767                 this.lastOverNode = null;
20768             }
20769             return this.onContainerOver(dd, e, data);
20770         }
20771         if(this.lastOverNode != n){
20772             if(this.lastOverNode){
20773                 this.onNodeOut(this.lastOverNode, dd, e, data);
20774             }
20775             this.onNodeEnter(n, dd, e, data);
20776             this.lastOverNode = n;
20777         }
20778         return this.onNodeOver(n, dd, e, data);
20779     },
20780
20781     /**
20782      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20783      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20784      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20785      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20786      * @param {Event} e The event
20787      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20788      */
20789     notifyOut : function(dd, e, data){
20790         if(this.lastOverNode){
20791             this.onNodeOut(this.lastOverNode, dd, e, data);
20792             this.lastOverNode = null;
20793         }
20794     },
20795
20796     /**
20797      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20798      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20799      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20800      * otherwise it will call {@link #onContainerDrop}.
20801      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20802      * @param {Event} e The event
20803      * @param {Object} data An object containing arbitrary data supplied by the drag source
20804      * @return {Boolean} True if the drop was valid, else false
20805      */
20806     notifyDrop : function(dd, e, data){
20807         if(this.lastOverNode){
20808             this.onNodeOut(this.lastOverNode, dd, e, data);
20809             this.lastOverNode = null;
20810         }
20811         var n = this.getTargetFromEvent(e);
20812         return n ?
20813             this.onNodeDrop(n, dd, e, data) :
20814             this.onContainerDrop(dd, e, data);
20815     },
20816
20817     // private
20818     triggerCacheRefresh : function(){
20819         Roo.dd.DDM.refreshCache(this.groups);
20820     }  
20821 });/*
20822  * Based on:
20823  * Ext JS Library 1.1.1
20824  * Copyright(c) 2006-2007, Ext JS, LLC.
20825  *
20826  * Originally Released Under LGPL - original licence link has changed is not relivant.
20827  *
20828  * Fork - LGPL
20829  * <script type="text/javascript">
20830  */
20831
20832
20833 /**
20834  * @class Roo.data.SortTypes
20835  * @singleton
20836  * Defines the default sorting (casting?) comparison functions used when sorting data.
20837  */
20838 Roo.data.SortTypes = {
20839     /**
20840      * Default sort that does nothing
20841      * @param {Mixed} s The value being converted
20842      * @return {Mixed} The comparison value
20843      */
20844     none : function(s){
20845         return s;
20846     },
20847     
20848     /**
20849      * The regular expression used to strip tags
20850      * @type {RegExp}
20851      * @property
20852      */
20853     stripTagsRE : /<\/?[^>]+>/gi,
20854     
20855     /**
20856      * Strips all HTML tags to sort on text only
20857      * @param {Mixed} s The value being converted
20858      * @return {String} The comparison value
20859      */
20860     asText : function(s){
20861         return String(s).replace(this.stripTagsRE, "");
20862     },
20863     
20864     /**
20865      * Strips all HTML tags to sort on text only - Case insensitive
20866      * @param {Mixed} s The value being converted
20867      * @return {String} The comparison value
20868      */
20869     asUCText : function(s){
20870         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20871     },
20872     
20873     /**
20874      * Case insensitive string
20875      * @param {Mixed} s The value being converted
20876      * @return {String} The comparison value
20877      */
20878     asUCString : function(s) {
20879         return String(s).toUpperCase();
20880     },
20881     
20882     /**
20883      * Date sorting
20884      * @param {Mixed} s The value being converted
20885      * @return {Number} The comparison value
20886      */
20887     asDate : function(s) {
20888         if(!s){
20889             return 0;
20890         }
20891         if(s instanceof Date){
20892             return s.getTime();
20893         }
20894         return Date.parse(String(s));
20895     },
20896     
20897     /**
20898      * Float sorting
20899      * @param {Mixed} s The value being converted
20900      * @return {Float} The comparison value
20901      */
20902     asFloat : function(s) {
20903         var val = parseFloat(String(s).replace(/,/g, ""));
20904         if(isNaN(val)) val = 0;
20905         return val;
20906     },
20907     
20908     /**
20909      * Integer sorting
20910      * @param {Mixed} s The value being converted
20911      * @return {Number} The comparison value
20912      */
20913     asInt : function(s) {
20914         var val = parseInt(String(s).replace(/,/g, ""));
20915         if(isNaN(val)) val = 0;
20916         return val;
20917     }
20918 };/*
20919  * Based on:
20920  * Ext JS Library 1.1.1
20921  * Copyright(c) 2006-2007, Ext JS, LLC.
20922  *
20923  * Originally Released Under LGPL - original licence link has changed is not relivant.
20924  *
20925  * Fork - LGPL
20926  * <script type="text/javascript">
20927  */
20928
20929 /**
20930 * @class Roo.data.Record
20931  * Instances of this class encapsulate both record <em>definition</em> information, and record
20932  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20933  * to access Records cached in an {@link Roo.data.Store} object.<br>
20934  * <p>
20935  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20936  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20937  * objects.<br>
20938  * <p>
20939  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20940  * @constructor
20941  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20942  * {@link #create}. The parameters are the same.
20943  * @param {Array} data An associative Array of data values keyed by the field name.
20944  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20945  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20946  * not specified an integer id is generated.
20947  */
20948 Roo.data.Record = function(data, id){
20949     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20950     this.data = data;
20951 };
20952
20953 /**
20954  * Generate a constructor for a specific record layout.
20955  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20956  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20957  * Each field definition object may contain the following properties: <ul>
20958  * <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,
20959  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20960  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20961  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20962  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20963  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20964  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20965  * this may be omitted.</p></li>
20966  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20967  * <ul><li>auto (Default, implies no conversion)</li>
20968  * <li>string</li>
20969  * <li>int</li>
20970  * <li>float</li>
20971  * <li>boolean</li>
20972  * <li>date</li></ul></p></li>
20973  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20974  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20975  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20976  * by the Reader into an object that will be stored in the Record. It is passed the
20977  * following parameters:<ul>
20978  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20979  * </ul></p></li>
20980  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20981  * </ul>
20982  * <br>usage:<br><pre><code>
20983 var TopicRecord = Roo.data.Record.create(
20984     {name: 'title', mapping: 'topic_title'},
20985     {name: 'author', mapping: 'username'},
20986     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20987     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20988     {name: 'lastPoster', mapping: 'user2'},
20989     {name: 'excerpt', mapping: 'post_text'}
20990 );
20991
20992 var myNewRecord = new TopicRecord({
20993     title: 'Do my job please',
20994     author: 'noobie',
20995     totalPosts: 1,
20996     lastPost: new Date(),
20997     lastPoster: 'Animal',
20998     excerpt: 'No way dude!'
20999 });
21000 myStore.add(myNewRecord);
21001 </code></pre>
21002  * @method create
21003  * @static
21004  */
21005 Roo.data.Record.create = function(o){
21006     var f = function(){
21007         f.superclass.constructor.apply(this, arguments);
21008     };
21009     Roo.extend(f, Roo.data.Record);
21010     var p = f.prototype;
21011     p.fields = new Roo.util.MixedCollection(false, function(field){
21012         return field.name;
21013     });
21014     for(var i = 0, len = o.length; i < len; i++){
21015         p.fields.add(new Roo.data.Field(o[i]));
21016     }
21017     f.getField = function(name){
21018         return p.fields.get(name);  
21019     };
21020     return f;
21021 };
21022
21023 Roo.data.Record.AUTO_ID = 1000;
21024 Roo.data.Record.EDIT = 'edit';
21025 Roo.data.Record.REJECT = 'reject';
21026 Roo.data.Record.COMMIT = 'commit';
21027
21028 Roo.data.Record.prototype = {
21029     /**
21030      * Readonly flag - true if this record has been modified.
21031      * @type Boolean
21032      */
21033     dirty : false,
21034     editing : false,
21035     error: null,
21036     modified: null,
21037
21038     // private
21039     join : function(store){
21040         this.store = store;
21041     },
21042
21043     /**
21044      * Set the named field to the specified value.
21045      * @param {String} name The name of the field to set.
21046      * @param {Object} value The value to set the field to.
21047      */
21048     set : function(name, value){
21049         if(this.data[name] == value){
21050             return;
21051         }
21052         this.dirty = true;
21053         if(!this.modified){
21054             this.modified = {};
21055         }
21056         if(typeof this.modified[name] == 'undefined'){
21057             this.modified[name] = this.data[name];
21058         }
21059         this.data[name] = value;
21060         if(!this.editing && this.store){
21061             this.store.afterEdit(this);
21062         }       
21063     },
21064
21065     /**
21066      * Get the value of the named field.
21067      * @param {String} name The name of the field to get the value of.
21068      * @return {Object} The value of the field.
21069      */
21070     get : function(name){
21071         return this.data[name]; 
21072     },
21073
21074     // private
21075     beginEdit : function(){
21076         this.editing = true;
21077         this.modified = {}; 
21078     },
21079
21080     // private
21081     cancelEdit : function(){
21082         this.editing = false;
21083         delete this.modified;
21084     },
21085
21086     // private
21087     endEdit : function(){
21088         this.editing = false;
21089         if(this.dirty && this.store){
21090             this.store.afterEdit(this);
21091         }
21092     },
21093
21094     /**
21095      * Usually called by the {@link Roo.data.Store} which owns the Record.
21096      * Rejects all changes made to the Record since either creation, or the last commit operation.
21097      * Modified fields are reverted to their original values.
21098      * <p>
21099      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21100      * of reject operations.
21101      */
21102     reject : function(){
21103         var m = this.modified;
21104         for(var n in m){
21105             if(typeof m[n] != "function"){
21106                 this.data[n] = m[n];
21107             }
21108         }
21109         this.dirty = false;
21110         delete this.modified;
21111         this.editing = false;
21112         if(this.store){
21113             this.store.afterReject(this);
21114         }
21115     },
21116
21117     /**
21118      * Usually called by the {@link Roo.data.Store} which owns the Record.
21119      * Commits all changes made to the Record since either creation, or the last commit operation.
21120      * <p>
21121      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21122      * of commit operations.
21123      */
21124     commit : function(){
21125         this.dirty = false;
21126         delete this.modified;
21127         this.editing = false;
21128         if(this.store){
21129             this.store.afterCommit(this);
21130         }
21131     },
21132
21133     // private
21134     hasError : function(){
21135         return this.error != null;
21136     },
21137
21138     // private
21139     clearError : function(){
21140         this.error = null;
21141     },
21142
21143     /**
21144      * Creates a copy of this record.
21145      * @param {String} id (optional) A new record id if you don't want to use this record's id
21146      * @return {Record}
21147      */
21148     copy : function(newId) {
21149         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21150     }
21151 };/*
21152  * Based on:
21153  * Ext JS Library 1.1.1
21154  * Copyright(c) 2006-2007, Ext JS, LLC.
21155  *
21156  * Originally Released Under LGPL - original licence link has changed is not relivant.
21157  *
21158  * Fork - LGPL
21159  * <script type="text/javascript">
21160  */
21161
21162
21163
21164 /**
21165  * @class Roo.data.Store
21166  * @extends Roo.util.Observable
21167  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21168  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21169  * <p>
21170  * 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
21171  * has no knowledge of the format of the data returned by the Proxy.<br>
21172  * <p>
21173  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21174  * instances from the data object. These records are cached and made available through accessor functions.
21175  * @constructor
21176  * Creates a new Store.
21177  * @param {Object} config A config object containing the objects needed for the Store to access data,
21178  * and read the data into Records.
21179  */
21180 Roo.data.Store = function(config){
21181     this.data = new Roo.util.MixedCollection(false);
21182     this.data.getKey = function(o){
21183         return o.id;
21184     };
21185     this.baseParams = {};
21186     // private
21187     this.paramNames = {
21188         "start" : "start",
21189         "limit" : "limit",
21190         "sort" : "sort",
21191         "dir" : "dir",
21192         "multisort" : "_multisort"
21193     };
21194
21195     if(config && config.data){
21196         this.inlineData = config.data;
21197         delete config.data;
21198     }
21199
21200     Roo.apply(this, config);
21201     
21202     if(this.reader){ // reader passed
21203         this.reader = Roo.factory(this.reader, Roo.data);
21204         this.reader.xmodule = this.xmodule || false;
21205         if(!this.recordType){
21206             this.recordType = this.reader.recordType;
21207         }
21208         if(this.reader.onMetaChange){
21209             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21210         }
21211     }
21212
21213     if(this.recordType){
21214         this.fields = this.recordType.prototype.fields;
21215     }
21216     this.modified = [];
21217
21218     this.addEvents({
21219         /**
21220          * @event datachanged
21221          * Fires when the data cache has changed, and a widget which is using this Store
21222          * as a Record cache should refresh its view.
21223          * @param {Store} this
21224          */
21225         datachanged : true,
21226         /**
21227          * @event metachange
21228          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21229          * @param {Store} this
21230          * @param {Object} meta The JSON metadata
21231          */
21232         metachange : true,
21233         /**
21234          * @event add
21235          * Fires when Records have been added to the Store
21236          * @param {Store} this
21237          * @param {Roo.data.Record[]} records The array of Records added
21238          * @param {Number} index The index at which the record(s) were added
21239          */
21240         add : true,
21241         /**
21242          * @event remove
21243          * Fires when a Record has been removed from the Store
21244          * @param {Store} this
21245          * @param {Roo.data.Record} record The Record that was removed
21246          * @param {Number} index The index at which the record was removed
21247          */
21248         remove : true,
21249         /**
21250          * @event update
21251          * Fires when a Record has been updated
21252          * @param {Store} this
21253          * @param {Roo.data.Record} record The Record that was updated
21254          * @param {String} operation The update operation being performed.  Value may be one of:
21255          * <pre><code>
21256  Roo.data.Record.EDIT
21257  Roo.data.Record.REJECT
21258  Roo.data.Record.COMMIT
21259          * </code></pre>
21260          */
21261         update : true,
21262         /**
21263          * @event clear
21264          * Fires when the data cache has been cleared.
21265          * @param {Store} this
21266          */
21267         clear : true,
21268         /**
21269          * @event beforeload
21270          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21271          * the load action will be canceled.
21272          * @param {Store} this
21273          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21274          */
21275         beforeload : true,
21276         /**
21277          * @event beforeloadadd
21278          * Fires after a new set of Records has been loaded.
21279          * @param {Store} this
21280          * @param {Roo.data.Record[]} records The Records that were loaded
21281          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21282          */
21283         beforeloadadd : true,
21284         /**
21285          * @event load
21286          * Fires after a new set of Records has been loaded, before they are added to the store.
21287          * @param {Store} this
21288          * @param {Roo.data.Record[]} records The Records that were loaded
21289          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21290          * @params {Object} return from reader
21291          */
21292         load : true,
21293         /**
21294          * @event loadexception
21295          * Fires if an exception occurs in the Proxy during loading.
21296          * Called with the signature of the Proxy's "loadexception" event.
21297          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21298          * 
21299          * @param {Proxy} 
21300          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21301          * @param {Object} load options 
21302          * @param {Object} jsonData from your request (normally this contains the Exception)
21303          */
21304         loadexception : true
21305     });
21306     
21307     if(this.proxy){
21308         this.proxy = Roo.factory(this.proxy, Roo.data);
21309         this.proxy.xmodule = this.xmodule || false;
21310         this.relayEvents(this.proxy,  ["loadexception"]);
21311     }
21312     this.sortToggle = {};
21313     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21314
21315     Roo.data.Store.superclass.constructor.call(this);
21316
21317     if(this.inlineData){
21318         this.loadData(this.inlineData);
21319         delete this.inlineData;
21320     }
21321 };
21322
21323 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21324      /**
21325     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21326     * without a remote query - used by combo/forms at present.
21327     */
21328     
21329     /**
21330     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21331     */
21332     /**
21333     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21334     */
21335     /**
21336     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21337     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21338     */
21339     /**
21340     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21341     * on any HTTP request
21342     */
21343     /**
21344     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21345     */
21346     /**
21347     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21348     */
21349     multiSort: false,
21350     /**
21351     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21352     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21353     */
21354     remoteSort : false,
21355
21356     /**
21357     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21358      * loaded or when a record is removed. (defaults to false).
21359     */
21360     pruneModifiedRecords : false,
21361
21362     // private
21363     lastOptions : null,
21364
21365     /**
21366      * Add Records to the Store and fires the add event.
21367      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21368      */
21369     add : function(records){
21370         records = [].concat(records);
21371         for(var i = 0, len = records.length; i < len; i++){
21372             records[i].join(this);
21373         }
21374         var index = this.data.length;
21375         this.data.addAll(records);
21376         this.fireEvent("add", this, records, index);
21377     },
21378
21379     /**
21380      * Remove a Record from the Store and fires the remove event.
21381      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21382      */
21383     remove : function(record){
21384         var index = this.data.indexOf(record);
21385         this.data.removeAt(index);
21386         if(this.pruneModifiedRecords){
21387             this.modified.remove(record);
21388         }
21389         this.fireEvent("remove", this, record, index);
21390     },
21391
21392     /**
21393      * Remove all Records from the Store and fires the clear event.
21394      */
21395     removeAll : function(){
21396         this.data.clear();
21397         if(this.pruneModifiedRecords){
21398             this.modified = [];
21399         }
21400         this.fireEvent("clear", this);
21401     },
21402
21403     /**
21404      * Inserts Records to the Store at the given index and fires the add event.
21405      * @param {Number} index The start index at which to insert the passed Records.
21406      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21407      */
21408     insert : function(index, records){
21409         records = [].concat(records);
21410         for(var i = 0, len = records.length; i < len; i++){
21411             this.data.insert(index, records[i]);
21412             records[i].join(this);
21413         }
21414         this.fireEvent("add", this, records, index);
21415     },
21416
21417     /**
21418      * Get the index within the cache of the passed Record.
21419      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21420      * @return {Number} The index of the passed Record. Returns -1 if not found.
21421      */
21422     indexOf : function(record){
21423         return this.data.indexOf(record);
21424     },
21425
21426     /**
21427      * Get the index within the cache of the Record with the passed id.
21428      * @param {String} id The id of the Record to find.
21429      * @return {Number} The index of the Record. Returns -1 if not found.
21430      */
21431     indexOfId : function(id){
21432         return this.data.indexOfKey(id);
21433     },
21434
21435     /**
21436      * Get the Record with the specified id.
21437      * @param {String} id The id of the Record to find.
21438      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21439      */
21440     getById : function(id){
21441         return this.data.key(id);
21442     },
21443
21444     /**
21445      * Get the Record at the specified index.
21446      * @param {Number} index The index of the Record to find.
21447      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21448      */
21449     getAt : function(index){
21450         return this.data.itemAt(index);
21451     },
21452
21453     /**
21454      * Returns a range of Records between specified indices.
21455      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21456      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21457      * @return {Roo.data.Record[]} An array of Records
21458      */
21459     getRange : function(start, end){
21460         return this.data.getRange(start, end);
21461     },
21462
21463     // private
21464     storeOptions : function(o){
21465         o = Roo.apply({}, o);
21466         delete o.callback;
21467         delete o.scope;
21468         this.lastOptions = o;
21469     },
21470
21471     /**
21472      * Loads the Record cache from the configured Proxy using the configured Reader.
21473      * <p>
21474      * If using remote paging, then the first load call must specify the <em>start</em>
21475      * and <em>limit</em> properties in the options.params property to establish the initial
21476      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21477      * <p>
21478      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21479      * and this call will return before the new data has been loaded. Perform any post-processing
21480      * in a callback function, or in a "load" event handler.</strong>
21481      * <p>
21482      * @param {Object} options An object containing properties which control loading options:<ul>
21483      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21484      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21485      * passed the following arguments:<ul>
21486      * <li>r : Roo.data.Record[]</li>
21487      * <li>options: Options object from the load call</li>
21488      * <li>success: Boolean success indicator</li></ul></li>
21489      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21490      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21491      * </ul>
21492      */
21493     load : function(options){
21494         options = options || {};
21495         if(this.fireEvent("beforeload", this, options) !== false){
21496             this.storeOptions(options);
21497             var p = Roo.apply(options.params || {}, this.baseParams);
21498             // if meta was not loaded from remote source.. try requesting it.
21499             if (!this.reader.metaFromRemote) {
21500                 p._requestMeta = 1;
21501             }
21502             if(this.sortInfo && this.remoteSort){
21503                 var pn = this.paramNames;
21504                 p[pn["sort"]] = this.sortInfo.field;
21505                 p[pn["dir"]] = this.sortInfo.direction;
21506             }
21507             if (this.multiSort) {
21508                 var pn = this.paramNames;
21509                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21510             }
21511             
21512             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21513         }
21514     },
21515
21516     /**
21517      * Reloads the Record cache from the configured Proxy using the configured Reader and
21518      * the options from the last load operation performed.
21519      * @param {Object} options (optional) An object containing properties which may override the options
21520      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21521      * the most recently used options are reused).
21522      */
21523     reload : function(options){
21524         this.load(Roo.applyIf(options||{}, this.lastOptions));
21525     },
21526
21527     // private
21528     // Called as a callback by the Reader during a load operation.
21529     loadRecords : function(o, options, success){
21530         if(!o || success === false){
21531             if(success !== false){
21532                 this.fireEvent("load", this, [], options, o);
21533             }
21534             if(options.callback){
21535                 options.callback.call(options.scope || this, [], options, false);
21536             }
21537             return;
21538         }
21539         // if data returned failure - throw an exception.
21540         if (o.success === false) {
21541             // show a message if no listener is registered.
21542             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21543                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21544             }
21545             // loadmask wil be hooked into this..
21546             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21547             return;
21548         }
21549         var r = o.records, t = o.totalRecords || r.length;
21550         
21551         this.fireEvent("beforeloadadd", this, r, options, o);
21552         
21553         if(!options || options.add !== true){
21554             if(this.pruneModifiedRecords){
21555                 this.modified = [];
21556             }
21557             for(var i = 0, len = r.length; i < len; i++){
21558                 r[i].join(this);
21559             }
21560             if(this.snapshot){
21561                 this.data = this.snapshot;
21562                 delete this.snapshot;
21563             }
21564             this.data.clear();
21565             this.data.addAll(r);
21566             this.totalLength = t;
21567             this.applySort();
21568             this.fireEvent("datachanged", this);
21569         }else{
21570             this.totalLength = Math.max(t, this.data.length+r.length);
21571             this.add(r);
21572         }
21573         this.fireEvent("load", this, r, options, o);
21574         if(options.callback){
21575             options.callback.call(options.scope || this, r, options, true);
21576         }
21577     },
21578
21579
21580     /**
21581      * Loads data from a passed data block. A Reader which understands the format of the data
21582      * must have been configured in the constructor.
21583      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21584      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21585      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21586      */
21587     loadData : function(o, append){
21588         var r = this.reader.readRecords(o);
21589         this.loadRecords(r, {add: append}, true);
21590     },
21591
21592     /**
21593      * Gets the number of cached records.
21594      * <p>
21595      * <em>If using paging, this may not be the total size of the dataset. If the data object
21596      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21597      * the data set size</em>
21598      */
21599     getCount : function(){
21600         return this.data.length || 0;
21601     },
21602
21603     /**
21604      * Gets the total number of records in the dataset as returned by the server.
21605      * <p>
21606      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21607      * the dataset size</em>
21608      */
21609     getTotalCount : function(){
21610         return this.totalLength || 0;
21611     },
21612
21613     /**
21614      * Returns the sort state of the Store as an object with two properties:
21615      * <pre><code>
21616  field {String} The name of the field by which the Records are sorted
21617  direction {String} The sort order, "ASC" or "DESC"
21618      * </code></pre>
21619      */
21620     getSortState : function(){
21621         return this.sortInfo;
21622     },
21623
21624     // private
21625     applySort : function(){
21626         if(this.sortInfo && !this.remoteSort){
21627             var s = this.sortInfo, f = s.field;
21628             var st = this.fields.get(f).sortType;
21629             var fn = function(r1, r2){
21630                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21631                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21632             };
21633             this.data.sort(s.direction, fn);
21634             if(this.snapshot && this.snapshot != this.data){
21635                 this.snapshot.sort(s.direction, fn);
21636             }
21637         }
21638     },
21639
21640     /**
21641      * Sets the default sort column and order to be used by the next load operation.
21642      * @param {String} fieldName The name of the field to sort by.
21643      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21644      */
21645     setDefaultSort : function(field, dir){
21646         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21647     },
21648
21649     /**
21650      * Sort the Records.
21651      * If remote sorting is used, the sort is performed on the server, and the cache is
21652      * reloaded. If local sorting is used, the cache is sorted internally.
21653      * @param {String} fieldName The name of the field to sort by.
21654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21655      */
21656     sort : function(fieldName, dir){
21657         var f = this.fields.get(fieldName);
21658         if(!dir){
21659             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21660             
21661             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21662                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21663             }else{
21664                 dir = f.sortDir;
21665             }
21666         }
21667         this.sortToggle[f.name] = dir;
21668         this.sortInfo = {field: f.name, direction: dir};
21669         if(!this.remoteSort){
21670             this.applySort();
21671             this.fireEvent("datachanged", this);
21672         }else{
21673             this.load(this.lastOptions);
21674         }
21675     },
21676
21677     /**
21678      * Calls the specified function for each of the Records in the cache.
21679      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21680      * Returning <em>false</em> aborts and exits the iteration.
21681      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21682      */
21683     each : function(fn, scope){
21684         this.data.each(fn, scope);
21685     },
21686
21687     /**
21688      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21689      * (e.g., during paging).
21690      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21691      */
21692     getModifiedRecords : function(){
21693         return this.modified;
21694     },
21695
21696     // private
21697     createFilterFn : function(property, value, anyMatch){
21698         if(!value.exec){ // not a regex
21699             value = String(value);
21700             if(value.length == 0){
21701                 return false;
21702             }
21703             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21704         }
21705         return function(r){
21706             return value.test(r.data[property]);
21707         };
21708     },
21709
21710     /**
21711      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21712      * @param {String} property A field on your records
21713      * @param {Number} start The record index to start at (defaults to 0)
21714      * @param {Number} end The last record index to include (defaults to length - 1)
21715      * @return {Number} The sum
21716      */
21717     sum : function(property, start, end){
21718         var rs = this.data.items, v = 0;
21719         start = start || 0;
21720         end = (end || end === 0) ? end : rs.length-1;
21721
21722         for(var i = start; i <= end; i++){
21723             v += (rs[i].data[property] || 0);
21724         }
21725         return v;
21726     },
21727
21728     /**
21729      * Filter the records by a specified property.
21730      * @param {String} field A field on your records
21731      * @param {String/RegExp} value Either a string that the field
21732      * should start with or a RegExp to test against the field
21733      * @param {Boolean} anyMatch True to match any part not just the beginning
21734      */
21735     filter : function(property, value, anyMatch){
21736         var fn = this.createFilterFn(property, value, anyMatch);
21737         return fn ? this.filterBy(fn) : this.clearFilter();
21738     },
21739
21740     /**
21741      * Filter by a function. The specified function will be called with each
21742      * record in this data source. If the function returns true the record is included,
21743      * otherwise it is filtered.
21744      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21745      * @param {Object} scope (optional) The scope of the function (defaults to this)
21746      */
21747     filterBy : function(fn, scope){
21748         this.snapshot = this.snapshot || this.data;
21749         this.data = this.queryBy(fn, scope||this);
21750         this.fireEvent("datachanged", this);
21751     },
21752
21753     /**
21754      * Query the records by a specified property.
21755      * @param {String} field A field on your records
21756      * @param {String/RegExp} value Either a string that the field
21757      * should start with or a RegExp to test against the field
21758      * @param {Boolean} anyMatch True to match any part not just the beginning
21759      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21760      */
21761     query : function(property, value, anyMatch){
21762         var fn = this.createFilterFn(property, value, anyMatch);
21763         return fn ? this.queryBy(fn) : this.data.clone();
21764     },
21765
21766     /**
21767      * Query by a function. The specified function will be called with each
21768      * record in this data source. If the function returns true the record is included
21769      * in the results.
21770      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21771      * @param {Object} scope (optional) The scope of the function (defaults to this)
21772       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21773      **/
21774     queryBy : function(fn, scope){
21775         var data = this.snapshot || this.data;
21776         return data.filterBy(fn, scope||this);
21777     },
21778
21779     /**
21780      * Collects unique values for a particular dataIndex from this store.
21781      * @param {String} dataIndex The property to collect
21782      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21783      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21784      * @return {Array} An array of the unique values
21785      **/
21786     collect : function(dataIndex, allowNull, bypassFilter){
21787         var d = (bypassFilter === true && this.snapshot) ?
21788                 this.snapshot.items : this.data.items;
21789         var v, sv, r = [], l = {};
21790         for(var i = 0, len = d.length; i < len; i++){
21791             v = d[i].data[dataIndex];
21792             sv = String(v);
21793             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21794                 l[sv] = true;
21795                 r[r.length] = v;
21796             }
21797         }
21798         return r;
21799     },
21800
21801     /**
21802      * Revert to a view of the Record cache with no filtering applied.
21803      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21804      */
21805     clearFilter : function(suppressEvent){
21806         if(this.snapshot && this.snapshot != this.data){
21807             this.data = this.snapshot;
21808             delete this.snapshot;
21809             if(suppressEvent !== true){
21810                 this.fireEvent("datachanged", this);
21811             }
21812         }
21813     },
21814
21815     // private
21816     afterEdit : function(record){
21817         if(this.modified.indexOf(record) == -1){
21818             this.modified.push(record);
21819         }
21820         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21821     },
21822     
21823     // private
21824     afterReject : function(record){
21825         this.modified.remove(record);
21826         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21827     },
21828
21829     // private
21830     afterCommit : function(record){
21831         this.modified.remove(record);
21832         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21833     },
21834
21835     /**
21836      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21837      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21838      */
21839     commitChanges : function(){
21840         var m = this.modified.slice(0);
21841         this.modified = [];
21842         for(var i = 0, len = m.length; i < len; i++){
21843             m[i].commit();
21844         }
21845     },
21846
21847     /**
21848      * Cancel outstanding changes on all changed records.
21849      */
21850     rejectChanges : function(){
21851         var m = this.modified.slice(0);
21852         this.modified = [];
21853         for(var i = 0, len = m.length; i < len; i++){
21854             m[i].reject();
21855         }
21856     },
21857
21858     onMetaChange : function(meta, rtype, o){
21859         this.recordType = rtype;
21860         this.fields = rtype.prototype.fields;
21861         delete this.snapshot;
21862         this.sortInfo = meta.sortInfo || this.sortInfo;
21863         this.modified = [];
21864         this.fireEvent('metachange', this, this.reader.meta);
21865     },
21866     
21867     moveIndex : function(data, type)
21868     {
21869         var index = this.indexOf(data);
21870         
21871         var newIndex = index + type;
21872         
21873         this.remove(data);
21874         
21875         this.insert(newIndex, data);
21876         
21877     }
21878 });/*
21879  * Based on:
21880  * Ext JS Library 1.1.1
21881  * Copyright(c) 2006-2007, Ext JS, LLC.
21882  *
21883  * Originally Released Under LGPL - original licence link has changed is not relivant.
21884  *
21885  * Fork - LGPL
21886  * <script type="text/javascript">
21887  */
21888
21889 /**
21890  * @class Roo.data.SimpleStore
21891  * @extends Roo.data.Store
21892  * Small helper class to make creating Stores from Array data easier.
21893  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21894  * @cfg {Array} fields An array of field definition objects, or field name strings.
21895  * @cfg {Array} data The multi-dimensional array of data
21896  * @constructor
21897  * @param {Object} config
21898  */
21899 Roo.data.SimpleStore = function(config){
21900     Roo.data.SimpleStore.superclass.constructor.call(this, {
21901         isLocal : true,
21902         reader: new Roo.data.ArrayReader({
21903                 id: config.id
21904             },
21905             Roo.data.Record.create(config.fields)
21906         ),
21907         proxy : new Roo.data.MemoryProxy(config.data)
21908     });
21909     this.load();
21910 };
21911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21912  * Based on:
21913  * Ext JS Library 1.1.1
21914  * Copyright(c) 2006-2007, Ext JS, LLC.
21915  *
21916  * Originally Released Under LGPL - original licence link has changed is not relivant.
21917  *
21918  * Fork - LGPL
21919  * <script type="text/javascript">
21920  */
21921
21922 /**
21923 /**
21924  * @extends Roo.data.Store
21925  * @class Roo.data.JsonStore
21926  * Small helper class to make creating Stores for JSON data easier. <br/>
21927 <pre><code>
21928 var store = new Roo.data.JsonStore({
21929     url: 'get-images.php',
21930     root: 'images',
21931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21932 });
21933 </code></pre>
21934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21935  * JsonReader and HttpProxy (unless inline data is provided).</b>
21936  * @cfg {Array} fields An array of field definition objects, or field name strings.
21937  * @constructor
21938  * @param {Object} config
21939  */
21940 Roo.data.JsonStore = function(c){
21941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21943         reader: new Roo.data.JsonReader(c, c.fields)
21944     }));
21945 };
21946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21947  * Based on:
21948  * Ext JS Library 1.1.1
21949  * Copyright(c) 2006-2007, Ext JS, LLC.
21950  *
21951  * Originally Released Under LGPL - original licence link has changed is not relivant.
21952  *
21953  * Fork - LGPL
21954  * <script type="text/javascript">
21955  */
21956
21957  
21958 Roo.data.Field = function(config){
21959     if(typeof config == "string"){
21960         config = {name: config};
21961     }
21962     Roo.apply(this, config);
21963     
21964     if(!this.type){
21965         this.type = "auto";
21966     }
21967     
21968     var st = Roo.data.SortTypes;
21969     // named sortTypes are supported, here we look them up
21970     if(typeof this.sortType == "string"){
21971         this.sortType = st[this.sortType];
21972     }
21973     
21974     // set default sortType for strings and dates
21975     if(!this.sortType){
21976         switch(this.type){
21977             case "string":
21978                 this.sortType = st.asUCString;
21979                 break;
21980             case "date":
21981                 this.sortType = st.asDate;
21982                 break;
21983             default:
21984                 this.sortType = st.none;
21985         }
21986     }
21987
21988     // define once
21989     var stripRe = /[\$,%]/g;
21990
21991     // prebuilt conversion function for this field, instead of
21992     // switching every time we're reading a value
21993     if(!this.convert){
21994         var cv, dateFormat = this.dateFormat;
21995         switch(this.type){
21996             case "":
21997             case "auto":
21998             case undefined:
21999                 cv = function(v){ return v; };
22000                 break;
22001             case "string":
22002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22003                 break;
22004             case "int":
22005                 cv = function(v){
22006                     return v !== undefined && v !== null && v !== '' ?
22007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22008                     };
22009                 break;
22010             case "float":
22011                 cv = function(v){
22012                     return v !== undefined && v !== null && v !== '' ?
22013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22014                     };
22015                 break;
22016             case "bool":
22017             case "boolean":
22018                 cv = function(v){ return v === true || v === "true" || v == 1; };
22019                 break;
22020             case "date":
22021                 cv = function(v){
22022                     if(!v){
22023                         return '';
22024                     }
22025                     if(v instanceof Date){
22026                         return v;
22027                     }
22028                     if(dateFormat){
22029                         if(dateFormat == "timestamp"){
22030                             return new Date(v*1000);
22031                         }
22032                         return Date.parseDate(v, dateFormat);
22033                     }
22034                     var parsed = Date.parse(v);
22035                     return parsed ? new Date(parsed) : null;
22036                 };
22037              break;
22038             
22039         }
22040         this.convert = cv;
22041     }
22042 };
22043
22044 Roo.data.Field.prototype = {
22045     dateFormat: null,
22046     defaultValue: "",
22047     mapping: null,
22048     sortType : null,
22049     sortDir : "ASC"
22050 };/*
22051  * Based on:
22052  * Ext JS Library 1.1.1
22053  * Copyright(c) 2006-2007, Ext JS, LLC.
22054  *
22055  * Originally Released Under LGPL - original licence link has changed is not relivant.
22056  *
22057  * Fork - LGPL
22058  * <script type="text/javascript">
22059  */
22060  
22061 // Base class for reading structured data from a data source.  This class is intended to be
22062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22063
22064 /**
22065  * @class Roo.data.DataReader
22066  * Base class for reading structured data from a data source.  This class is intended to be
22067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22068  */
22069
22070 Roo.data.DataReader = function(meta, recordType){
22071     
22072     this.meta = meta;
22073     
22074     this.recordType = recordType instanceof Array ? 
22075         Roo.data.Record.create(recordType) : recordType;
22076 };
22077
22078 Roo.data.DataReader.prototype = {
22079      /**
22080      * Create an empty record
22081      * @param {Object} data (optional) - overlay some values
22082      * @return {Roo.data.Record} record created.
22083      */
22084     newRow :  function(d) {
22085         var da =  {};
22086         this.recordType.prototype.fields.each(function(c) {
22087             switch( c.type) {
22088                 case 'int' : da[c.name] = 0; break;
22089                 case 'date' : da[c.name] = new Date(); break;
22090                 case 'float' : da[c.name] = 0.0; break;
22091                 case 'boolean' : da[c.name] = false; break;
22092                 default : da[c.name] = ""; break;
22093             }
22094             
22095         });
22096         return new this.recordType(Roo.apply(da, d));
22097     }
22098     
22099 };/*
22100  * Based on:
22101  * Ext JS Library 1.1.1
22102  * Copyright(c) 2006-2007, Ext JS, LLC.
22103  *
22104  * Originally Released Under LGPL - original licence link has changed is not relivant.
22105  *
22106  * Fork - LGPL
22107  * <script type="text/javascript">
22108  */
22109
22110 /**
22111  * @class Roo.data.DataProxy
22112  * @extends Roo.data.Observable
22113  * This class is an abstract base class for implementations which provide retrieval of
22114  * unformatted data objects.<br>
22115  * <p>
22116  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22117  * (of the appropriate type which knows how to parse the data object) to provide a block of
22118  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22119  * <p>
22120  * Custom implementations must implement the load method as described in
22121  * {@link Roo.data.HttpProxy#load}.
22122  */
22123 Roo.data.DataProxy = function(){
22124     this.addEvents({
22125         /**
22126          * @event beforeload
22127          * Fires before a network request is made to retrieve a data object.
22128          * @param {Object} This DataProxy object.
22129          * @param {Object} params The params parameter to the load function.
22130          */
22131         beforeload : true,
22132         /**
22133          * @event load
22134          * Fires before the load method's callback is called.
22135          * @param {Object} This DataProxy object.
22136          * @param {Object} o The data object.
22137          * @param {Object} arg The callback argument object passed to the load function.
22138          */
22139         load : true,
22140         /**
22141          * @event loadexception
22142          * Fires if an Exception occurs during data retrieval.
22143          * @param {Object} This DataProxy object.
22144          * @param {Object} o The data object.
22145          * @param {Object} arg The callback argument object passed to the load function.
22146          * @param {Object} e The Exception.
22147          */
22148         loadexception : true
22149     });
22150     Roo.data.DataProxy.superclass.constructor.call(this);
22151 };
22152
22153 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22154
22155     /**
22156      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22157      */
22158 /*
22159  * Based on:
22160  * Ext JS Library 1.1.1
22161  * Copyright(c) 2006-2007, Ext JS, LLC.
22162  *
22163  * Originally Released Under LGPL - original licence link has changed is not relivant.
22164  *
22165  * Fork - LGPL
22166  * <script type="text/javascript">
22167  */
22168 /**
22169  * @class Roo.data.MemoryProxy
22170  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22171  * to the Reader when its load method is called.
22172  * @constructor
22173  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22174  */
22175 Roo.data.MemoryProxy = function(data){
22176     if (data.data) {
22177         data = data.data;
22178     }
22179     Roo.data.MemoryProxy.superclass.constructor.call(this);
22180     this.data = data;
22181 };
22182
22183 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22184     /**
22185      * Load data from the requested source (in this case an in-memory
22186      * data object passed to the constructor), read the data object into
22187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22188      * process that block using the passed callback.
22189      * @param {Object} params This parameter is not used by the MemoryProxy class.
22190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22191      * object into a block of Roo.data.Records.
22192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22193      * The function must be passed <ul>
22194      * <li>The Record block object</li>
22195      * <li>The "arg" argument from the load function</li>
22196      * <li>A boolean success indicator</li>
22197      * </ul>
22198      * @param {Object} scope The scope in which to call the callback
22199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22200      */
22201     load : function(params, reader, callback, scope, arg){
22202         params = params || {};
22203         var result;
22204         try {
22205             result = reader.readRecords(this.data);
22206         }catch(e){
22207             this.fireEvent("loadexception", this, arg, null, e);
22208             callback.call(scope, null, arg, false);
22209             return;
22210         }
22211         callback.call(scope, result, arg, true);
22212     },
22213     
22214     // private
22215     update : function(params, records){
22216         
22217     }
22218 });/*
22219  * Based on:
22220  * Ext JS Library 1.1.1
22221  * Copyright(c) 2006-2007, Ext JS, LLC.
22222  *
22223  * Originally Released Under LGPL - original licence link has changed is not relivant.
22224  *
22225  * Fork - LGPL
22226  * <script type="text/javascript">
22227  */
22228 /**
22229  * @class Roo.data.HttpProxy
22230  * @extends Roo.data.DataProxy
22231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22232  * configured to reference a certain URL.<br><br>
22233  * <p>
22234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22235  * from which the running page was served.<br><br>
22236  * <p>
22237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22238  * <p>
22239  * Be aware that to enable the browser to parse an XML document, the server must set
22240  * the Content-Type header in the HTTP response to "text/xml".
22241  * @constructor
22242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22244  * will be used to make the request.
22245  */
22246 Roo.data.HttpProxy = function(conn){
22247     Roo.data.HttpProxy.superclass.constructor.call(this);
22248     // is conn a conn config or a real conn?
22249     this.conn = conn;
22250     this.useAjax = !conn || !conn.events;
22251   
22252 };
22253
22254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22255     // thse are take from connection...
22256     
22257     /**
22258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22259      */
22260     /**
22261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22262      * extra parameters to each request made by this object. (defaults to undefined)
22263      */
22264     /**
22265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22266      *  to each request made by this object. (defaults to undefined)
22267      */
22268     /**
22269      * @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)
22270      */
22271     /**
22272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22273      */
22274      /**
22275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22276      * @type Boolean
22277      */
22278   
22279
22280     /**
22281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22282      * @type Boolean
22283      */
22284     /**
22285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22287      * a finer-grained basis than the DataProxy events.
22288      */
22289     getConnection : function(){
22290         return this.useAjax ? Roo.Ajax : this.conn;
22291     },
22292
22293     /**
22294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22296      * process that block using the passed callback.
22297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22298      * for the request to the remote server.
22299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22300      * object into a block of Roo.data.Records.
22301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22302      * The function must be passed <ul>
22303      * <li>The Record block object</li>
22304      * <li>The "arg" argument from the load function</li>
22305      * <li>A boolean success indicator</li>
22306      * </ul>
22307      * @param {Object} scope The scope in which to call the callback
22308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22309      */
22310     load : function(params, reader, callback, scope, arg){
22311         if(this.fireEvent("beforeload", this, params) !== false){
22312             var  o = {
22313                 params : params || {},
22314                 request: {
22315                     callback : callback,
22316                     scope : scope,
22317                     arg : arg
22318                 },
22319                 reader: reader,
22320                 callback : this.loadResponse,
22321                 scope: this
22322             };
22323             if(this.useAjax){
22324                 Roo.applyIf(o, this.conn);
22325                 if(this.activeRequest){
22326                     Roo.Ajax.abort(this.activeRequest);
22327                 }
22328                 this.activeRequest = Roo.Ajax.request(o);
22329             }else{
22330                 this.conn.request(o);
22331             }
22332         }else{
22333             callback.call(scope||this, null, arg, false);
22334         }
22335     },
22336
22337     // private
22338     loadResponse : function(o, success, response){
22339         delete this.activeRequest;
22340         if(!success){
22341             this.fireEvent("loadexception", this, o, response);
22342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22343             return;
22344         }
22345         var result;
22346         try {
22347             result = o.reader.read(response);
22348         }catch(e){
22349             this.fireEvent("loadexception", this, o, response, e);
22350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22351             return;
22352         }
22353         
22354         this.fireEvent("load", this, o, o.request.arg);
22355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22356     },
22357
22358     // private
22359     update : function(dataSet){
22360
22361     },
22362
22363     // private
22364     updateResponse : function(dataSet){
22365
22366     }
22367 });/*
22368  * Based on:
22369  * Ext JS Library 1.1.1
22370  * Copyright(c) 2006-2007, Ext JS, LLC.
22371  *
22372  * Originally Released Under LGPL - original licence link has changed is not relivant.
22373  *
22374  * Fork - LGPL
22375  * <script type="text/javascript">
22376  */
22377
22378 /**
22379  * @class Roo.data.ScriptTagProxy
22380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22381  * other than the originating domain of the running page.<br><br>
22382  * <p>
22383  * <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
22384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22385  * <p>
22386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22387  * source code that is used as the source inside a &lt;script> tag.<br><br>
22388  * <p>
22389  * In order for the browser to process the returned data, the server must wrap the data object
22390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22392  * depending on whether the callback name was passed:
22393  * <p>
22394  * <pre><code>
22395 boolean scriptTag = false;
22396 String cb = request.getParameter("callback");
22397 if (cb != null) {
22398     scriptTag = true;
22399     response.setContentType("text/javascript");
22400 } else {
22401     response.setContentType("application/x-json");
22402 }
22403 Writer out = response.getWriter();
22404 if (scriptTag) {
22405     out.write(cb + "(");
22406 }
22407 out.print(dataBlock.toJsonString());
22408 if (scriptTag) {
22409     out.write(");");
22410 }
22411 </pre></code>
22412  *
22413  * @constructor
22414  * @param {Object} config A configuration object.
22415  */
22416 Roo.data.ScriptTagProxy = function(config){
22417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22418     Roo.apply(this, config);
22419     this.head = document.getElementsByTagName("head")[0];
22420 };
22421
22422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22423
22424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22425     /**
22426      * @cfg {String} url The URL from which to request the data object.
22427      */
22428     /**
22429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22430      */
22431     timeout : 30000,
22432     /**
22433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22434      * the server the name of the callback function set up by the load call to process the returned data object.
22435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22436      * javascript output which calls this named function passing the data object as its only parameter.
22437      */
22438     callbackParam : "callback",
22439     /**
22440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22441      * name to the request.
22442      */
22443     nocache : true,
22444
22445     /**
22446      * Load data from the configured URL, read the data object into
22447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22448      * process that block using the passed callback.
22449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22450      * for the request to the remote server.
22451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22452      * object into a block of Roo.data.Records.
22453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22454      * The function must be passed <ul>
22455      * <li>The Record block object</li>
22456      * <li>The "arg" argument from the load function</li>
22457      * <li>A boolean success indicator</li>
22458      * </ul>
22459      * @param {Object} scope The scope in which to call the callback
22460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22461      */
22462     load : function(params, reader, callback, scope, arg){
22463         if(this.fireEvent("beforeload", this, params) !== false){
22464
22465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22466
22467             var url = this.url;
22468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22469             if(this.nocache){
22470                 url += "&_dc=" + (new Date().getTime());
22471             }
22472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22473             var trans = {
22474                 id : transId,
22475                 cb : "stcCallback"+transId,
22476                 scriptId : "stcScript"+transId,
22477                 params : params,
22478                 arg : arg,
22479                 url : url,
22480                 callback : callback,
22481                 scope : scope,
22482                 reader : reader
22483             };
22484             var conn = this;
22485
22486             window[trans.cb] = function(o){
22487                 conn.handleResponse(o, trans);
22488             };
22489
22490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22491
22492             if(this.autoAbort !== false){
22493                 this.abort();
22494             }
22495
22496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22497
22498             var script = document.createElement("script");
22499             script.setAttribute("src", url);
22500             script.setAttribute("type", "text/javascript");
22501             script.setAttribute("id", trans.scriptId);
22502             this.head.appendChild(script);
22503
22504             this.trans = trans;
22505         }else{
22506             callback.call(scope||this, null, arg, false);
22507         }
22508     },
22509
22510     // private
22511     isLoading : function(){
22512         return this.trans ? true : false;
22513     },
22514
22515     /**
22516      * Abort the current server request.
22517      */
22518     abort : function(){
22519         if(this.isLoading()){
22520             this.destroyTrans(this.trans);
22521         }
22522     },
22523
22524     // private
22525     destroyTrans : function(trans, isLoaded){
22526         this.head.removeChild(document.getElementById(trans.scriptId));
22527         clearTimeout(trans.timeoutId);
22528         if(isLoaded){
22529             window[trans.cb] = undefined;
22530             try{
22531                 delete window[trans.cb];
22532             }catch(e){}
22533         }else{
22534             // if hasn't been loaded, wait for load to remove it to prevent script error
22535             window[trans.cb] = function(){
22536                 window[trans.cb] = undefined;
22537                 try{
22538                     delete window[trans.cb];
22539                 }catch(e){}
22540             };
22541         }
22542     },
22543
22544     // private
22545     handleResponse : function(o, trans){
22546         this.trans = false;
22547         this.destroyTrans(trans, true);
22548         var result;
22549         try {
22550             result = trans.reader.readRecords(o);
22551         }catch(e){
22552             this.fireEvent("loadexception", this, o, trans.arg, e);
22553             trans.callback.call(trans.scope||window, null, trans.arg, false);
22554             return;
22555         }
22556         this.fireEvent("load", this, o, trans.arg);
22557         trans.callback.call(trans.scope||window, result, trans.arg, true);
22558     },
22559
22560     // private
22561     handleFailure : function(trans){
22562         this.trans = false;
22563         this.destroyTrans(trans, false);
22564         this.fireEvent("loadexception", this, null, trans.arg);
22565         trans.callback.call(trans.scope||window, null, trans.arg, false);
22566     }
22567 });/*
22568  * Based on:
22569  * Ext JS Library 1.1.1
22570  * Copyright(c) 2006-2007, Ext JS, LLC.
22571  *
22572  * Originally Released Under LGPL - original licence link has changed is not relivant.
22573  *
22574  * Fork - LGPL
22575  * <script type="text/javascript">
22576  */
22577
22578 /**
22579  * @class Roo.data.JsonReader
22580  * @extends Roo.data.DataReader
22581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22582  * based on mappings in a provided Roo.data.Record constructor.
22583  * 
22584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22585  * in the reply previously. 
22586  * 
22587  * <p>
22588  * Example code:
22589  * <pre><code>
22590 var RecordDef = Roo.data.Record.create([
22591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22593 ]);
22594 var myReader = new Roo.data.JsonReader({
22595     totalProperty: "results",    // The property which contains the total dataset size (optional)
22596     root: "rows",                // The property which contains an Array of row objects
22597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22598 }, RecordDef);
22599 </code></pre>
22600  * <p>
22601  * This would consume a JSON file like this:
22602  * <pre><code>
22603 { 'results': 2, 'rows': [
22604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22606 }
22607 </code></pre>
22608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22610  * paged from the remote server.
22611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22612  * @cfg {String} root name of the property which contains the Array of row objects.
22613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22614  * @constructor
22615  * Create a new JsonReader
22616  * @param {Object} meta Metadata configuration options
22617  * @param {Object} recordType Either an Array of field definition objects,
22618  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22619  */
22620 Roo.data.JsonReader = function(meta, recordType){
22621     
22622     meta = meta || {};
22623     // set some defaults:
22624     Roo.applyIf(meta, {
22625         totalProperty: 'total',
22626         successProperty : 'success',
22627         root : 'data',
22628         id : 'id'
22629     });
22630     
22631     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22632 };
22633 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22634     
22635     /**
22636      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22637      * Used by Store query builder to append _requestMeta to params.
22638      * 
22639      */
22640     metaFromRemote : false,
22641     /**
22642      * This method is only used by a DataProxy which has retrieved data from a remote server.
22643      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22644      * @return {Object} data A data block which is used by an Roo.data.Store object as
22645      * a cache of Roo.data.Records.
22646      */
22647     read : function(response){
22648         var json = response.responseText;
22649        
22650         var o = /* eval:var:o */ eval("("+json+")");
22651         if(!o) {
22652             throw {message: "JsonReader.read: Json object not found"};
22653         }
22654         
22655         if(o.metaData){
22656             
22657             delete this.ef;
22658             this.metaFromRemote = true;
22659             this.meta = o.metaData;
22660             this.recordType = Roo.data.Record.create(o.metaData.fields);
22661             this.onMetaChange(this.meta, this.recordType, o);
22662         }
22663         return this.readRecords(o);
22664     },
22665
22666     // private function a store will implement
22667     onMetaChange : function(meta, recordType, o){
22668
22669     },
22670
22671     /**
22672          * @ignore
22673          */
22674     simpleAccess: function(obj, subsc) {
22675         return obj[subsc];
22676     },
22677
22678         /**
22679          * @ignore
22680          */
22681     getJsonAccessor: function(){
22682         var re = /[\[\.]/;
22683         return function(expr) {
22684             try {
22685                 return(re.test(expr))
22686                     ? new Function("obj", "return obj." + expr)
22687                     : function(obj){
22688                         return obj[expr];
22689                     };
22690             } catch(e){}
22691             return Roo.emptyFn;
22692         };
22693     }(),
22694
22695     /**
22696      * Create a data block containing Roo.data.Records from an XML document.
22697      * @param {Object} o An object which contains an Array of row objects in the property specified
22698      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22699      * which contains the total size of the dataset.
22700      * @return {Object} data A data block which is used by an Roo.data.Store object as
22701      * a cache of Roo.data.Records.
22702      */
22703     readRecords : function(o){
22704         /**
22705          * After any data loads, the raw JSON data is available for further custom processing.
22706          * @type Object
22707          */
22708         this.o = o;
22709         var s = this.meta, Record = this.recordType,
22710             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22711
22712 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22713         if (!this.ef) {
22714             if(s.totalProperty) {
22715                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22716                 }
22717                 if(s.successProperty) {
22718                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22719                 }
22720                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22721                 if (s.id) {
22722                         var g = this.getJsonAccessor(s.id);
22723                         this.getId = function(rec) {
22724                                 var r = g(rec);  
22725                                 return (r === undefined || r === "") ? null : r;
22726                         };
22727                 } else {
22728                         this.getId = function(){return null;};
22729                 }
22730             this.ef = [];
22731             for(var jj = 0; jj < fl; jj++){
22732                 f = fi[jj];
22733                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22734                 this.ef[jj] = this.getJsonAccessor(map);
22735             }
22736         }
22737
22738         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22739         if(s.totalProperty){
22740             var vt = parseInt(this.getTotal(o), 10);
22741             if(!isNaN(vt)){
22742                 totalRecords = vt;
22743             }
22744         }
22745         if(s.successProperty){
22746             var vs = this.getSuccess(o);
22747             if(vs === false || vs === 'false'){
22748                 success = false;
22749             }
22750         }
22751         var records = [];
22752         for(var i = 0; i < c; i++){
22753                 var n = root[i];
22754             var values = {};
22755             var id = this.getId(n);
22756             for(var j = 0; j < fl; j++){
22757                 f = fi[j];
22758             var v = this.ef[j](n);
22759             if (!f.convert) {
22760                 Roo.log('missing convert for ' + f.name);
22761                 Roo.log(f);
22762                 continue;
22763             }
22764             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22765             }
22766             var record = new Record(values, id);
22767             record.json = n;
22768             records[i] = record;
22769         }
22770         return {
22771             raw : o,
22772             success : success,
22773             records : records,
22774             totalRecords : totalRecords
22775         };
22776     }
22777 });/*
22778  * Based on:
22779  * Ext JS Library 1.1.1
22780  * Copyright(c) 2006-2007, Ext JS, LLC.
22781  *
22782  * Originally Released Under LGPL - original licence link has changed is not relivant.
22783  *
22784  * Fork - LGPL
22785  * <script type="text/javascript">
22786  */
22787
22788 /**
22789  * @class Roo.data.XmlReader
22790  * @extends Roo.data.DataReader
22791  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22792  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22793  * <p>
22794  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22795  * header in the HTTP response must be set to "text/xml".</em>
22796  * <p>
22797  * Example code:
22798  * <pre><code>
22799 var RecordDef = Roo.data.Record.create([
22800    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22801    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22802 ]);
22803 var myReader = new Roo.data.XmlReader({
22804    totalRecords: "results", // The element which contains the total dataset size (optional)
22805    record: "row",           // The repeated element which contains row information
22806    id: "id"                 // The element within the row that provides an ID for the record (optional)
22807 }, RecordDef);
22808 </code></pre>
22809  * <p>
22810  * This would consume an XML file like this:
22811  * <pre><code>
22812 &lt;?xml?>
22813 &lt;dataset>
22814  &lt;results>2&lt;/results>
22815  &lt;row>
22816    &lt;id>1&lt;/id>
22817    &lt;name>Bill&lt;/name>
22818    &lt;occupation>Gardener&lt;/occupation>
22819  &lt;/row>
22820  &lt;row>
22821    &lt;id>2&lt;/id>
22822    &lt;name>Ben&lt;/name>
22823    &lt;occupation>Horticulturalist&lt;/occupation>
22824  &lt;/row>
22825 &lt;/dataset>
22826 </code></pre>
22827  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22828  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22829  * paged from the remote server.
22830  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22831  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22832  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22833  * a record identifier value.
22834  * @constructor
22835  * Create a new XmlReader
22836  * @param {Object} meta Metadata configuration options
22837  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22838  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22839  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22840  */
22841 Roo.data.XmlReader = function(meta, recordType){
22842     meta = meta || {};
22843     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22844 };
22845 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22846     /**
22847      * This method is only used by a DataProxy which has retrieved data from a remote server.
22848          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22849          * to contain a method called 'responseXML' that returns an XML document object.
22850      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22851      * a cache of Roo.data.Records.
22852      */
22853     read : function(response){
22854         var doc = response.responseXML;
22855         if(!doc) {
22856             throw {message: "XmlReader.read: XML Document not available"};
22857         }
22858         return this.readRecords(doc);
22859     },
22860
22861     /**
22862      * Create a data block containing Roo.data.Records from an XML document.
22863          * @param {Object} doc A parsed XML document.
22864      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22865      * a cache of Roo.data.Records.
22866      */
22867     readRecords : function(doc){
22868         /**
22869          * After any data loads/reads, the raw XML Document is available for further custom processing.
22870          * @type XMLDocument
22871          */
22872         this.xmlData = doc;
22873         var root = doc.documentElement || doc;
22874         var q = Roo.DomQuery;
22875         var recordType = this.recordType, fields = recordType.prototype.fields;
22876         var sid = this.meta.id;
22877         var totalRecords = 0, success = true;
22878         if(this.meta.totalRecords){
22879             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22880         }
22881         
22882         if(this.meta.success){
22883             var sv = q.selectValue(this.meta.success, root, true);
22884             success = sv !== false && sv !== 'false';
22885         }
22886         var records = [];
22887         var ns = q.select(this.meta.record, root);
22888         for(var i = 0, len = ns.length; i < len; i++) {
22889                 var n = ns[i];
22890                 var values = {};
22891                 var id = sid ? q.selectValue(sid, n) : undefined;
22892                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22893                     var f = fields.items[j];
22894                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22895                     v = f.convert(v);
22896                     values[f.name] = v;
22897                 }
22898                 var record = new recordType(values, id);
22899                 record.node = n;
22900                 records[records.length] = record;
22901             }
22902
22903             return {
22904                 success : success,
22905                 records : records,
22906                 totalRecords : totalRecords || records.length
22907             };
22908     }
22909 });/*
22910  * Based on:
22911  * Ext JS Library 1.1.1
22912  * Copyright(c) 2006-2007, Ext JS, LLC.
22913  *
22914  * Originally Released Under LGPL - original licence link has changed is not relivant.
22915  *
22916  * Fork - LGPL
22917  * <script type="text/javascript">
22918  */
22919
22920 /**
22921  * @class Roo.data.ArrayReader
22922  * @extends Roo.data.DataReader
22923  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22924  * Each element of that Array represents a row of data fields. The
22925  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22926  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22927  * <p>
22928  * Example code:.
22929  * <pre><code>
22930 var RecordDef = Roo.data.Record.create([
22931     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22932     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22933 ]);
22934 var myReader = new Roo.data.ArrayReader({
22935     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22936 }, RecordDef);
22937 </code></pre>
22938  * <p>
22939  * This would consume an Array like this:
22940  * <pre><code>
22941 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22942   </code></pre>
22943  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22944  * @constructor
22945  * Create a new JsonReader
22946  * @param {Object} meta Metadata configuration options.
22947  * @param {Object} recordType Either an Array of field definition objects
22948  * as specified to {@link Roo.data.Record#create},
22949  * or an {@link Roo.data.Record} object
22950  * created using {@link Roo.data.Record#create}.
22951  */
22952 Roo.data.ArrayReader = function(meta, recordType){
22953     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22954 };
22955
22956 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22957     /**
22958      * Create a data block containing Roo.data.Records from an XML document.
22959      * @param {Object} o An Array of row objects which represents the dataset.
22960      * @return {Object} data A data block which is used by an Roo.data.Store object as
22961      * a cache of Roo.data.Records.
22962      */
22963     readRecords : function(o){
22964         var sid = this.meta ? this.meta.id : null;
22965         var recordType = this.recordType, fields = recordType.prototype.fields;
22966         var records = [];
22967         var root = o;
22968             for(var i = 0; i < root.length; i++){
22969                     var n = root[i];
22970                 var values = {};
22971                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22972                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22973                 var f = fields.items[j];
22974                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22975                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22976                 v = f.convert(v);
22977                 values[f.name] = v;
22978             }
22979                 var record = new recordType(values, id);
22980                 record.json = n;
22981                 records[records.length] = record;
22982             }
22983             return {
22984                 records : records,
22985                 totalRecords : records.length
22986             };
22987     }
22988 });/*
22989  * Based on:
22990  * Ext JS Library 1.1.1
22991  * Copyright(c) 2006-2007, Ext JS, LLC.
22992  *
22993  * Originally Released Under LGPL - original licence link has changed is not relivant.
22994  *
22995  * Fork - LGPL
22996  * <script type="text/javascript">
22997  */
22998
22999
23000 /**
23001  * @class Roo.data.Tree
23002  * @extends Roo.util.Observable
23003  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23004  * in the tree have most standard DOM functionality.
23005  * @constructor
23006  * @param {Node} root (optional) The root node
23007  */
23008 Roo.data.Tree = function(root){
23009    this.nodeHash = {};
23010    /**
23011     * The root node for this tree
23012     * @type Node
23013     */
23014    this.root = null;
23015    if(root){
23016        this.setRootNode(root);
23017    }
23018    this.addEvents({
23019        /**
23020         * @event append
23021         * Fires when a new child node is appended to a node in this tree.
23022         * @param {Tree} tree The owner tree
23023         * @param {Node} parent The parent node
23024         * @param {Node} node The newly appended node
23025         * @param {Number} index The index of the newly appended node
23026         */
23027        "append" : true,
23028        /**
23029         * @event remove
23030         * Fires when a child node is removed from a node in this tree.
23031         * @param {Tree} tree The owner tree
23032         * @param {Node} parent The parent node
23033         * @param {Node} node The child node removed
23034         */
23035        "remove" : true,
23036        /**
23037         * @event move
23038         * Fires when a node is moved to a new location in the tree
23039         * @param {Tree} tree The owner tree
23040         * @param {Node} node The node moved
23041         * @param {Node} oldParent The old parent of this node
23042         * @param {Node} newParent The new parent of this node
23043         * @param {Number} index The index it was moved to
23044         */
23045        "move" : true,
23046        /**
23047         * @event insert
23048         * Fires when a new child node is inserted in a node in this tree.
23049         * @param {Tree} tree The owner tree
23050         * @param {Node} parent The parent node
23051         * @param {Node} node The child node inserted
23052         * @param {Node} refNode The child node the node was inserted before
23053         */
23054        "insert" : true,
23055        /**
23056         * @event beforeappend
23057         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23058         * @param {Tree} tree The owner tree
23059         * @param {Node} parent The parent node
23060         * @param {Node} node The child node to be appended
23061         */
23062        "beforeappend" : true,
23063        /**
23064         * @event beforeremove
23065         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node to be removed
23069         */
23070        "beforeremove" : true,
23071        /**
23072         * @event beforemove
23073         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} node The node being moved
23076         * @param {Node} oldParent The parent of the node
23077         * @param {Node} newParent The new parent the node is moving to
23078         * @param {Number} index The index it is being moved to
23079         */
23080        "beforemove" : true,
23081        /**
23082         * @event beforeinsert
23083         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23084         * @param {Tree} tree The owner tree
23085         * @param {Node} parent The parent node
23086         * @param {Node} node The child node to be inserted
23087         * @param {Node} refNode The child node the node is being inserted before
23088         */
23089        "beforeinsert" : true
23090    });
23091
23092     Roo.data.Tree.superclass.constructor.call(this);
23093 };
23094
23095 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23096     pathSeparator: "/",
23097
23098     proxyNodeEvent : function(){
23099         return this.fireEvent.apply(this, arguments);
23100     },
23101
23102     /**
23103      * Returns the root node for this tree.
23104      * @return {Node}
23105      */
23106     getRootNode : function(){
23107         return this.root;
23108     },
23109
23110     /**
23111      * Sets the root node for this tree.
23112      * @param {Node} node
23113      * @return {Node}
23114      */
23115     setRootNode : function(node){
23116         this.root = node;
23117         node.ownerTree = this;
23118         node.isRoot = true;
23119         this.registerNode(node);
23120         return node;
23121     },
23122
23123     /**
23124      * Gets a node in this tree by its id.
23125      * @param {String} id
23126      * @return {Node}
23127      */
23128     getNodeById : function(id){
23129         return this.nodeHash[id];
23130     },
23131
23132     registerNode : function(node){
23133         this.nodeHash[node.id] = node;
23134     },
23135
23136     unregisterNode : function(node){
23137         delete this.nodeHash[node.id];
23138     },
23139
23140     toString : function(){
23141         return "[Tree"+(this.id?" "+this.id:"")+"]";
23142     }
23143 });
23144
23145 /**
23146  * @class Roo.data.Node
23147  * @extends Roo.util.Observable
23148  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23149  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23150  * @constructor
23151  * @param {Object} attributes The attributes/config for the node
23152  */
23153 Roo.data.Node = function(attributes){
23154     /**
23155      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23156      * @type {Object}
23157      */
23158     this.attributes = attributes || {};
23159     this.leaf = this.attributes.leaf;
23160     /**
23161      * The node id. @type String
23162      */
23163     this.id = this.attributes.id;
23164     if(!this.id){
23165         this.id = Roo.id(null, "ynode-");
23166         this.attributes.id = this.id;
23167     }
23168      
23169     
23170     /**
23171      * All child nodes of this node. @type Array
23172      */
23173     this.childNodes = [];
23174     if(!this.childNodes.indexOf){ // indexOf is a must
23175         this.childNodes.indexOf = function(o){
23176             for(var i = 0, len = this.length; i < len; i++){
23177                 if(this[i] == o) {
23178                     return i;
23179                 }
23180             }
23181             return -1;
23182         };
23183     }
23184     /**
23185      * The parent node for this node. @type Node
23186      */
23187     this.parentNode = null;
23188     /**
23189      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23190      */
23191     this.firstChild = null;
23192     /**
23193      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23194      */
23195     this.lastChild = null;
23196     /**
23197      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23198      */
23199     this.previousSibling = null;
23200     /**
23201      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23202      */
23203     this.nextSibling = null;
23204
23205     this.addEvents({
23206        /**
23207         * @event append
23208         * Fires when a new child node is appended
23209         * @param {Tree} tree The owner tree
23210         * @param {Node} this This node
23211         * @param {Node} node The newly appended node
23212         * @param {Number} index The index of the newly appended node
23213         */
23214        "append" : true,
23215        /**
23216         * @event remove
23217         * Fires when a child node is removed
23218         * @param {Tree} tree The owner tree
23219         * @param {Node} this This node
23220         * @param {Node} node The removed node
23221         */
23222        "remove" : true,
23223        /**
23224         * @event move
23225         * Fires when this node is moved to a new location in the tree
23226         * @param {Tree} tree The owner tree
23227         * @param {Node} this This node
23228         * @param {Node} oldParent The old parent of this node
23229         * @param {Node} newParent The new parent of this node
23230         * @param {Number} index The index it was moved to
23231         */
23232        "move" : true,
23233        /**
23234         * @event insert
23235         * Fires when a new child node is inserted.
23236         * @param {Tree} tree The owner tree
23237         * @param {Node} this This node
23238         * @param {Node} node The child node inserted
23239         * @param {Node} refNode The child node the node was inserted before
23240         */
23241        "insert" : true,
23242        /**
23243         * @event beforeappend
23244         * Fires before a new child is appended, return false to cancel the append.
23245         * @param {Tree} tree The owner tree
23246         * @param {Node} this This node
23247         * @param {Node} node The child node to be appended
23248         */
23249        "beforeappend" : true,
23250        /**
23251         * @event beforeremove
23252         * Fires before a child is removed, return false to cancel the remove.
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The child node to be removed
23256         */
23257        "beforeremove" : true,
23258        /**
23259         * @event beforemove
23260         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} oldParent The parent of this node
23264         * @param {Node} newParent The new parent this node is moving to
23265         * @param {Number} index The index it is being moved to
23266         */
23267        "beforemove" : true,
23268        /**
23269         * @event beforeinsert
23270         * Fires before a new child is inserted, return false to cancel the insert.
23271         * @param {Tree} tree The owner tree
23272         * @param {Node} this This node
23273         * @param {Node} node The child node to be inserted
23274         * @param {Node} refNode The child node the node is being inserted before
23275         */
23276        "beforeinsert" : true
23277    });
23278     this.listeners = this.attributes.listeners;
23279     Roo.data.Node.superclass.constructor.call(this);
23280 };
23281
23282 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23283     fireEvent : function(evtName){
23284         // first do standard event for this node
23285         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23286             return false;
23287         }
23288         // then bubble it up to the tree if the event wasn't cancelled
23289         var ot = this.getOwnerTree();
23290         if(ot){
23291             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23292                 return false;
23293             }
23294         }
23295         return true;
23296     },
23297
23298     /**
23299      * Returns true if this node is a leaf
23300      * @return {Boolean}
23301      */
23302     isLeaf : function(){
23303         return this.leaf === true;
23304     },
23305
23306     // private
23307     setFirstChild : function(node){
23308         this.firstChild = node;
23309     },
23310
23311     //private
23312     setLastChild : function(node){
23313         this.lastChild = node;
23314     },
23315
23316
23317     /**
23318      * Returns true if this node is the last child of its parent
23319      * @return {Boolean}
23320      */
23321     isLast : function(){
23322        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23323     },
23324
23325     /**
23326      * Returns true if this node is the first child of its parent
23327      * @return {Boolean}
23328      */
23329     isFirst : function(){
23330        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23331     },
23332
23333     hasChildNodes : function(){
23334         return !this.isLeaf() && this.childNodes.length > 0;
23335     },
23336
23337     /**
23338      * Insert node(s) as the last child node of this node.
23339      * @param {Node/Array} node The node or Array of nodes to append
23340      * @return {Node} The appended node if single append, or null if an array was passed
23341      */
23342     appendChild : function(node){
23343         var multi = false;
23344         if(node instanceof Array){
23345             multi = node;
23346         }else if(arguments.length > 1){
23347             multi = arguments;
23348         }
23349         // if passed an array or multiple args do them one by one
23350         if(multi){
23351             for(var i = 0, len = multi.length; i < len; i++) {
23352                 this.appendChild(multi[i]);
23353             }
23354         }else{
23355             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23356                 return false;
23357             }
23358             var index = this.childNodes.length;
23359             var oldParent = node.parentNode;
23360             // it's a move, make sure we move it cleanly
23361             if(oldParent){
23362                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23363                     return false;
23364                 }
23365                 oldParent.removeChild(node);
23366             }
23367             index = this.childNodes.length;
23368             if(index == 0){
23369                 this.setFirstChild(node);
23370             }
23371             this.childNodes.push(node);
23372             node.parentNode = this;
23373             var ps = this.childNodes[index-1];
23374             if(ps){
23375                 node.previousSibling = ps;
23376                 ps.nextSibling = node;
23377             }else{
23378                 node.previousSibling = null;
23379             }
23380             node.nextSibling = null;
23381             this.setLastChild(node);
23382             node.setOwnerTree(this.getOwnerTree());
23383             this.fireEvent("append", this.ownerTree, this, node, index);
23384             if(oldParent){
23385                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23386             }
23387             return node;
23388         }
23389     },
23390
23391     /**
23392      * Removes a child node from this node.
23393      * @param {Node} node The node to remove
23394      * @return {Node} The removed node
23395      */
23396     removeChild : function(node){
23397         var index = this.childNodes.indexOf(node);
23398         if(index == -1){
23399             return false;
23400         }
23401         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23402             return false;
23403         }
23404
23405         // remove it from childNodes collection
23406         this.childNodes.splice(index, 1);
23407
23408         // update siblings
23409         if(node.previousSibling){
23410             node.previousSibling.nextSibling = node.nextSibling;
23411         }
23412         if(node.nextSibling){
23413             node.nextSibling.previousSibling = node.previousSibling;
23414         }
23415
23416         // update child refs
23417         if(this.firstChild == node){
23418             this.setFirstChild(node.nextSibling);
23419         }
23420         if(this.lastChild == node){
23421             this.setLastChild(node.previousSibling);
23422         }
23423
23424         node.setOwnerTree(null);
23425         // clear any references from the node
23426         node.parentNode = null;
23427         node.previousSibling = null;
23428         node.nextSibling = null;
23429         this.fireEvent("remove", this.ownerTree, this, node);
23430         return node;
23431     },
23432
23433     /**
23434      * Inserts the first node before the second node in this nodes childNodes collection.
23435      * @param {Node} node The node to insert
23436      * @param {Node} refNode The node to insert before (if null the node is appended)
23437      * @return {Node} The inserted node
23438      */
23439     insertBefore : function(node, refNode){
23440         if(!refNode){ // like standard Dom, refNode can be null for append
23441             return this.appendChild(node);
23442         }
23443         // nothing to do
23444         if(node == refNode){
23445             return false;
23446         }
23447
23448         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23449             return false;
23450         }
23451         var index = this.childNodes.indexOf(refNode);
23452         var oldParent = node.parentNode;
23453         var refIndex = index;
23454
23455         // when moving internally, indexes will change after remove
23456         if(oldParent == this && this.childNodes.indexOf(node) < index){
23457             refIndex--;
23458         }
23459
23460         // it's a move, make sure we move it cleanly
23461         if(oldParent){
23462             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23463                 return false;
23464             }
23465             oldParent.removeChild(node);
23466         }
23467         if(refIndex == 0){
23468             this.setFirstChild(node);
23469         }
23470         this.childNodes.splice(refIndex, 0, node);
23471         node.parentNode = this;
23472         var ps = this.childNodes[refIndex-1];
23473         if(ps){
23474             node.previousSibling = ps;
23475             ps.nextSibling = node;
23476         }else{
23477             node.previousSibling = null;
23478         }
23479         node.nextSibling = refNode;
23480         refNode.previousSibling = node;
23481         node.setOwnerTree(this.getOwnerTree());
23482         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23483         if(oldParent){
23484             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23485         }
23486         return node;
23487     },
23488
23489     /**
23490      * Returns the child node at the specified index.
23491      * @param {Number} index
23492      * @return {Node}
23493      */
23494     item : function(index){
23495         return this.childNodes[index];
23496     },
23497
23498     /**
23499      * Replaces one child node in this node with another.
23500      * @param {Node} newChild The replacement node
23501      * @param {Node} oldChild The node to replace
23502      * @return {Node} The replaced node
23503      */
23504     replaceChild : function(newChild, oldChild){
23505         this.insertBefore(newChild, oldChild);
23506         this.removeChild(oldChild);
23507         return oldChild;
23508     },
23509
23510     /**
23511      * Returns the index of a child node
23512      * @param {Node} node
23513      * @return {Number} The index of the node or -1 if it was not found
23514      */
23515     indexOf : function(child){
23516         return this.childNodes.indexOf(child);
23517     },
23518
23519     /**
23520      * Returns the tree this node is in.
23521      * @return {Tree}
23522      */
23523     getOwnerTree : function(){
23524         // if it doesn't have one, look for one
23525         if(!this.ownerTree){
23526             var p = this;
23527             while(p){
23528                 if(p.ownerTree){
23529                     this.ownerTree = p.ownerTree;
23530                     break;
23531                 }
23532                 p = p.parentNode;
23533             }
23534         }
23535         return this.ownerTree;
23536     },
23537
23538     /**
23539      * Returns depth of this node (the root node has a depth of 0)
23540      * @return {Number}
23541      */
23542     getDepth : function(){
23543         var depth = 0;
23544         var p = this;
23545         while(p.parentNode){
23546             ++depth;
23547             p = p.parentNode;
23548         }
23549         return depth;
23550     },
23551
23552     // private
23553     setOwnerTree : function(tree){
23554         // if it's move, we need to update everyone
23555         if(tree != this.ownerTree){
23556             if(this.ownerTree){
23557                 this.ownerTree.unregisterNode(this);
23558             }
23559             this.ownerTree = tree;
23560             var cs = this.childNodes;
23561             for(var i = 0, len = cs.length; i < len; i++) {
23562                 cs[i].setOwnerTree(tree);
23563             }
23564             if(tree){
23565                 tree.registerNode(this);
23566             }
23567         }
23568     },
23569
23570     /**
23571      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23572      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23573      * @return {String} The path
23574      */
23575     getPath : function(attr){
23576         attr = attr || "id";
23577         var p = this.parentNode;
23578         var b = [this.attributes[attr]];
23579         while(p){
23580             b.unshift(p.attributes[attr]);
23581             p = p.parentNode;
23582         }
23583         var sep = this.getOwnerTree().pathSeparator;
23584         return sep + b.join(sep);
23585     },
23586
23587     /**
23588      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23589      * function call will be the scope provided or the current node. The arguments to the function
23590      * will be the args provided or the current node. If the function returns false at any point,
23591      * the bubble is stopped.
23592      * @param {Function} fn The function to call
23593      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23594      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23595      */
23596     bubble : function(fn, scope, args){
23597         var p = this;
23598         while(p){
23599             if(fn.call(scope || p, args || p) === false){
23600                 break;
23601             }
23602             p = p.parentNode;
23603         }
23604     },
23605
23606     /**
23607      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23608      * function call will be the scope provided or the current node. The arguments to the function
23609      * will be the args provided or the current node. If the function returns false at any point,
23610      * the cascade is stopped on that branch.
23611      * @param {Function} fn The function to call
23612      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23613      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23614      */
23615     cascade : function(fn, scope, args){
23616         if(fn.call(scope || this, args || this) !== false){
23617             var cs = this.childNodes;
23618             for(var i = 0, len = cs.length; i < len; i++) {
23619                 cs[i].cascade(fn, scope, args);
23620             }
23621         }
23622     },
23623
23624     /**
23625      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23626      * function call will be the scope provided or the current node. The arguments to the function
23627      * will be the args provided or the current node. If the function returns false at any point,
23628      * the iteration stops.
23629      * @param {Function} fn The function to call
23630      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23631      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23632      */
23633     eachChild : function(fn, scope, args){
23634         var cs = this.childNodes;
23635         for(var i = 0, len = cs.length; i < len; i++) {
23636                 if(fn.call(scope || this, args || cs[i]) === false){
23637                     break;
23638                 }
23639         }
23640     },
23641
23642     /**
23643      * Finds the first child that has the attribute with the specified value.
23644      * @param {String} attribute The attribute name
23645      * @param {Mixed} value The value to search for
23646      * @return {Node} The found child or null if none was found
23647      */
23648     findChild : function(attribute, value){
23649         var cs = this.childNodes;
23650         for(var i = 0, len = cs.length; i < len; i++) {
23651                 if(cs[i].attributes[attribute] == value){
23652                     return cs[i];
23653                 }
23654         }
23655         return null;
23656     },
23657
23658     /**
23659      * Finds the first child by a custom function. The child matches if the function passed
23660      * returns true.
23661      * @param {Function} fn
23662      * @param {Object} scope (optional)
23663      * @return {Node} The found child or null if none was found
23664      */
23665     findChildBy : function(fn, scope){
23666         var cs = this.childNodes;
23667         for(var i = 0, len = cs.length; i < len; i++) {
23668                 if(fn.call(scope||cs[i], cs[i]) === true){
23669                     return cs[i];
23670                 }
23671         }
23672         return null;
23673     },
23674
23675     /**
23676      * Sorts this nodes children using the supplied sort function
23677      * @param {Function} fn
23678      * @param {Object} scope (optional)
23679      */
23680     sort : function(fn, scope){
23681         var cs = this.childNodes;
23682         var len = cs.length;
23683         if(len > 0){
23684             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23685             cs.sort(sortFn);
23686             for(var i = 0; i < len; i++){
23687                 var n = cs[i];
23688                 n.previousSibling = cs[i-1];
23689                 n.nextSibling = cs[i+1];
23690                 if(i == 0){
23691                     this.setFirstChild(n);
23692                 }
23693                 if(i == len-1){
23694                     this.setLastChild(n);
23695                 }
23696             }
23697         }
23698     },
23699
23700     /**
23701      * Returns true if this node is an ancestor (at any point) of the passed node.
23702      * @param {Node} node
23703      * @return {Boolean}
23704      */
23705     contains : function(node){
23706         return node.isAncestor(this);
23707     },
23708
23709     /**
23710      * Returns true if the passed node is an ancestor (at any point) of this node.
23711      * @param {Node} node
23712      * @return {Boolean}
23713      */
23714     isAncestor : function(node){
23715         var p = this.parentNode;
23716         while(p){
23717             if(p == node){
23718                 return true;
23719             }
23720             p = p.parentNode;
23721         }
23722         return false;
23723     },
23724
23725     toString : function(){
23726         return "[Node"+(this.id?" "+this.id:"")+"]";
23727     }
23728 });/*
23729  * Based on:
23730  * Ext JS Library 1.1.1
23731  * Copyright(c) 2006-2007, Ext JS, LLC.
23732  *
23733  * Originally Released Under LGPL - original licence link has changed is not relivant.
23734  *
23735  * Fork - LGPL
23736  * <script type="text/javascript">
23737  */
23738  (function(){ 
23739 /**
23740  * @class Roo.Layer
23741  * @extends Roo.Element
23742  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23743  * automatic maintaining of shadow/shim positions.
23744  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23745  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23746  * you can pass a string with a CSS class name. False turns off the shadow.
23747  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23748  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23749  * @cfg {String} cls CSS class to add to the element
23750  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23751  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23752  * @constructor
23753  * @param {Object} config An object with config options.
23754  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23755  */
23756
23757 Roo.Layer = function(config, existingEl){
23758     config = config || {};
23759     var dh = Roo.DomHelper;
23760     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23761     if(existingEl){
23762         this.dom = Roo.getDom(existingEl);
23763     }
23764     if(!this.dom){
23765         var o = config.dh || {tag: "div", cls: "x-layer"};
23766         this.dom = dh.append(pel, o);
23767     }
23768     if(config.cls){
23769         this.addClass(config.cls);
23770     }
23771     this.constrain = config.constrain !== false;
23772     this.visibilityMode = Roo.Element.VISIBILITY;
23773     if(config.id){
23774         this.id = this.dom.id = config.id;
23775     }else{
23776         this.id = Roo.id(this.dom);
23777     }
23778     this.zindex = config.zindex || this.getZIndex();
23779     this.position("absolute", this.zindex);
23780     if(config.shadow){
23781         this.shadowOffset = config.shadowOffset || 4;
23782         this.shadow = new Roo.Shadow({
23783             offset : this.shadowOffset,
23784             mode : config.shadow
23785         });
23786     }else{
23787         this.shadowOffset = 0;
23788     }
23789     this.useShim = config.shim !== false && Roo.useShims;
23790     this.useDisplay = config.useDisplay;
23791     this.hide();
23792 };
23793
23794 var supr = Roo.Element.prototype;
23795
23796 // shims are shared among layer to keep from having 100 iframes
23797 var shims = [];
23798
23799 Roo.extend(Roo.Layer, Roo.Element, {
23800
23801     getZIndex : function(){
23802         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23803     },
23804
23805     getShim : function(){
23806         if(!this.useShim){
23807             return null;
23808         }
23809         if(this.shim){
23810             return this.shim;
23811         }
23812         var shim = shims.shift();
23813         if(!shim){
23814             shim = this.createShim();
23815             shim.enableDisplayMode('block');
23816             shim.dom.style.display = 'none';
23817             shim.dom.style.visibility = 'visible';
23818         }
23819         var pn = this.dom.parentNode;
23820         if(shim.dom.parentNode != pn){
23821             pn.insertBefore(shim.dom, this.dom);
23822         }
23823         shim.setStyle('z-index', this.getZIndex()-2);
23824         this.shim = shim;
23825         return shim;
23826     },
23827
23828     hideShim : function(){
23829         if(this.shim){
23830             this.shim.setDisplayed(false);
23831             shims.push(this.shim);
23832             delete this.shim;
23833         }
23834     },
23835
23836     disableShadow : function(){
23837         if(this.shadow){
23838             this.shadowDisabled = true;
23839             this.shadow.hide();
23840             this.lastShadowOffset = this.shadowOffset;
23841             this.shadowOffset = 0;
23842         }
23843     },
23844
23845     enableShadow : function(show){
23846         if(this.shadow){
23847             this.shadowDisabled = false;
23848             this.shadowOffset = this.lastShadowOffset;
23849             delete this.lastShadowOffset;
23850             if(show){
23851                 this.sync(true);
23852             }
23853         }
23854     },
23855
23856     // private
23857     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23858     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23859     sync : function(doShow){
23860         var sw = this.shadow;
23861         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23862             var sh = this.getShim();
23863
23864             var w = this.getWidth(),
23865                 h = this.getHeight();
23866
23867             var l = this.getLeft(true),
23868                 t = this.getTop(true);
23869
23870             if(sw && !this.shadowDisabled){
23871                 if(doShow && !sw.isVisible()){
23872                     sw.show(this);
23873                 }else{
23874                     sw.realign(l, t, w, h);
23875                 }
23876                 if(sh){
23877                     if(doShow){
23878                        sh.show();
23879                     }
23880                     // fit the shim behind the shadow, so it is shimmed too
23881                     var a = sw.adjusts, s = sh.dom.style;
23882                     s.left = (Math.min(l, l+a.l))+"px";
23883                     s.top = (Math.min(t, t+a.t))+"px";
23884                     s.width = (w+a.w)+"px";
23885                     s.height = (h+a.h)+"px";
23886                 }
23887             }else if(sh){
23888                 if(doShow){
23889                    sh.show();
23890                 }
23891                 sh.setSize(w, h);
23892                 sh.setLeftTop(l, t);
23893             }
23894             
23895         }
23896     },
23897
23898     // private
23899     destroy : function(){
23900         this.hideShim();
23901         if(this.shadow){
23902             this.shadow.hide();
23903         }
23904         this.removeAllListeners();
23905         var pn = this.dom.parentNode;
23906         if(pn){
23907             pn.removeChild(this.dom);
23908         }
23909         Roo.Element.uncache(this.id);
23910     },
23911
23912     remove : function(){
23913         this.destroy();
23914     },
23915
23916     // private
23917     beginUpdate : function(){
23918         this.updating = true;
23919     },
23920
23921     // private
23922     endUpdate : function(){
23923         this.updating = false;
23924         this.sync(true);
23925     },
23926
23927     // private
23928     hideUnders : function(negOffset){
23929         if(this.shadow){
23930             this.shadow.hide();
23931         }
23932         this.hideShim();
23933     },
23934
23935     // private
23936     constrainXY : function(){
23937         if(this.constrain){
23938             var vw = Roo.lib.Dom.getViewWidth(),
23939                 vh = Roo.lib.Dom.getViewHeight();
23940             var s = Roo.get(document).getScroll();
23941
23942             var xy = this.getXY();
23943             var x = xy[0], y = xy[1];   
23944             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23945             // only move it if it needs it
23946             var moved = false;
23947             // first validate right/bottom
23948             if((x + w) > vw+s.left){
23949                 x = vw - w - this.shadowOffset;
23950                 moved = true;
23951             }
23952             if((y + h) > vh+s.top){
23953                 y = vh - h - this.shadowOffset;
23954                 moved = true;
23955             }
23956             // then make sure top/left isn't negative
23957             if(x < s.left){
23958                 x = s.left;
23959                 moved = true;
23960             }
23961             if(y < s.top){
23962                 y = s.top;
23963                 moved = true;
23964             }
23965             if(moved){
23966                 if(this.avoidY){
23967                     var ay = this.avoidY;
23968                     if(y <= ay && (y+h) >= ay){
23969                         y = ay-h-5;   
23970                     }
23971                 }
23972                 xy = [x, y];
23973                 this.storeXY(xy);
23974                 supr.setXY.call(this, xy);
23975                 this.sync();
23976             }
23977         }
23978     },
23979
23980     isVisible : function(){
23981         return this.visible;    
23982     },
23983
23984     // private
23985     showAction : function(){
23986         this.visible = true; // track visibility to prevent getStyle calls
23987         if(this.useDisplay === true){
23988             this.setDisplayed("");
23989         }else if(this.lastXY){
23990             supr.setXY.call(this, this.lastXY);
23991         }else if(this.lastLT){
23992             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23993         }
23994     },
23995
23996     // private
23997     hideAction : function(){
23998         this.visible = false;
23999         if(this.useDisplay === true){
24000             this.setDisplayed(false);
24001         }else{
24002             this.setLeftTop(-10000,-10000);
24003         }
24004     },
24005
24006     // overridden Element method
24007     setVisible : function(v, a, d, c, e){
24008         if(v){
24009             this.showAction();
24010         }
24011         if(a && v){
24012             var cb = function(){
24013                 this.sync(true);
24014                 if(c){
24015                     c();
24016                 }
24017             }.createDelegate(this);
24018             supr.setVisible.call(this, true, true, d, cb, e);
24019         }else{
24020             if(!v){
24021                 this.hideUnders(true);
24022             }
24023             var cb = c;
24024             if(a){
24025                 cb = function(){
24026                     this.hideAction();
24027                     if(c){
24028                         c();
24029                     }
24030                 }.createDelegate(this);
24031             }
24032             supr.setVisible.call(this, v, a, d, cb, e);
24033             if(v){
24034                 this.sync(true);
24035             }else if(!a){
24036                 this.hideAction();
24037             }
24038         }
24039     },
24040
24041     storeXY : function(xy){
24042         delete this.lastLT;
24043         this.lastXY = xy;
24044     },
24045
24046     storeLeftTop : function(left, top){
24047         delete this.lastXY;
24048         this.lastLT = [left, top];
24049     },
24050
24051     // private
24052     beforeFx : function(){
24053         this.beforeAction();
24054         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24055     },
24056
24057     // private
24058     afterFx : function(){
24059         Roo.Layer.superclass.afterFx.apply(this, arguments);
24060         this.sync(this.isVisible());
24061     },
24062
24063     // private
24064     beforeAction : function(){
24065         if(!this.updating && this.shadow){
24066             this.shadow.hide();
24067         }
24068     },
24069
24070     // overridden Element method
24071     setLeft : function(left){
24072         this.storeLeftTop(left, this.getTop(true));
24073         supr.setLeft.apply(this, arguments);
24074         this.sync();
24075     },
24076
24077     setTop : function(top){
24078         this.storeLeftTop(this.getLeft(true), top);
24079         supr.setTop.apply(this, arguments);
24080         this.sync();
24081     },
24082
24083     setLeftTop : function(left, top){
24084         this.storeLeftTop(left, top);
24085         supr.setLeftTop.apply(this, arguments);
24086         this.sync();
24087     },
24088
24089     setXY : function(xy, a, d, c, e){
24090         this.fixDisplay();
24091         this.beforeAction();
24092         this.storeXY(xy);
24093         var cb = this.createCB(c);
24094         supr.setXY.call(this, xy, a, d, cb, e);
24095         if(!a){
24096             cb();
24097         }
24098     },
24099
24100     // private
24101     createCB : function(c){
24102         var el = this;
24103         return function(){
24104             el.constrainXY();
24105             el.sync(true);
24106             if(c){
24107                 c();
24108             }
24109         };
24110     },
24111
24112     // overridden Element method
24113     setX : function(x, a, d, c, e){
24114         this.setXY([x, this.getY()], a, d, c, e);
24115     },
24116
24117     // overridden Element method
24118     setY : function(y, a, d, c, e){
24119         this.setXY([this.getX(), y], a, d, c, e);
24120     },
24121
24122     // overridden Element method
24123     setSize : function(w, h, a, d, c, e){
24124         this.beforeAction();
24125         var cb = this.createCB(c);
24126         supr.setSize.call(this, w, h, a, d, cb, e);
24127         if(!a){
24128             cb();
24129         }
24130     },
24131
24132     // overridden Element method
24133     setWidth : function(w, a, d, c, e){
24134         this.beforeAction();
24135         var cb = this.createCB(c);
24136         supr.setWidth.call(this, w, a, d, cb, e);
24137         if(!a){
24138             cb();
24139         }
24140     },
24141
24142     // overridden Element method
24143     setHeight : function(h, a, d, c, e){
24144         this.beforeAction();
24145         var cb = this.createCB(c);
24146         supr.setHeight.call(this, h, a, d, cb, e);
24147         if(!a){
24148             cb();
24149         }
24150     },
24151
24152     // overridden Element method
24153     setBounds : function(x, y, w, h, a, d, c, e){
24154         this.beforeAction();
24155         var cb = this.createCB(c);
24156         if(!a){
24157             this.storeXY([x, y]);
24158             supr.setXY.call(this, [x, y]);
24159             supr.setSize.call(this, w, h, a, d, cb, e);
24160             cb();
24161         }else{
24162             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24163         }
24164         return this;
24165     },
24166     
24167     /**
24168      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24169      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24170      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24171      * @param {Number} zindex The new z-index to set
24172      * @return {this} The Layer
24173      */
24174     setZIndex : function(zindex){
24175         this.zindex = zindex;
24176         this.setStyle("z-index", zindex + 2);
24177         if(this.shadow){
24178             this.shadow.setZIndex(zindex + 1);
24179         }
24180         if(this.shim){
24181             this.shim.setStyle("z-index", zindex);
24182         }
24183     }
24184 });
24185 })();/*
24186  * Based on:
24187  * Ext JS Library 1.1.1
24188  * Copyright(c) 2006-2007, Ext JS, LLC.
24189  *
24190  * Originally Released Under LGPL - original licence link has changed is not relivant.
24191  *
24192  * Fork - LGPL
24193  * <script type="text/javascript">
24194  */
24195
24196
24197 /**
24198  * @class Roo.Shadow
24199  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24200  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24201  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24202  * @constructor
24203  * Create a new Shadow
24204  * @param {Object} config The config object
24205  */
24206 Roo.Shadow = function(config){
24207     Roo.apply(this, config);
24208     if(typeof this.mode != "string"){
24209         this.mode = this.defaultMode;
24210     }
24211     var o = this.offset, a = {h: 0};
24212     var rad = Math.floor(this.offset/2);
24213     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24214         case "drop":
24215             a.w = 0;
24216             a.l = a.t = o;
24217             a.t -= 1;
24218             if(Roo.isIE){
24219                 a.l -= this.offset + rad;
24220                 a.t -= this.offset + rad;
24221                 a.w -= rad;
24222                 a.h -= rad;
24223                 a.t += 1;
24224             }
24225         break;
24226         case "sides":
24227             a.w = (o*2);
24228             a.l = -o;
24229             a.t = o-1;
24230             if(Roo.isIE){
24231                 a.l -= (this.offset - rad);
24232                 a.t -= this.offset + rad;
24233                 a.l += 1;
24234                 a.w -= (this.offset - rad)*2;
24235                 a.w -= rad + 1;
24236                 a.h -= 1;
24237             }
24238         break;
24239         case "frame":
24240             a.w = a.h = (o*2);
24241             a.l = a.t = -o;
24242             a.t += 1;
24243             a.h -= 2;
24244             if(Roo.isIE){
24245                 a.l -= (this.offset - rad);
24246                 a.t -= (this.offset - rad);
24247                 a.l += 1;
24248                 a.w -= (this.offset + rad + 1);
24249                 a.h -= (this.offset + rad);
24250                 a.h += 1;
24251             }
24252         break;
24253     };
24254
24255     this.adjusts = a;
24256 };
24257
24258 Roo.Shadow.prototype = {
24259     /**
24260      * @cfg {String} mode
24261      * The shadow display mode.  Supports the following options:<br />
24262      * sides: Shadow displays on both sides and bottom only<br />
24263      * frame: Shadow displays equally on all four sides<br />
24264      * drop: Traditional bottom-right drop shadow (default)
24265      */
24266     /**
24267      * @cfg {String} offset
24268      * The number of pixels to offset the shadow from the element (defaults to 4)
24269      */
24270     offset: 4,
24271
24272     // private
24273     defaultMode: "drop",
24274
24275     /**
24276      * Displays the shadow under the target element
24277      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24278      */
24279     show : function(target){
24280         target = Roo.get(target);
24281         if(!this.el){
24282             this.el = Roo.Shadow.Pool.pull();
24283             if(this.el.dom.nextSibling != target.dom){
24284                 this.el.insertBefore(target);
24285             }
24286         }
24287         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24288         if(Roo.isIE){
24289             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24290         }
24291         this.realign(
24292             target.getLeft(true),
24293             target.getTop(true),
24294             target.getWidth(),
24295             target.getHeight()
24296         );
24297         this.el.dom.style.display = "block";
24298     },
24299
24300     /**
24301      * Returns true if the shadow is visible, else false
24302      */
24303     isVisible : function(){
24304         return this.el ? true : false;  
24305     },
24306
24307     /**
24308      * Direct alignment when values are already available. Show must be called at least once before
24309      * calling this method to ensure it is initialized.
24310      * @param {Number} left The target element left position
24311      * @param {Number} top The target element top position
24312      * @param {Number} width The target element width
24313      * @param {Number} height The target element height
24314      */
24315     realign : function(l, t, w, h){
24316         if(!this.el){
24317             return;
24318         }
24319         var a = this.adjusts, d = this.el.dom, s = d.style;
24320         var iea = 0;
24321         s.left = (l+a.l)+"px";
24322         s.top = (t+a.t)+"px";
24323         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24324  
24325         if(s.width != sws || s.height != shs){
24326             s.width = sws;
24327             s.height = shs;
24328             if(!Roo.isIE){
24329                 var cn = d.childNodes;
24330                 var sww = Math.max(0, (sw-12))+"px";
24331                 cn[0].childNodes[1].style.width = sww;
24332                 cn[1].childNodes[1].style.width = sww;
24333                 cn[2].childNodes[1].style.width = sww;
24334                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24335             }
24336         }
24337     },
24338
24339     /**
24340      * Hides this shadow
24341      */
24342     hide : function(){
24343         if(this.el){
24344             this.el.dom.style.display = "none";
24345             Roo.Shadow.Pool.push(this.el);
24346             delete this.el;
24347         }
24348     },
24349
24350     /**
24351      * Adjust the z-index of this shadow
24352      * @param {Number} zindex The new z-index
24353      */
24354     setZIndex : function(z){
24355         this.zIndex = z;
24356         if(this.el){
24357             this.el.setStyle("z-index", z);
24358         }
24359     }
24360 };
24361
24362 // Private utility class that manages the internal Shadow cache
24363 Roo.Shadow.Pool = function(){
24364     var p = [];
24365     var markup = Roo.isIE ?
24366                  '<div class="x-ie-shadow"></div>' :
24367                  '<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>';
24368     return {
24369         pull : function(){
24370             var sh = p.shift();
24371             if(!sh){
24372                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24373                 sh.autoBoxAdjust = false;
24374             }
24375             return sh;
24376         },
24377
24378         push : function(sh){
24379             p.push(sh);
24380         }
24381     };
24382 }();/*
24383  * Based on:
24384  * Ext JS Library 1.1.1
24385  * Copyright(c) 2006-2007, Ext JS, LLC.
24386  *
24387  * Originally Released Under LGPL - original licence link has changed is not relivant.
24388  *
24389  * Fork - LGPL
24390  * <script type="text/javascript">
24391  */
24392
24393
24394 /**
24395  * @class Roo.SplitBar
24396  * @extends Roo.util.Observable
24397  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24398  * <br><br>
24399  * Usage:
24400  * <pre><code>
24401 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24402                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24403 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24404 split.minSize = 100;
24405 split.maxSize = 600;
24406 split.animate = true;
24407 split.on('moved', splitterMoved);
24408 </code></pre>
24409  * @constructor
24410  * Create a new SplitBar
24411  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24412  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24413  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24414  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24415                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24416                         position of the SplitBar).
24417  */
24418 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24419     
24420     /** @private */
24421     this.el = Roo.get(dragElement, true);
24422     this.el.dom.unselectable = "on";
24423     /** @private */
24424     this.resizingEl = Roo.get(resizingElement, true);
24425
24426     /**
24427      * @private
24428      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24429      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24430      * @type Number
24431      */
24432     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24433     
24434     /**
24435      * The minimum size of the resizing element. (Defaults to 0)
24436      * @type Number
24437      */
24438     this.minSize = 0;
24439     
24440     /**
24441      * The maximum size of the resizing element. (Defaults to 2000)
24442      * @type Number
24443      */
24444     this.maxSize = 2000;
24445     
24446     /**
24447      * Whether to animate the transition to the new size
24448      * @type Boolean
24449      */
24450     this.animate = false;
24451     
24452     /**
24453      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24454      * @type Boolean
24455      */
24456     this.useShim = false;
24457     
24458     /** @private */
24459     this.shim = null;
24460     
24461     if(!existingProxy){
24462         /** @private */
24463         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24464     }else{
24465         this.proxy = Roo.get(existingProxy).dom;
24466     }
24467     /** @private */
24468     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24469     
24470     /** @private */
24471     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24472     
24473     /** @private */
24474     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24475     
24476     /** @private */
24477     this.dragSpecs = {};
24478     
24479     /**
24480      * @private The adapter to use to positon and resize elements
24481      */
24482     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24483     this.adapter.init(this);
24484     
24485     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24486         /** @private */
24487         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24488         this.el.addClass("x-splitbar-h");
24489     }else{
24490         /** @private */
24491         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24492         this.el.addClass("x-splitbar-v");
24493     }
24494     
24495     this.addEvents({
24496         /**
24497          * @event resize
24498          * Fires when the splitter is moved (alias for {@link #event-moved})
24499          * @param {Roo.SplitBar} this
24500          * @param {Number} newSize the new width or height
24501          */
24502         "resize" : true,
24503         /**
24504          * @event moved
24505          * Fires when the splitter is moved
24506          * @param {Roo.SplitBar} this
24507          * @param {Number} newSize the new width or height
24508          */
24509         "moved" : true,
24510         /**
24511          * @event beforeresize
24512          * Fires before the splitter is dragged
24513          * @param {Roo.SplitBar} this
24514          */
24515         "beforeresize" : true,
24516
24517         "beforeapply" : true
24518     });
24519
24520     Roo.util.Observable.call(this);
24521 };
24522
24523 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24524     onStartProxyDrag : function(x, y){
24525         this.fireEvent("beforeresize", this);
24526         if(!this.overlay){
24527             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24528             o.unselectable();
24529             o.enableDisplayMode("block");
24530             // all splitbars share the same overlay
24531             Roo.SplitBar.prototype.overlay = o;
24532         }
24533         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24534         this.overlay.show();
24535         Roo.get(this.proxy).setDisplayed("block");
24536         var size = this.adapter.getElementSize(this);
24537         this.activeMinSize = this.getMinimumSize();;
24538         this.activeMaxSize = this.getMaximumSize();;
24539         var c1 = size - this.activeMinSize;
24540         var c2 = Math.max(this.activeMaxSize - size, 0);
24541         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24542             this.dd.resetConstraints();
24543             this.dd.setXConstraint(
24544                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24545                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24546             );
24547             this.dd.setYConstraint(0, 0);
24548         }else{
24549             this.dd.resetConstraints();
24550             this.dd.setXConstraint(0, 0);
24551             this.dd.setYConstraint(
24552                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24553                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24554             );
24555          }
24556         this.dragSpecs.startSize = size;
24557         this.dragSpecs.startPoint = [x, y];
24558         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24559     },
24560     
24561     /** 
24562      * @private Called after the drag operation by the DDProxy
24563      */
24564     onEndProxyDrag : function(e){
24565         Roo.get(this.proxy).setDisplayed(false);
24566         var endPoint = Roo.lib.Event.getXY(e);
24567         if(this.overlay){
24568             this.overlay.hide();
24569         }
24570         var newSize;
24571         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24572             newSize = this.dragSpecs.startSize + 
24573                 (this.placement == Roo.SplitBar.LEFT ?
24574                     endPoint[0] - this.dragSpecs.startPoint[0] :
24575                     this.dragSpecs.startPoint[0] - endPoint[0]
24576                 );
24577         }else{
24578             newSize = this.dragSpecs.startSize + 
24579                 (this.placement == Roo.SplitBar.TOP ?
24580                     endPoint[1] - this.dragSpecs.startPoint[1] :
24581                     this.dragSpecs.startPoint[1] - endPoint[1]
24582                 );
24583         }
24584         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24585         if(newSize != this.dragSpecs.startSize){
24586             if(this.fireEvent('beforeapply', this, newSize) !== false){
24587                 this.adapter.setElementSize(this, newSize);
24588                 this.fireEvent("moved", this, newSize);
24589                 this.fireEvent("resize", this, newSize);
24590             }
24591         }
24592     },
24593     
24594     /**
24595      * Get the adapter this SplitBar uses
24596      * @return The adapter object
24597      */
24598     getAdapter : function(){
24599         return this.adapter;
24600     },
24601     
24602     /**
24603      * Set the adapter this SplitBar uses
24604      * @param {Object} adapter A SplitBar adapter object
24605      */
24606     setAdapter : function(adapter){
24607         this.adapter = adapter;
24608         this.adapter.init(this);
24609     },
24610     
24611     /**
24612      * Gets the minimum size for the resizing element
24613      * @return {Number} The minimum size
24614      */
24615     getMinimumSize : function(){
24616         return this.minSize;
24617     },
24618     
24619     /**
24620      * Sets the minimum size for the resizing element
24621      * @param {Number} minSize The minimum size
24622      */
24623     setMinimumSize : function(minSize){
24624         this.minSize = minSize;
24625     },
24626     
24627     /**
24628      * Gets the maximum size for the resizing element
24629      * @return {Number} The maximum size
24630      */
24631     getMaximumSize : function(){
24632         return this.maxSize;
24633     },
24634     
24635     /**
24636      * Sets the maximum size for the resizing element
24637      * @param {Number} maxSize The maximum size
24638      */
24639     setMaximumSize : function(maxSize){
24640         this.maxSize = maxSize;
24641     },
24642     
24643     /**
24644      * Sets the initialize size for the resizing element
24645      * @param {Number} size The initial size
24646      */
24647     setCurrentSize : function(size){
24648         var oldAnimate = this.animate;
24649         this.animate = false;
24650         this.adapter.setElementSize(this, size);
24651         this.animate = oldAnimate;
24652     },
24653     
24654     /**
24655      * Destroy this splitbar. 
24656      * @param {Boolean} removeEl True to remove the element
24657      */
24658     destroy : function(removeEl){
24659         if(this.shim){
24660             this.shim.remove();
24661         }
24662         this.dd.unreg();
24663         this.proxy.parentNode.removeChild(this.proxy);
24664         if(removeEl){
24665             this.el.remove();
24666         }
24667     }
24668 });
24669
24670 /**
24671  * @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.
24672  */
24673 Roo.SplitBar.createProxy = function(dir){
24674     var proxy = new Roo.Element(document.createElement("div"));
24675     proxy.unselectable();
24676     var cls = 'x-splitbar-proxy';
24677     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24678     document.body.appendChild(proxy.dom);
24679     return proxy.dom;
24680 };
24681
24682 /** 
24683  * @class Roo.SplitBar.BasicLayoutAdapter
24684  * Default Adapter. It assumes the splitter and resizing element are not positioned
24685  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24686  */
24687 Roo.SplitBar.BasicLayoutAdapter = function(){
24688 };
24689
24690 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24691     // do nothing for now
24692     init : function(s){
24693     
24694     },
24695     /**
24696      * Called before drag operations to get the current size of the resizing element. 
24697      * @param {Roo.SplitBar} s The SplitBar using this adapter
24698      */
24699      getElementSize : function(s){
24700         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24701             return s.resizingEl.getWidth();
24702         }else{
24703             return s.resizingEl.getHeight();
24704         }
24705     },
24706     
24707     /**
24708      * Called after drag operations to set the size of the resizing element.
24709      * @param {Roo.SplitBar} s The SplitBar using this adapter
24710      * @param {Number} newSize The new size to set
24711      * @param {Function} onComplete A function to be invoked when resizing is complete
24712      */
24713     setElementSize : function(s, newSize, onComplete){
24714         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24715             if(!s.animate){
24716                 s.resizingEl.setWidth(newSize);
24717                 if(onComplete){
24718                     onComplete(s, newSize);
24719                 }
24720             }else{
24721                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24722             }
24723         }else{
24724             
24725             if(!s.animate){
24726                 s.resizingEl.setHeight(newSize);
24727                 if(onComplete){
24728                     onComplete(s, newSize);
24729                 }
24730             }else{
24731                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24732             }
24733         }
24734     }
24735 };
24736
24737 /** 
24738  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24739  * @extends Roo.SplitBar.BasicLayoutAdapter
24740  * Adapter that  moves the splitter element to align with the resized sizing element. 
24741  * Used with an absolute positioned SplitBar.
24742  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24743  * document.body, make sure you assign an id to the body element.
24744  */
24745 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24746     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24747     this.container = Roo.get(container);
24748 };
24749
24750 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24751     init : function(s){
24752         this.basic.init(s);
24753     },
24754     
24755     getElementSize : function(s){
24756         return this.basic.getElementSize(s);
24757     },
24758     
24759     setElementSize : function(s, newSize, onComplete){
24760         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24761     },
24762     
24763     moveSplitter : function(s){
24764         var yes = Roo.SplitBar;
24765         switch(s.placement){
24766             case yes.LEFT:
24767                 s.el.setX(s.resizingEl.getRight());
24768                 break;
24769             case yes.RIGHT:
24770                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24771                 break;
24772             case yes.TOP:
24773                 s.el.setY(s.resizingEl.getBottom());
24774                 break;
24775             case yes.BOTTOM:
24776                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24777                 break;
24778         }
24779     }
24780 };
24781
24782 /**
24783  * Orientation constant - Create a vertical SplitBar
24784  * @static
24785  * @type Number
24786  */
24787 Roo.SplitBar.VERTICAL = 1;
24788
24789 /**
24790  * Orientation constant - Create a horizontal SplitBar
24791  * @static
24792  * @type Number
24793  */
24794 Roo.SplitBar.HORIZONTAL = 2;
24795
24796 /**
24797  * Placement constant - The resizing element is to the left of the splitter element
24798  * @static
24799  * @type Number
24800  */
24801 Roo.SplitBar.LEFT = 1;
24802
24803 /**
24804  * Placement constant - The resizing element is to the right of the splitter element
24805  * @static
24806  * @type Number
24807  */
24808 Roo.SplitBar.RIGHT = 2;
24809
24810 /**
24811  * Placement constant - The resizing element is positioned above the splitter element
24812  * @static
24813  * @type Number
24814  */
24815 Roo.SplitBar.TOP = 3;
24816
24817 /**
24818  * Placement constant - The resizing element is positioned under splitter element
24819  * @static
24820  * @type Number
24821  */
24822 Roo.SplitBar.BOTTOM = 4;
24823 /*
24824  * Based on:
24825  * Ext JS Library 1.1.1
24826  * Copyright(c) 2006-2007, Ext JS, LLC.
24827  *
24828  * Originally Released Under LGPL - original licence link has changed is not relivant.
24829  *
24830  * Fork - LGPL
24831  * <script type="text/javascript">
24832  */
24833
24834 /**
24835  * @class Roo.View
24836  * @extends Roo.util.Observable
24837  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24838  * This class also supports single and multi selection modes. <br>
24839  * Create a data model bound view:
24840  <pre><code>
24841  var store = new Roo.data.Store(...);
24842
24843  var view = new Roo.View({
24844     el : "my-element",
24845     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24846  
24847     singleSelect: true,
24848     selectedClass: "ydataview-selected",
24849     store: store
24850  });
24851
24852  // listen for node click?
24853  view.on("click", function(vw, index, node, e){
24854  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24855  });
24856
24857  // load XML data
24858  dataModel.load("foobar.xml");
24859  </code></pre>
24860  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24861  * <br><br>
24862  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24863  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24864  * 
24865  * Note: old style constructor is still suported (container, template, config)
24866  * 
24867  * @constructor
24868  * Create a new View
24869  * @param {Object} config The config object
24870  * 
24871  */
24872 Roo.View = function(config, depreciated_tpl, depreciated_config){
24873     
24874     this.parent = false;
24875     
24876     if (typeof(depreciated_tpl) == 'undefined') {
24877         // new way.. - universal constructor.
24878         Roo.apply(this, config);
24879         this.el  = Roo.get(this.el);
24880     } else {
24881         // old format..
24882         this.el  = Roo.get(config);
24883         this.tpl = depreciated_tpl;
24884         Roo.apply(this, depreciated_config);
24885     }
24886     this.wrapEl  = this.el.wrap().wrap();
24887     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24888     
24889     
24890     if(typeof(this.tpl) == "string"){
24891         this.tpl = new Roo.Template(this.tpl);
24892     } else {
24893         // support xtype ctors..
24894         this.tpl = new Roo.factory(this.tpl, Roo);
24895     }
24896     
24897     
24898     this.tpl.compile();
24899     
24900     /** @private */
24901     this.addEvents({
24902         /**
24903          * @event beforeclick
24904          * Fires before a click is processed. Returns false to cancel the default action.
24905          * @param {Roo.View} this
24906          * @param {Number} index The index of the target node
24907          * @param {HTMLElement} node The target node
24908          * @param {Roo.EventObject} e The raw event object
24909          */
24910             "beforeclick" : true,
24911         /**
24912          * @event click
24913          * Fires when a template node is clicked.
24914          * @param {Roo.View} this
24915          * @param {Number} index The index of the target node
24916          * @param {HTMLElement} node The target node
24917          * @param {Roo.EventObject} e The raw event object
24918          */
24919             "click" : true,
24920         /**
24921          * @event dblclick
24922          * Fires when a template node is double clicked.
24923          * @param {Roo.View} this
24924          * @param {Number} index The index of the target node
24925          * @param {HTMLElement} node The target node
24926          * @param {Roo.EventObject} e The raw event object
24927          */
24928             "dblclick" : true,
24929         /**
24930          * @event contextmenu
24931          * Fires when a template node is right clicked.
24932          * @param {Roo.View} this
24933          * @param {Number} index The index of the target node
24934          * @param {HTMLElement} node The target node
24935          * @param {Roo.EventObject} e The raw event object
24936          */
24937             "contextmenu" : true,
24938         /**
24939          * @event selectionchange
24940          * Fires when the selected nodes change.
24941          * @param {Roo.View} this
24942          * @param {Array} selections Array of the selected nodes
24943          */
24944             "selectionchange" : true,
24945     
24946         /**
24947          * @event beforeselect
24948          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24949          * @param {Roo.View} this
24950          * @param {HTMLElement} node The node to be selected
24951          * @param {Array} selections Array of currently selected nodes
24952          */
24953             "beforeselect" : true,
24954         /**
24955          * @event preparedata
24956          * Fires on every row to render, to allow you to change the data.
24957          * @param {Roo.View} this
24958          * @param {Object} data to be rendered (change this)
24959          */
24960           "preparedata" : true
24961           
24962           
24963         });
24964
24965
24966
24967     this.el.on({
24968         "click": this.onClick,
24969         "dblclick": this.onDblClick,
24970         "contextmenu": this.onContextMenu,
24971         scope:this
24972     });
24973
24974     this.selections = [];
24975     this.nodes = [];
24976     this.cmp = new Roo.CompositeElementLite([]);
24977     if(this.store){
24978         this.store = Roo.factory(this.store, Roo.data);
24979         this.setStore(this.store, true);
24980     }
24981     
24982     if ( this.footer && this.footer.xtype) {
24983            
24984          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24985         
24986         this.footer.dataSource = this.store
24987         this.footer.container = fctr;
24988         this.footer = Roo.factory(this.footer, Roo);
24989         fctr.insertFirst(this.el);
24990         
24991         // this is a bit insane - as the paging toolbar seems to detach the el..
24992 //        dom.parentNode.parentNode.parentNode
24993          // they get detached?
24994     }
24995     
24996     
24997     Roo.View.superclass.constructor.call(this);
24998     
24999     
25000 };
25001
25002 Roo.extend(Roo.View, Roo.util.Observable, {
25003     
25004      /**
25005      * @cfg {Roo.data.Store} store Data store to load data from.
25006      */
25007     store : false,
25008     
25009     /**
25010      * @cfg {String|Roo.Element} el The container element.
25011      */
25012     el : '',
25013     
25014     /**
25015      * @cfg {String|Roo.Template} tpl The template used by this View 
25016      */
25017     tpl : false,
25018     /**
25019      * @cfg {String} dataName the named area of the template to use as the data area
25020      *                          Works with domtemplates roo-name="name"
25021      */
25022     dataName: false,
25023     /**
25024      * @cfg {String} selectedClass The css class to add to selected nodes
25025      */
25026     selectedClass : "x-view-selected",
25027      /**
25028      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25029      */
25030     emptyText : "",
25031     
25032     /**
25033      * @cfg {String} text to display on mask (default Loading)
25034      */
25035     mask : false,
25036     /**
25037      * @cfg {Boolean} multiSelect Allow multiple selection
25038      */
25039     multiSelect : false,
25040     /**
25041      * @cfg {Boolean} singleSelect Allow single selection
25042      */
25043     singleSelect:  false,
25044     
25045     /**
25046      * @cfg {Boolean} toggleSelect - selecting 
25047      */
25048     toggleSelect : false,
25049     
25050     /**
25051      * @cfg {Boolean} tickable - selecting 
25052      */
25053     tickable : false,
25054     
25055     /**
25056      * Returns the element this view is bound to.
25057      * @return {Roo.Element}
25058      */
25059     getEl : function(){
25060         return this.wrapEl;
25061     },
25062     
25063     
25064
25065     /**
25066      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25067      */
25068     refresh : function(){
25069         //Roo.log('refresh');
25070         var t = this.tpl;
25071         
25072         // if we are using something like 'domtemplate', then
25073         // the what gets used is:
25074         // t.applySubtemplate(NAME, data, wrapping data..)
25075         // the outer template then get' applied with
25076         //     the store 'extra data'
25077         // and the body get's added to the
25078         //      roo-name="data" node?
25079         //      <span class='roo-tpl-{name}'></span> ?????
25080         
25081         
25082         
25083         this.clearSelections();
25084         this.el.update("");
25085         var html = [];
25086         var records = this.store.getRange();
25087         if(records.length < 1) {
25088             
25089             // is this valid??  = should it render a template??
25090             
25091             this.el.update(this.emptyText);
25092             return;
25093         }
25094         var el = this.el;
25095         if (this.dataName) {
25096             this.el.update(t.apply(this.store.meta)); //????
25097             el = this.el.child('.roo-tpl-' + this.dataName);
25098         }
25099         
25100         for(var i = 0, len = records.length; i < len; i++){
25101             var data = this.prepareData(records[i].data, i, records[i]);
25102             this.fireEvent("preparedata", this, data, i, records[i]);
25103             
25104             var d = Roo.apply({}, data);
25105             
25106             if(this.tickable){
25107                 Roo.apply(d, {'roo-id' : Roo.id()});
25108                 
25109                 var _this = this;
25110             
25111                 Roo.each(this.parent.item, function(item){
25112                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25113                         return;
25114                     }
25115                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25116                 });
25117             }
25118             
25119             html[html.length] = Roo.util.Format.trim(
25120                 this.dataName ?
25121                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25122                     t.apply(d)
25123             );
25124         }
25125         
25126         
25127         
25128         el.update(html.join(""));
25129         this.nodes = el.dom.childNodes;
25130         this.updateIndexes(0);
25131     },
25132     
25133
25134     /**
25135      * Function to override to reformat the data that is sent to
25136      * the template for each node.
25137      * DEPRICATED - use the preparedata event handler.
25138      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25139      * a JSON object for an UpdateManager bound view).
25140      */
25141     prepareData : function(data, index, record)
25142     {
25143         this.fireEvent("preparedata", this, data, index, record);
25144         return data;
25145     },
25146
25147     onUpdate : function(ds, record){
25148         // Roo.log('on update');   
25149         this.clearSelections();
25150         var index = this.store.indexOf(record);
25151         var n = this.nodes[index];
25152         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25153         n.parentNode.removeChild(n);
25154         this.updateIndexes(index, index);
25155     },
25156
25157     
25158     
25159 // --------- FIXME     
25160     onAdd : function(ds, records, index)
25161     {
25162         //Roo.log(['on Add', ds, records, index] );        
25163         this.clearSelections();
25164         if(this.nodes.length == 0){
25165             this.refresh();
25166             return;
25167         }
25168         var n = this.nodes[index];
25169         for(var i = 0, len = records.length; i < len; i++){
25170             var d = this.prepareData(records[i].data, i, records[i]);
25171             if(n){
25172                 this.tpl.insertBefore(n, d);
25173             }else{
25174                 
25175                 this.tpl.append(this.el, d);
25176             }
25177         }
25178         this.updateIndexes(index);
25179     },
25180
25181     onRemove : function(ds, record, index){
25182        // Roo.log('onRemove');
25183         this.clearSelections();
25184         var el = this.dataName  ?
25185             this.el.child('.roo-tpl-' + this.dataName) :
25186             this.el; 
25187         
25188         el.dom.removeChild(this.nodes[index]);
25189         this.updateIndexes(index);
25190     },
25191
25192     /**
25193      * Refresh an individual node.
25194      * @param {Number} index
25195      */
25196     refreshNode : function(index){
25197         this.onUpdate(this.store, this.store.getAt(index));
25198     },
25199
25200     updateIndexes : function(startIndex, endIndex){
25201         var ns = this.nodes;
25202         startIndex = startIndex || 0;
25203         endIndex = endIndex || ns.length - 1;
25204         for(var i = startIndex; i <= endIndex; i++){
25205             ns[i].nodeIndex = i;
25206         }
25207     },
25208
25209     /**
25210      * Changes the data store this view uses and refresh the view.
25211      * @param {Store} store
25212      */
25213     setStore : function(store, initial){
25214         if(!initial && this.store){
25215             this.store.un("datachanged", this.refresh);
25216             this.store.un("add", this.onAdd);
25217             this.store.un("remove", this.onRemove);
25218             this.store.un("update", this.onUpdate);
25219             this.store.un("clear", this.refresh);
25220             this.store.un("beforeload", this.onBeforeLoad);
25221             this.store.un("load", this.onLoad);
25222             this.store.un("loadexception", this.onLoad);
25223         }
25224         if(store){
25225           
25226             store.on("datachanged", this.refresh, this);
25227             store.on("add", this.onAdd, this);
25228             store.on("remove", this.onRemove, this);
25229             store.on("update", this.onUpdate, this);
25230             store.on("clear", this.refresh, this);
25231             store.on("beforeload", this.onBeforeLoad, this);
25232             store.on("load", this.onLoad, this);
25233             store.on("loadexception", this.onLoad, this);
25234         }
25235         
25236         if(store){
25237             this.refresh();
25238         }
25239     },
25240     /**
25241      * onbeforeLoad - masks the loading area.
25242      *
25243      */
25244     onBeforeLoad : function(store,opts)
25245     {
25246          //Roo.log('onBeforeLoad');   
25247         if (!opts.add) {
25248             this.el.update("");
25249         }
25250         this.el.mask(this.mask ? this.mask : "Loading" ); 
25251     },
25252     onLoad : function ()
25253     {
25254         this.el.unmask();
25255     },
25256     
25257
25258     /**
25259      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25260      * @param {HTMLElement} node
25261      * @return {HTMLElement} The template node
25262      */
25263     findItemFromChild : function(node){
25264         var el = this.dataName  ?
25265             this.el.child('.roo-tpl-' + this.dataName,true) :
25266             this.el.dom; 
25267         
25268         if(!node || node.parentNode == el){
25269                     return node;
25270             }
25271             var p = node.parentNode;
25272             while(p && p != el){
25273             if(p.parentNode == el){
25274                 return p;
25275             }
25276             p = p.parentNode;
25277         }
25278             return null;
25279     },
25280
25281     /** @ignore */
25282     onClick : function(e){
25283         var item = this.findItemFromChild(e.getTarget());
25284         if(item){
25285             var index = this.indexOf(item);
25286             if(this.onItemClick(item, index, e) !== false){
25287                 this.fireEvent("click", this, index, item, e);
25288             }
25289         }else{
25290             this.clearSelections();
25291         }
25292     },
25293
25294     /** @ignore */
25295     onContextMenu : function(e){
25296         var item = this.findItemFromChild(e.getTarget());
25297         if(item){
25298             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25299         }
25300     },
25301
25302     /** @ignore */
25303     onDblClick : function(e){
25304         var item = this.findItemFromChild(e.getTarget());
25305         if(item){
25306             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25307         }
25308     },
25309
25310     onItemClick : function(item, index, e)
25311     {
25312         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25313             return false;
25314         }
25315         if (this.toggleSelect) {
25316             var m = this.isSelected(item) ? 'unselect' : 'select';
25317             //Roo.log(m);
25318             var _t = this;
25319             _t[m](item, true, false);
25320             return true;
25321         }
25322         if(this.multiSelect || this.singleSelect){
25323             if(this.multiSelect && e.shiftKey && this.lastSelection){
25324                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25325             }else{
25326                 this.select(item, this.multiSelect && e.ctrlKey);
25327                 this.lastSelection = item;
25328             }
25329             
25330             if(!this.tickable){
25331                 e.preventDefault();
25332             }
25333             
25334         }
25335         return true;
25336     },
25337
25338     /**
25339      * Get the number of selected nodes.
25340      * @return {Number}
25341      */
25342     getSelectionCount : function(){
25343         return this.selections.length;
25344     },
25345
25346     /**
25347      * Get the currently selected nodes.
25348      * @return {Array} An array of HTMLElements
25349      */
25350     getSelectedNodes : function(){
25351         return this.selections;
25352     },
25353
25354     /**
25355      * Get the indexes of the selected nodes.
25356      * @return {Array}
25357      */
25358     getSelectedIndexes : function(){
25359         var indexes = [], s = this.selections;
25360         for(var i = 0, len = s.length; i < len; i++){
25361             indexes.push(s[i].nodeIndex);
25362         }
25363         return indexes;
25364     },
25365
25366     /**
25367      * Clear all selections
25368      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25369      */
25370     clearSelections : function(suppressEvent){
25371         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25372             this.cmp.elements = this.selections;
25373             this.cmp.removeClass(this.selectedClass);
25374             this.selections = [];
25375             if(!suppressEvent){
25376                 this.fireEvent("selectionchange", this, this.selections);
25377             }
25378         }
25379     },
25380
25381     /**
25382      * Returns true if the passed node is selected
25383      * @param {HTMLElement/Number} node The node or node index
25384      * @return {Boolean}
25385      */
25386     isSelected : function(node){
25387         var s = this.selections;
25388         if(s.length < 1){
25389             return false;
25390         }
25391         node = this.getNode(node);
25392         return s.indexOf(node) !== -1;
25393     },
25394
25395     /**
25396      * Selects nodes.
25397      * @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
25398      * @param {Boolean} keepExisting (optional) true to keep existing selections
25399      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25400      */
25401     select : function(nodeInfo, keepExisting, suppressEvent){
25402         if(nodeInfo instanceof Array){
25403             if(!keepExisting){
25404                 this.clearSelections(true);
25405             }
25406             for(var i = 0, len = nodeInfo.length; i < len; i++){
25407                 this.select(nodeInfo[i], true, true);
25408             }
25409             return;
25410         } 
25411         var node = this.getNode(nodeInfo);
25412         if(!node || this.isSelected(node)){
25413             return; // already selected.
25414         }
25415         if(!keepExisting){
25416             this.clearSelections(true);
25417         }
25418         
25419         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25420             Roo.fly(node).addClass(this.selectedClass);
25421             this.selections.push(node);
25422             if(!suppressEvent){
25423                 this.fireEvent("selectionchange", this, this.selections);
25424             }
25425         }
25426         
25427         
25428     },
25429       /**
25430      * Unselects nodes.
25431      * @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
25432      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25433      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25434      */
25435     unselect : function(nodeInfo, keepExisting, suppressEvent)
25436     {
25437         if(nodeInfo instanceof Array){
25438             Roo.each(this.selections, function(s) {
25439                 this.unselect(s, nodeInfo);
25440             }, this);
25441             return;
25442         }
25443         var node = this.getNode(nodeInfo);
25444         if(!node || !this.isSelected(node)){
25445             //Roo.log("not selected");
25446             return; // not selected.
25447         }
25448         // fireevent???
25449         var ns = [];
25450         Roo.each(this.selections, function(s) {
25451             if (s == node ) {
25452                 Roo.fly(node).removeClass(this.selectedClass);
25453
25454                 return;
25455             }
25456             ns.push(s);
25457         },this);
25458         
25459         this.selections= ns;
25460         this.fireEvent("selectionchange", this, this.selections);
25461     },
25462
25463     /**
25464      * Gets a template node.
25465      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25466      * @return {HTMLElement} The node or null if it wasn't found
25467      */
25468     getNode : function(nodeInfo){
25469         if(typeof nodeInfo == "string"){
25470             return document.getElementById(nodeInfo);
25471         }else if(typeof nodeInfo == "number"){
25472             return this.nodes[nodeInfo];
25473         }
25474         return nodeInfo;
25475     },
25476
25477     /**
25478      * Gets a range template nodes.
25479      * @param {Number} startIndex
25480      * @param {Number} endIndex
25481      * @return {Array} An array of nodes
25482      */
25483     getNodes : function(start, end){
25484         var ns = this.nodes;
25485         start = start || 0;
25486         end = typeof end == "undefined" ? ns.length - 1 : end;
25487         var nodes = [];
25488         if(start <= end){
25489             for(var i = start; i <= end; i++){
25490                 nodes.push(ns[i]);
25491             }
25492         } else{
25493             for(var i = start; i >= end; i--){
25494                 nodes.push(ns[i]);
25495             }
25496         }
25497         return nodes;
25498     },
25499
25500     /**
25501      * Finds the index of the passed node
25502      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25503      * @return {Number} The index of the node or -1
25504      */
25505     indexOf : function(node){
25506         node = this.getNode(node);
25507         if(typeof node.nodeIndex == "number"){
25508             return node.nodeIndex;
25509         }
25510         var ns = this.nodes;
25511         for(var i = 0, len = ns.length; i < len; i++){
25512             if(ns[i] == node){
25513                 return i;
25514             }
25515         }
25516         return -1;
25517     }
25518 });
25519 /*
25520  * Based on:
25521  * Ext JS Library 1.1.1
25522  * Copyright(c) 2006-2007, Ext JS, LLC.
25523  *
25524  * Originally Released Under LGPL - original licence link has changed is not relivant.
25525  *
25526  * Fork - LGPL
25527  * <script type="text/javascript">
25528  */
25529
25530 /**
25531  * @class Roo.JsonView
25532  * @extends Roo.View
25533  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25534 <pre><code>
25535 var view = new Roo.JsonView({
25536     container: "my-element",
25537     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25538     multiSelect: true, 
25539     jsonRoot: "data" 
25540 });
25541
25542 // listen for node click?
25543 view.on("click", function(vw, index, node, e){
25544     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25545 });
25546
25547 // direct load of JSON data
25548 view.load("foobar.php");
25549
25550 // Example from my blog list
25551 var tpl = new Roo.Template(
25552     '&lt;div class="entry"&gt;' +
25553     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25554     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25555     "&lt;/div&gt;&lt;hr /&gt;"
25556 );
25557
25558 var moreView = new Roo.JsonView({
25559     container :  "entry-list", 
25560     template : tpl,
25561     jsonRoot: "posts"
25562 });
25563 moreView.on("beforerender", this.sortEntries, this);
25564 moreView.load({
25565     url: "/blog/get-posts.php",
25566     params: "allposts=true",
25567     text: "Loading Blog Entries..."
25568 });
25569 </code></pre>
25570
25571 * Note: old code is supported with arguments : (container, template, config)
25572
25573
25574  * @constructor
25575  * Create a new JsonView
25576  * 
25577  * @param {Object} config The config object
25578  * 
25579  */
25580 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25581     
25582     
25583     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25584
25585     var um = this.el.getUpdateManager();
25586     um.setRenderer(this);
25587     um.on("update", this.onLoad, this);
25588     um.on("failure", this.onLoadException, this);
25589
25590     /**
25591      * @event beforerender
25592      * Fires before rendering of the downloaded JSON data.
25593      * @param {Roo.JsonView} this
25594      * @param {Object} data The JSON data loaded
25595      */
25596     /**
25597      * @event load
25598      * Fires when data is loaded.
25599      * @param {Roo.JsonView} this
25600      * @param {Object} data The JSON data loaded
25601      * @param {Object} response The raw Connect response object
25602      */
25603     /**
25604      * @event loadexception
25605      * Fires when loading fails.
25606      * @param {Roo.JsonView} this
25607      * @param {Object} response The raw Connect response object
25608      */
25609     this.addEvents({
25610         'beforerender' : true,
25611         'load' : true,
25612         'loadexception' : true
25613     });
25614 };
25615 Roo.extend(Roo.JsonView, Roo.View, {
25616     /**
25617      * @type {String} The root property in the loaded JSON object that contains the data
25618      */
25619     jsonRoot : "",
25620
25621     /**
25622      * Refreshes the view.
25623      */
25624     refresh : function(){
25625         this.clearSelections();
25626         this.el.update("");
25627         var html = [];
25628         var o = this.jsonData;
25629         if(o && o.length > 0){
25630             for(var i = 0, len = o.length; i < len; i++){
25631                 var data = this.prepareData(o[i], i, o);
25632                 html[html.length] = this.tpl.apply(data);
25633             }
25634         }else{
25635             html.push(this.emptyText);
25636         }
25637         this.el.update(html.join(""));
25638         this.nodes = this.el.dom.childNodes;
25639         this.updateIndexes(0);
25640     },
25641
25642     /**
25643      * 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.
25644      * @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:
25645      <pre><code>
25646      view.load({
25647          url: "your-url.php",
25648          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25649          callback: yourFunction,
25650          scope: yourObject, //(optional scope)
25651          discardUrl: false,
25652          nocache: false,
25653          text: "Loading...",
25654          timeout: 30,
25655          scripts: false
25656      });
25657      </code></pre>
25658      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25659      * 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.
25660      * @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}
25661      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25662      * @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.
25663      */
25664     load : function(){
25665         var um = this.el.getUpdateManager();
25666         um.update.apply(um, arguments);
25667     },
25668
25669     render : function(el, response){
25670         this.clearSelections();
25671         this.el.update("");
25672         var o;
25673         try{
25674             o = Roo.util.JSON.decode(response.responseText);
25675             if(this.jsonRoot){
25676                 
25677                 o = o[this.jsonRoot];
25678             }
25679         } catch(e){
25680         }
25681         /**
25682          * The current JSON data or null
25683          */
25684         this.jsonData = o;
25685         this.beforeRender();
25686         this.refresh();
25687     },
25688
25689 /**
25690  * Get the number of records in the current JSON dataset
25691  * @return {Number}
25692  */
25693     getCount : function(){
25694         return this.jsonData ? this.jsonData.length : 0;
25695     },
25696
25697 /**
25698  * Returns the JSON object for the specified node(s)
25699  * @param {HTMLElement/Array} node The node or an array of nodes
25700  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25701  * you get the JSON object for the node
25702  */
25703     getNodeData : function(node){
25704         if(node instanceof Array){
25705             var data = [];
25706             for(var i = 0, len = node.length; i < len; i++){
25707                 data.push(this.getNodeData(node[i]));
25708             }
25709             return data;
25710         }
25711         return this.jsonData[this.indexOf(node)] || null;
25712     },
25713
25714     beforeRender : function(){
25715         this.snapshot = this.jsonData;
25716         if(this.sortInfo){
25717             this.sort.apply(this, this.sortInfo);
25718         }
25719         this.fireEvent("beforerender", this, this.jsonData);
25720     },
25721
25722     onLoad : function(el, o){
25723         this.fireEvent("load", this, this.jsonData, o);
25724     },
25725
25726     onLoadException : function(el, o){
25727         this.fireEvent("loadexception", this, o);
25728     },
25729
25730 /**
25731  * Filter the data by a specific property.
25732  * @param {String} property A property on your JSON objects
25733  * @param {String/RegExp} value Either string that the property values
25734  * should start with, or a RegExp to test against the property
25735  */
25736     filter : function(property, value){
25737         if(this.jsonData){
25738             var data = [];
25739             var ss = this.snapshot;
25740             if(typeof value == "string"){
25741                 var vlen = value.length;
25742                 if(vlen == 0){
25743                     this.clearFilter();
25744                     return;
25745                 }
25746                 value = value.toLowerCase();
25747                 for(var i = 0, len = ss.length; i < len; i++){
25748                     var o = ss[i];
25749                     if(o[property].substr(0, vlen).toLowerCase() == value){
25750                         data.push(o);
25751                     }
25752                 }
25753             } else if(value.exec){ // regex?
25754                 for(var i = 0, len = ss.length; i < len; i++){
25755                     var o = ss[i];
25756                     if(value.test(o[property])){
25757                         data.push(o);
25758                     }
25759                 }
25760             } else{
25761                 return;
25762             }
25763             this.jsonData = data;
25764             this.refresh();
25765         }
25766     },
25767
25768 /**
25769  * Filter by a function. The passed function will be called with each
25770  * object in the current dataset. If the function returns true the value is kept,
25771  * otherwise it is filtered.
25772  * @param {Function} fn
25773  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25774  */
25775     filterBy : function(fn, scope){
25776         if(this.jsonData){
25777             var data = [];
25778             var ss = this.snapshot;
25779             for(var i = 0, len = ss.length; i < len; i++){
25780                 var o = ss[i];
25781                 if(fn.call(scope || this, o)){
25782                     data.push(o);
25783                 }
25784             }
25785             this.jsonData = data;
25786             this.refresh();
25787         }
25788     },
25789
25790 /**
25791  * Clears the current filter.
25792  */
25793     clearFilter : function(){
25794         if(this.snapshot && this.jsonData != this.snapshot){
25795             this.jsonData = this.snapshot;
25796             this.refresh();
25797         }
25798     },
25799
25800
25801 /**
25802  * Sorts the data for this view and refreshes it.
25803  * @param {String} property A property on your JSON objects to sort on
25804  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25805  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25806  */
25807     sort : function(property, dir, sortType){
25808         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25809         if(this.jsonData){
25810             var p = property;
25811             var dsc = dir && dir.toLowerCase() == "desc";
25812             var f = function(o1, o2){
25813                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25814                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25815                 ;
25816                 if(v1 < v2){
25817                     return dsc ? +1 : -1;
25818                 } else if(v1 > v2){
25819                     return dsc ? -1 : +1;
25820                 } else{
25821                     return 0;
25822                 }
25823             };
25824             this.jsonData.sort(f);
25825             this.refresh();
25826             if(this.jsonData != this.snapshot){
25827                 this.snapshot.sort(f);
25828             }
25829         }
25830     }
25831 });/*
25832  * Based on:
25833  * Ext JS Library 1.1.1
25834  * Copyright(c) 2006-2007, Ext JS, LLC.
25835  *
25836  * Originally Released Under LGPL - original licence link has changed is not relivant.
25837  *
25838  * Fork - LGPL
25839  * <script type="text/javascript">
25840  */
25841  
25842
25843 /**
25844  * @class Roo.ColorPalette
25845  * @extends Roo.Component
25846  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25847  * Here's an example of typical usage:
25848  * <pre><code>
25849 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25850 cp.render('my-div');
25851
25852 cp.on('select', function(palette, selColor){
25853     // do something with selColor
25854 });
25855 </code></pre>
25856  * @constructor
25857  * Create a new ColorPalette
25858  * @param {Object} config The config object
25859  */
25860 Roo.ColorPalette = function(config){
25861     Roo.ColorPalette.superclass.constructor.call(this, config);
25862     this.addEvents({
25863         /**
25864              * @event select
25865              * Fires when a color is selected
25866              * @param {ColorPalette} this
25867              * @param {String} color The 6-digit color hex code (without the # symbol)
25868              */
25869         select: true
25870     });
25871
25872     if(this.handler){
25873         this.on("select", this.handler, this.scope, true);
25874     }
25875 };
25876 Roo.extend(Roo.ColorPalette, Roo.Component, {
25877     /**
25878      * @cfg {String} itemCls
25879      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25880      */
25881     itemCls : "x-color-palette",
25882     /**
25883      * @cfg {String} value
25884      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25885      * the hex codes are case-sensitive.
25886      */
25887     value : null,
25888     clickEvent:'click',
25889     // private
25890     ctype: "Roo.ColorPalette",
25891
25892     /**
25893      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25894      */
25895     allowReselect : false,
25896
25897     /**
25898      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25899      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25900      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25901      * of colors with the width setting until the box is symmetrical.</p>
25902      * <p>You can override individual colors if needed:</p>
25903      * <pre><code>
25904 var cp = new Roo.ColorPalette();
25905 cp.colors[0] = "FF0000";  // change the first box to red
25906 </code></pre>
25907
25908 Or you can provide a custom array of your own for complete control:
25909 <pre><code>
25910 var cp = new Roo.ColorPalette();
25911 cp.colors = ["000000", "993300", "333300"];
25912 </code></pre>
25913      * @type Array
25914      */
25915     colors : [
25916         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25917         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25918         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25919         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25920         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25921     ],
25922
25923     // private
25924     onRender : function(container, position){
25925         var t = new Roo.MasterTemplate(
25926             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25927         );
25928         var c = this.colors;
25929         for(var i = 0, len = c.length; i < len; i++){
25930             t.add([c[i]]);
25931         }
25932         var el = document.createElement("div");
25933         el.className = this.itemCls;
25934         t.overwrite(el);
25935         container.dom.insertBefore(el, position);
25936         this.el = Roo.get(el);
25937         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25938         if(this.clickEvent != 'click'){
25939             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25940         }
25941     },
25942
25943     // private
25944     afterRender : function(){
25945         Roo.ColorPalette.superclass.afterRender.call(this);
25946         if(this.value){
25947             var s = this.value;
25948             this.value = null;
25949             this.select(s);
25950         }
25951     },
25952
25953     // private
25954     handleClick : function(e, t){
25955         e.preventDefault();
25956         if(!this.disabled){
25957             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25958             this.select(c.toUpperCase());
25959         }
25960     },
25961
25962     /**
25963      * Selects the specified color in the palette (fires the select event)
25964      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25965      */
25966     select : function(color){
25967         color = color.replace("#", "");
25968         if(color != this.value || this.allowReselect){
25969             var el = this.el;
25970             if(this.value){
25971                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25972             }
25973             el.child("a.color-"+color).addClass("x-color-palette-sel");
25974             this.value = color;
25975             this.fireEvent("select", this, color);
25976         }
25977     }
25978 });/*
25979  * Based on:
25980  * Ext JS Library 1.1.1
25981  * Copyright(c) 2006-2007, Ext JS, LLC.
25982  *
25983  * Originally Released Under LGPL - original licence link has changed is not relivant.
25984  *
25985  * Fork - LGPL
25986  * <script type="text/javascript">
25987  */
25988  
25989 /**
25990  * @class Roo.DatePicker
25991  * @extends Roo.Component
25992  * Simple date picker class.
25993  * @constructor
25994  * Create a new DatePicker
25995  * @param {Object} config The config object
25996  */
25997 Roo.DatePicker = function(config){
25998     Roo.DatePicker.superclass.constructor.call(this, config);
25999
26000     this.value = config && config.value ?
26001                  config.value.clearTime() : new Date().clearTime();
26002
26003     this.addEvents({
26004         /**
26005              * @event select
26006              * Fires when a date is selected
26007              * @param {DatePicker} this
26008              * @param {Date} date The selected date
26009              */
26010         'select': true,
26011         /**
26012              * @event monthchange
26013              * Fires when the displayed month changes 
26014              * @param {DatePicker} this
26015              * @param {Date} date The selected month
26016              */
26017         'monthchange': true
26018     });
26019
26020     if(this.handler){
26021         this.on("select", this.handler,  this.scope || this);
26022     }
26023     // build the disabledDatesRE
26024     if(!this.disabledDatesRE && this.disabledDates){
26025         var dd = this.disabledDates;
26026         var re = "(?:";
26027         for(var i = 0; i < dd.length; i++){
26028             re += dd[i];
26029             if(i != dd.length-1) re += "|";
26030         }
26031         this.disabledDatesRE = new RegExp(re + ")");
26032     }
26033 };
26034
26035 Roo.extend(Roo.DatePicker, Roo.Component, {
26036     /**
26037      * @cfg {String} todayText
26038      * The text to display on the button that selects the current date (defaults to "Today")
26039      */
26040     todayText : "Today",
26041     /**
26042      * @cfg {String} okText
26043      * The text to display on the ok button
26044      */
26045     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26046     /**
26047      * @cfg {String} cancelText
26048      * The text to display on the cancel button
26049      */
26050     cancelText : "Cancel",
26051     /**
26052      * @cfg {String} todayTip
26053      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26054      */
26055     todayTip : "{0} (Spacebar)",
26056     /**
26057      * @cfg {Date} minDate
26058      * Minimum allowable date (JavaScript date object, defaults to null)
26059      */
26060     minDate : null,
26061     /**
26062      * @cfg {Date} maxDate
26063      * Maximum allowable date (JavaScript date object, defaults to null)
26064      */
26065     maxDate : null,
26066     /**
26067      * @cfg {String} minText
26068      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26069      */
26070     minText : "This date is before the minimum date",
26071     /**
26072      * @cfg {String} maxText
26073      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26074      */
26075     maxText : "This date is after the maximum date",
26076     /**
26077      * @cfg {String} format
26078      * The default date format string which can be overriden for localization support.  The format must be
26079      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26080      */
26081     format : "m/d/y",
26082     /**
26083      * @cfg {Array} disabledDays
26084      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26085      */
26086     disabledDays : null,
26087     /**
26088      * @cfg {String} disabledDaysText
26089      * The tooltip to display when the date falls on a disabled day (defaults to "")
26090      */
26091     disabledDaysText : "",
26092     /**
26093      * @cfg {RegExp} disabledDatesRE
26094      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26095      */
26096     disabledDatesRE : null,
26097     /**
26098      * @cfg {String} disabledDatesText
26099      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26100      */
26101     disabledDatesText : "",
26102     /**
26103      * @cfg {Boolean} constrainToViewport
26104      * True to constrain the date picker to the viewport (defaults to true)
26105      */
26106     constrainToViewport : true,
26107     /**
26108      * @cfg {Array} monthNames
26109      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26110      */
26111     monthNames : Date.monthNames,
26112     /**
26113      * @cfg {Array} dayNames
26114      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26115      */
26116     dayNames : Date.dayNames,
26117     /**
26118      * @cfg {String} nextText
26119      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26120      */
26121     nextText: 'Next Month (Control+Right)',
26122     /**
26123      * @cfg {String} prevText
26124      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26125      */
26126     prevText: 'Previous Month (Control+Left)',
26127     /**
26128      * @cfg {String} monthYearText
26129      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26130      */
26131     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26132     /**
26133      * @cfg {Number} startDay
26134      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26135      */
26136     startDay : 0,
26137     /**
26138      * @cfg {Bool} showClear
26139      * Show a clear button (usefull for date form elements that can be blank.)
26140      */
26141     
26142     showClear: false,
26143     
26144     /**
26145      * Sets the value of the date field
26146      * @param {Date} value The date to set
26147      */
26148     setValue : function(value){
26149         var old = this.value;
26150         
26151         if (typeof(value) == 'string') {
26152          
26153             value = Date.parseDate(value, this.format);
26154         }
26155         if (!value) {
26156             value = new Date();
26157         }
26158         
26159         this.value = value.clearTime(true);
26160         if(this.el){
26161             this.update(this.value);
26162         }
26163     },
26164
26165     /**
26166      * Gets the current selected value of the date field
26167      * @return {Date} The selected date
26168      */
26169     getValue : function(){
26170         return this.value;
26171     },
26172
26173     // private
26174     focus : function(){
26175         if(this.el){
26176             this.update(this.activeDate);
26177         }
26178     },
26179
26180     // privateval
26181     onRender : function(container, position){
26182         
26183         var m = [
26184              '<table cellspacing="0">',
26185                 '<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>',
26186                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26187         var dn = this.dayNames;
26188         for(var i = 0; i < 7; i++){
26189             var d = this.startDay+i;
26190             if(d > 6){
26191                 d = d-7;
26192             }
26193             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26194         }
26195         m[m.length] = "</tr></thead><tbody><tr>";
26196         for(var i = 0; i < 42; i++) {
26197             if(i % 7 == 0 && i != 0){
26198                 m[m.length] = "</tr><tr>";
26199             }
26200             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26201         }
26202         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26203             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26204
26205         var el = document.createElement("div");
26206         el.className = "x-date-picker";
26207         el.innerHTML = m.join("");
26208
26209         container.dom.insertBefore(el, position);
26210
26211         this.el = Roo.get(el);
26212         this.eventEl = Roo.get(el.firstChild);
26213
26214         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26215             handler: this.showPrevMonth,
26216             scope: this,
26217             preventDefault:true,
26218             stopDefault:true
26219         });
26220
26221         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26222             handler: this.showNextMonth,
26223             scope: this,
26224             preventDefault:true,
26225             stopDefault:true
26226         });
26227
26228         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26229
26230         this.monthPicker = this.el.down('div.x-date-mp');
26231         this.monthPicker.enableDisplayMode('block');
26232         
26233         var kn = new Roo.KeyNav(this.eventEl, {
26234             "left" : function(e){
26235                 e.ctrlKey ?
26236                     this.showPrevMonth() :
26237                     this.update(this.activeDate.add("d", -1));
26238             },
26239
26240             "right" : function(e){
26241                 e.ctrlKey ?
26242                     this.showNextMonth() :
26243                     this.update(this.activeDate.add("d", 1));
26244             },
26245
26246             "up" : function(e){
26247                 e.ctrlKey ?
26248                     this.showNextYear() :
26249                     this.update(this.activeDate.add("d", -7));
26250             },
26251
26252             "down" : function(e){
26253                 e.ctrlKey ?
26254                     this.showPrevYear() :
26255                     this.update(this.activeDate.add("d", 7));
26256             },
26257
26258             "pageUp" : function(e){
26259                 this.showNextMonth();
26260             },
26261
26262             "pageDown" : function(e){
26263                 this.showPrevMonth();
26264             },
26265
26266             "enter" : function(e){
26267                 e.stopPropagation();
26268                 return true;
26269             },
26270
26271             scope : this
26272         });
26273
26274         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26275
26276         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26277
26278         this.el.unselectable();
26279         
26280         this.cells = this.el.select("table.x-date-inner tbody td");
26281         this.textNodes = this.el.query("table.x-date-inner tbody span");
26282
26283         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26284             text: "&#160;",
26285             tooltip: this.monthYearText
26286         });
26287
26288         this.mbtn.on('click', this.showMonthPicker, this);
26289         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26290
26291
26292         var today = (new Date()).dateFormat(this.format);
26293         
26294         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26295         if (this.showClear) {
26296             baseTb.add( new Roo.Toolbar.Fill());
26297         }
26298         baseTb.add({
26299             text: String.format(this.todayText, today),
26300             tooltip: String.format(this.todayTip, today),
26301             handler: this.selectToday,
26302             scope: this
26303         });
26304         
26305         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26306             
26307         //});
26308         if (this.showClear) {
26309             
26310             baseTb.add( new Roo.Toolbar.Fill());
26311             baseTb.add({
26312                 text: '&#160;',
26313                 cls: 'x-btn-icon x-btn-clear',
26314                 handler: function() {
26315                     //this.value = '';
26316                     this.fireEvent("select", this, '');
26317                 },
26318                 scope: this
26319             });
26320         }
26321         
26322         
26323         if(Roo.isIE){
26324             this.el.repaint();
26325         }
26326         this.update(this.value);
26327     },
26328
26329     createMonthPicker : function(){
26330         if(!this.monthPicker.dom.firstChild){
26331             var buf = ['<table border="0" cellspacing="0">'];
26332             for(var i = 0; i < 6; i++){
26333                 buf.push(
26334                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26335                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26336                     i == 0 ?
26337                     '<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>' :
26338                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26339                 );
26340             }
26341             buf.push(
26342                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26343                     this.okText,
26344                     '</button><button type="button" class="x-date-mp-cancel">',
26345                     this.cancelText,
26346                     '</button></td></tr>',
26347                 '</table>'
26348             );
26349             this.monthPicker.update(buf.join(''));
26350             this.monthPicker.on('click', this.onMonthClick, this);
26351             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26352
26353             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26354             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26355
26356             this.mpMonths.each(function(m, a, i){
26357                 i += 1;
26358                 if((i%2) == 0){
26359                     m.dom.xmonth = 5 + Math.round(i * .5);
26360                 }else{
26361                     m.dom.xmonth = Math.round((i-1) * .5);
26362                 }
26363             });
26364         }
26365     },
26366
26367     showMonthPicker : function(){
26368         this.createMonthPicker();
26369         var size = this.el.getSize();
26370         this.monthPicker.setSize(size);
26371         this.monthPicker.child('table').setSize(size);
26372
26373         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26374         this.updateMPMonth(this.mpSelMonth);
26375         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26376         this.updateMPYear(this.mpSelYear);
26377
26378         this.monthPicker.slideIn('t', {duration:.2});
26379     },
26380
26381     updateMPYear : function(y){
26382         this.mpyear = y;
26383         var ys = this.mpYears.elements;
26384         for(var i = 1; i <= 10; i++){
26385             var td = ys[i-1], y2;
26386             if((i%2) == 0){
26387                 y2 = y + Math.round(i * .5);
26388                 td.firstChild.innerHTML = y2;
26389                 td.xyear = y2;
26390             }else{
26391                 y2 = y - (5-Math.round(i * .5));
26392                 td.firstChild.innerHTML = y2;
26393                 td.xyear = y2;
26394             }
26395             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26396         }
26397     },
26398
26399     updateMPMonth : function(sm){
26400         this.mpMonths.each(function(m, a, i){
26401             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26402         });
26403     },
26404
26405     selectMPMonth: function(m){
26406         
26407     },
26408
26409     onMonthClick : function(e, t){
26410         e.stopEvent();
26411         var el = new Roo.Element(t), pn;
26412         if(el.is('button.x-date-mp-cancel')){
26413             this.hideMonthPicker();
26414         }
26415         else if(el.is('button.x-date-mp-ok')){
26416             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26417             this.hideMonthPicker();
26418         }
26419         else if(pn = el.up('td.x-date-mp-month', 2)){
26420             this.mpMonths.removeClass('x-date-mp-sel');
26421             pn.addClass('x-date-mp-sel');
26422             this.mpSelMonth = pn.dom.xmonth;
26423         }
26424         else if(pn = el.up('td.x-date-mp-year', 2)){
26425             this.mpYears.removeClass('x-date-mp-sel');
26426             pn.addClass('x-date-mp-sel');
26427             this.mpSelYear = pn.dom.xyear;
26428         }
26429         else if(el.is('a.x-date-mp-prev')){
26430             this.updateMPYear(this.mpyear-10);
26431         }
26432         else if(el.is('a.x-date-mp-next')){
26433             this.updateMPYear(this.mpyear+10);
26434         }
26435     },
26436
26437     onMonthDblClick : function(e, t){
26438         e.stopEvent();
26439         var el = new Roo.Element(t), pn;
26440         if(pn = el.up('td.x-date-mp-month', 2)){
26441             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26442             this.hideMonthPicker();
26443         }
26444         else if(pn = el.up('td.x-date-mp-year', 2)){
26445             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26446             this.hideMonthPicker();
26447         }
26448     },
26449
26450     hideMonthPicker : function(disableAnim){
26451         if(this.monthPicker){
26452             if(disableAnim === true){
26453                 this.monthPicker.hide();
26454             }else{
26455                 this.monthPicker.slideOut('t', {duration:.2});
26456             }
26457         }
26458     },
26459
26460     // private
26461     showPrevMonth : function(e){
26462         this.update(this.activeDate.add("mo", -1));
26463     },
26464
26465     // private
26466     showNextMonth : function(e){
26467         this.update(this.activeDate.add("mo", 1));
26468     },
26469
26470     // private
26471     showPrevYear : function(){
26472         this.update(this.activeDate.add("y", -1));
26473     },
26474
26475     // private
26476     showNextYear : function(){
26477         this.update(this.activeDate.add("y", 1));
26478     },
26479
26480     // private
26481     handleMouseWheel : function(e){
26482         var delta = e.getWheelDelta();
26483         if(delta > 0){
26484             this.showPrevMonth();
26485             e.stopEvent();
26486         } else if(delta < 0){
26487             this.showNextMonth();
26488             e.stopEvent();
26489         }
26490     },
26491
26492     // private
26493     handleDateClick : function(e, t){
26494         e.stopEvent();
26495         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26496             this.setValue(new Date(t.dateValue));
26497             this.fireEvent("select", this, this.value);
26498         }
26499     },
26500
26501     // private
26502     selectToday : function(){
26503         this.setValue(new Date().clearTime());
26504         this.fireEvent("select", this, this.value);
26505     },
26506
26507     // private
26508     update : function(date)
26509     {
26510         var vd = this.activeDate;
26511         this.activeDate = date;
26512         if(vd && this.el){
26513             var t = date.getTime();
26514             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26515                 this.cells.removeClass("x-date-selected");
26516                 this.cells.each(function(c){
26517                    if(c.dom.firstChild.dateValue == t){
26518                        c.addClass("x-date-selected");
26519                        setTimeout(function(){
26520                             try{c.dom.firstChild.focus();}catch(e){}
26521                        }, 50);
26522                        return false;
26523                    }
26524                 });
26525                 return;
26526             }
26527         }
26528         
26529         var days = date.getDaysInMonth();
26530         var firstOfMonth = date.getFirstDateOfMonth();
26531         var startingPos = firstOfMonth.getDay()-this.startDay;
26532
26533         if(startingPos <= this.startDay){
26534             startingPos += 7;
26535         }
26536
26537         var pm = date.add("mo", -1);
26538         var prevStart = pm.getDaysInMonth()-startingPos;
26539
26540         var cells = this.cells.elements;
26541         var textEls = this.textNodes;
26542         days += startingPos;
26543
26544         // convert everything to numbers so it's fast
26545         var day = 86400000;
26546         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26547         var today = new Date().clearTime().getTime();
26548         var sel = date.clearTime().getTime();
26549         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26550         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26551         var ddMatch = this.disabledDatesRE;
26552         var ddText = this.disabledDatesText;
26553         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26554         var ddaysText = this.disabledDaysText;
26555         var format = this.format;
26556
26557         var setCellClass = function(cal, cell){
26558             cell.title = "";
26559             var t = d.getTime();
26560             cell.firstChild.dateValue = t;
26561             if(t == today){
26562                 cell.className += " x-date-today";
26563                 cell.title = cal.todayText;
26564             }
26565             if(t == sel){
26566                 cell.className += " x-date-selected";
26567                 setTimeout(function(){
26568                     try{cell.firstChild.focus();}catch(e){}
26569                 }, 50);
26570             }
26571             // disabling
26572             if(t < min) {
26573                 cell.className = " x-date-disabled";
26574                 cell.title = cal.minText;
26575                 return;
26576             }
26577             if(t > max) {
26578                 cell.className = " x-date-disabled";
26579                 cell.title = cal.maxText;
26580                 return;
26581             }
26582             if(ddays){
26583                 if(ddays.indexOf(d.getDay()) != -1){
26584                     cell.title = ddaysText;
26585                     cell.className = " x-date-disabled";
26586                 }
26587             }
26588             if(ddMatch && format){
26589                 var fvalue = d.dateFormat(format);
26590                 if(ddMatch.test(fvalue)){
26591                     cell.title = ddText.replace("%0", fvalue);
26592                     cell.className = " x-date-disabled";
26593                 }
26594             }
26595         };
26596
26597         var i = 0;
26598         for(; i < startingPos; i++) {
26599             textEls[i].innerHTML = (++prevStart);
26600             d.setDate(d.getDate()+1);
26601             cells[i].className = "x-date-prevday";
26602             setCellClass(this, cells[i]);
26603         }
26604         for(; i < days; i++){
26605             intDay = i - startingPos + 1;
26606             textEls[i].innerHTML = (intDay);
26607             d.setDate(d.getDate()+1);
26608             cells[i].className = "x-date-active";
26609             setCellClass(this, cells[i]);
26610         }
26611         var extraDays = 0;
26612         for(; i < 42; i++) {
26613              textEls[i].innerHTML = (++extraDays);
26614              d.setDate(d.getDate()+1);
26615              cells[i].className = "x-date-nextday";
26616              setCellClass(this, cells[i]);
26617         }
26618
26619         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26620         this.fireEvent('monthchange', this, date);
26621         
26622         if(!this.internalRender){
26623             var main = this.el.dom.firstChild;
26624             var w = main.offsetWidth;
26625             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26626             Roo.fly(main).setWidth(w);
26627             this.internalRender = true;
26628             // opera does not respect the auto grow header center column
26629             // then, after it gets a width opera refuses to recalculate
26630             // without a second pass
26631             if(Roo.isOpera && !this.secondPass){
26632                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26633                 this.secondPass = true;
26634                 this.update.defer(10, this, [date]);
26635             }
26636         }
26637         
26638         
26639     }
26640 });        /*
26641  * Based on:
26642  * Ext JS Library 1.1.1
26643  * Copyright(c) 2006-2007, Ext JS, LLC.
26644  *
26645  * Originally Released Under LGPL - original licence link has changed is not relivant.
26646  *
26647  * Fork - LGPL
26648  * <script type="text/javascript">
26649  */
26650 /**
26651  * @class Roo.TabPanel
26652  * @extends Roo.util.Observable
26653  * A lightweight tab container.
26654  * <br><br>
26655  * Usage:
26656  * <pre><code>
26657 // basic tabs 1, built from existing content
26658 var tabs = new Roo.TabPanel("tabs1");
26659 tabs.addTab("script", "View Script");
26660 tabs.addTab("markup", "View Markup");
26661 tabs.activate("script");
26662
26663 // more advanced tabs, built from javascript
26664 var jtabs = new Roo.TabPanel("jtabs");
26665 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26666
26667 // set up the UpdateManager
26668 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26669 var updater = tab2.getUpdateManager();
26670 updater.setDefaultUrl("ajax1.htm");
26671 tab2.on('activate', updater.refresh, updater, true);
26672
26673 // Use setUrl for Ajax loading
26674 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26675 tab3.setUrl("ajax2.htm", null, true);
26676
26677 // Disabled tab
26678 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26679 tab4.disable();
26680
26681 jtabs.activate("jtabs-1");
26682  * </code></pre>
26683  * @constructor
26684  * Create a new TabPanel.
26685  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26686  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26687  */
26688 Roo.TabPanel = function(container, config){
26689     /**
26690     * The container element for this TabPanel.
26691     * @type Roo.Element
26692     */
26693     this.el = Roo.get(container, true);
26694     if(config){
26695         if(typeof config == "boolean"){
26696             this.tabPosition = config ? "bottom" : "top";
26697         }else{
26698             Roo.apply(this, config);
26699         }
26700     }
26701     if(this.tabPosition == "bottom"){
26702         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26703         this.el.addClass("x-tabs-bottom");
26704     }
26705     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26706     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26707     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26708     if(Roo.isIE){
26709         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26710     }
26711     if(this.tabPosition != "bottom"){
26712         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26713          * @type Roo.Element
26714          */
26715         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26716         this.el.addClass("x-tabs-top");
26717     }
26718     this.items = [];
26719
26720     this.bodyEl.setStyle("position", "relative");
26721
26722     this.active = null;
26723     this.activateDelegate = this.activate.createDelegate(this);
26724
26725     this.addEvents({
26726         /**
26727          * @event tabchange
26728          * Fires when the active tab changes
26729          * @param {Roo.TabPanel} this
26730          * @param {Roo.TabPanelItem} activePanel The new active tab
26731          */
26732         "tabchange": true,
26733         /**
26734          * @event beforetabchange
26735          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26736          * @param {Roo.TabPanel} this
26737          * @param {Object} e Set cancel to true on this object to cancel the tab change
26738          * @param {Roo.TabPanelItem} tab The tab being changed to
26739          */
26740         "beforetabchange" : true
26741     });
26742
26743     Roo.EventManager.onWindowResize(this.onResize, this);
26744     this.cpad = this.el.getPadding("lr");
26745     this.hiddenCount = 0;
26746
26747
26748     // toolbar on the tabbar support...
26749     if (this.toolbar) {
26750         var tcfg = this.toolbar;
26751         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26752         this.toolbar = new Roo.Toolbar(tcfg);
26753         if (Roo.isSafari) {
26754             var tbl = tcfg.container.child('table', true);
26755             tbl.setAttribute('width', '100%');
26756         }
26757         
26758     }
26759    
26760
26761
26762     Roo.TabPanel.superclass.constructor.call(this);
26763 };
26764
26765 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26766     /*
26767      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26768      */
26769     tabPosition : "top",
26770     /*
26771      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26772      */
26773     currentTabWidth : 0,
26774     /*
26775      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26776      */
26777     minTabWidth : 40,
26778     /*
26779      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26780      */
26781     maxTabWidth : 250,
26782     /*
26783      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26784      */
26785     preferredTabWidth : 175,
26786     /*
26787      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26788      */
26789     resizeTabs : false,
26790     /*
26791      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26792      */
26793     monitorResize : true,
26794     /*
26795      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26796      */
26797     toolbar : false,
26798
26799     /**
26800      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26801      * @param {String} id The id of the div to use <b>or create</b>
26802      * @param {String} text The text for the tab
26803      * @param {String} content (optional) Content to put in the TabPanelItem body
26804      * @param {Boolean} closable (optional) True to create a close icon on the tab
26805      * @return {Roo.TabPanelItem} The created TabPanelItem
26806      */
26807     addTab : function(id, text, content, closable){
26808         var item = new Roo.TabPanelItem(this, id, text, closable);
26809         this.addTabItem(item);
26810         if(content){
26811             item.setContent(content);
26812         }
26813         return item;
26814     },
26815
26816     /**
26817      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26818      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26819      * @return {Roo.TabPanelItem}
26820      */
26821     getTab : function(id){
26822         return this.items[id];
26823     },
26824
26825     /**
26826      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26827      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26828      */
26829     hideTab : function(id){
26830         var t = this.items[id];
26831         if(!t.isHidden()){
26832            t.setHidden(true);
26833            this.hiddenCount++;
26834            this.autoSizeTabs();
26835         }
26836     },
26837
26838     /**
26839      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26840      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26841      */
26842     unhideTab : function(id){
26843         var t = this.items[id];
26844         if(t.isHidden()){
26845            t.setHidden(false);
26846            this.hiddenCount--;
26847            this.autoSizeTabs();
26848         }
26849     },
26850
26851     /**
26852      * Adds an existing {@link Roo.TabPanelItem}.
26853      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26854      */
26855     addTabItem : function(item){
26856         this.items[item.id] = item;
26857         this.items.push(item);
26858         if(this.resizeTabs){
26859            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26860            this.autoSizeTabs();
26861         }else{
26862             item.autoSize();
26863         }
26864     },
26865
26866     /**
26867      * Removes a {@link Roo.TabPanelItem}.
26868      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26869      */
26870     removeTab : function(id){
26871         var items = this.items;
26872         var tab = items[id];
26873         if(!tab) { return; }
26874         var index = items.indexOf(tab);
26875         if(this.active == tab && items.length > 1){
26876             var newTab = this.getNextAvailable(index);
26877             if(newTab) {
26878                 newTab.activate();
26879             }
26880         }
26881         this.stripEl.dom.removeChild(tab.pnode.dom);
26882         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26883             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26884         }
26885         items.splice(index, 1);
26886         delete this.items[tab.id];
26887         tab.fireEvent("close", tab);
26888         tab.purgeListeners();
26889         this.autoSizeTabs();
26890     },
26891
26892     getNextAvailable : function(start){
26893         var items = this.items;
26894         var index = start;
26895         // look for a next tab that will slide over to
26896         // replace the one being removed
26897         while(index < items.length){
26898             var item = items[++index];
26899             if(item && !item.isHidden()){
26900                 return item;
26901             }
26902         }
26903         // if one isn't found select the previous tab (on the left)
26904         index = start;
26905         while(index >= 0){
26906             var item = items[--index];
26907             if(item && !item.isHidden()){
26908                 return item;
26909             }
26910         }
26911         return null;
26912     },
26913
26914     /**
26915      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26916      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26917      */
26918     disableTab : function(id){
26919         var tab = this.items[id];
26920         if(tab && this.active != tab){
26921             tab.disable();
26922         }
26923     },
26924
26925     /**
26926      * Enables a {@link Roo.TabPanelItem} that is disabled.
26927      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26928      */
26929     enableTab : function(id){
26930         var tab = this.items[id];
26931         tab.enable();
26932     },
26933
26934     /**
26935      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26936      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26937      * @return {Roo.TabPanelItem} The TabPanelItem.
26938      */
26939     activate : function(id){
26940         var tab = this.items[id];
26941         if(!tab){
26942             return null;
26943         }
26944         if(tab == this.active || tab.disabled){
26945             return tab;
26946         }
26947         var e = {};
26948         this.fireEvent("beforetabchange", this, e, tab);
26949         if(e.cancel !== true && !tab.disabled){
26950             if(this.active){
26951                 this.active.hide();
26952             }
26953             this.active = this.items[id];
26954             this.active.show();
26955             this.fireEvent("tabchange", this, this.active);
26956         }
26957         return tab;
26958     },
26959
26960     /**
26961      * Gets the active {@link Roo.TabPanelItem}.
26962      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26963      */
26964     getActiveTab : function(){
26965         return this.active;
26966     },
26967
26968     /**
26969      * Updates the tab body element to fit the height of the container element
26970      * for overflow scrolling
26971      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26972      */
26973     syncHeight : function(targetHeight){
26974         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26975         var bm = this.bodyEl.getMargins();
26976         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26977         this.bodyEl.setHeight(newHeight);
26978         return newHeight;
26979     },
26980
26981     onResize : function(){
26982         if(this.monitorResize){
26983             this.autoSizeTabs();
26984         }
26985     },
26986
26987     /**
26988      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26989      */
26990     beginUpdate : function(){
26991         this.updating = true;
26992     },
26993
26994     /**
26995      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26996      */
26997     endUpdate : function(){
26998         this.updating = false;
26999         this.autoSizeTabs();
27000     },
27001
27002     /**
27003      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27004      */
27005     autoSizeTabs : function(){
27006         var count = this.items.length;
27007         var vcount = count - this.hiddenCount;
27008         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27009         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27010         var availWidth = Math.floor(w / vcount);
27011         var b = this.stripBody;
27012         if(b.getWidth() > w){
27013             var tabs = this.items;
27014             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27015             if(availWidth < this.minTabWidth){
27016                 /*if(!this.sleft){    // incomplete scrolling code
27017                     this.createScrollButtons();
27018                 }
27019                 this.showScroll();
27020                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27021             }
27022         }else{
27023             if(this.currentTabWidth < this.preferredTabWidth){
27024                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27025             }
27026         }
27027     },
27028
27029     /**
27030      * Returns the number of tabs in this TabPanel.
27031      * @return {Number}
27032      */
27033      getCount : function(){
27034          return this.items.length;
27035      },
27036
27037     /**
27038      * Resizes all the tabs to the passed width
27039      * @param {Number} The new width
27040      */
27041     setTabWidth : function(width){
27042         this.currentTabWidth = width;
27043         for(var i = 0, len = this.items.length; i < len; i++) {
27044                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27045         }
27046     },
27047
27048     /**
27049      * Destroys this TabPanel
27050      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27051      */
27052     destroy : function(removeEl){
27053         Roo.EventManager.removeResizeListener(this.onResize, this);
27054         for(var i = 0, len = this.items.length; i < len; i++){
27055             this.items[i].purgeListeners();
27056         }
27057         if(removeEl === true){
27058             this.el.update("");
27059             this.el.remove();
27060         }
27061     }
27062 });
27063
27064 /**
27065  * @class Roo.TabPanelItem
27066  * @extends Roo.util.Observable
27067  * Represents an individual item (tab plus body) in a TabPanel.
27068  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27069  * @param {String} id The id of this TabPanelItem
27070  * @param {String} text The text for the tab of this TabPanelItem
27071  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27072  */
27073 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27074     /**
27075      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27076      * @type Roo.TabPanel
27077      */
27078     this.tabPanel = tabPanel;
27079     /**
27080      * The id for this TabPanelItem
27081      * @type String
27082      */
27083     this.id = id;
27084     /** @private */
27085     this.disabled = false;
27086     /** @private */
27087     this.text = text;
27088     /** @private */
27089     this.loaded = false;
27090     this.closable = closable;
27091
27092     /**
27093      * The body element for this TabPanelItem.
27094      * @type Roo.Element
27095      */
27096     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27097     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27098     this.bodyEl.setStyle("display", "block");
27099     this.bodyEl.setStyle("zoom", "1");
27100     this.hideAction();
27101
27102     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27103     /** @private */
27104     this.el = Roo.get(els.el, true);
27105     this.inner = Roo.get(els.inner, true);
27106     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27107     this.pnode = Roo.get(els.el.parentNode, true);
27108     this.el.on("mousedown", this.onTabMouseDown, this);
27109     this.el.on("click", this.onTabClick, this);
27110     /** @private */
27111     if(closable){
27112         var c = Roo.get(els.close, true);
27113         c.dom.title = this.closeText;
27114         c.addClassOnOver("close-over");
27115         c.on("click", this.closeClick, this);
27116      }
27117
27118     this.addEvents({
27119          /**
27120          * @event activate
27121          * Fires when this tab becomes the active tab.
27122          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27123          * @param {Roo.TabPanelItem} this
27124          */
27125         "activate": true,
27126         /**
27127          * @event beforeclose
27128          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27129          * @param {Roo.TabPanelItem} this
27130          * @param {Object} e Set cancel to true on this object to cancel the close.
27131          */
27132         "beforeclose": true,
27133         /**
27134          * @event close
27135          * Fires when this tab is closed.
27136          * @param {Roo.TabPanelItem} this
27137          */
27138          "close": true,
27139         /**
27140          * @event deactivate
27141          * Fires when this tab is no longer the active tab.
27142          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27143          * @param {Roo.TabPanelItem} this
27144          */
27145          "deactivate" : true
27146     });
27147     this.hidden = false;
27148
27149     Roo.TabPanelItem.superclass.constructor.call(this);
27150 };
27151
27152 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27153     purgeListeners : function(){
27154        Roo.util.Observable.prototype.purgeListeners.call(this);
27155        this.el.removeAllListeners();
27156     },
27157     /**
27158      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27159      */
27160     show : function(){
27161         this.pnode.addClass("on");
27162         this.showAction();
27163         if(Roo.isOpera){
27164             this.tabPanel.stripWrap.repaint();
27165         }
27166         this.fireEvent("activate", this.tabPanel, this);
27167     },
27168
27169     /**
27170      * Returns true if this tab is the active tab.
27171      * @return {Boolean}
27172      */
27173     isActive : function(){
27174         return this.tabPanel.getActiveTab() == this;
27175     },
27176
27177     /**
27178      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27179      */
27180     hide : function(){
27181         this.pnode.removeClass("on");
27182         this.hideAction();
27183         this.fireEvent("deactivate", this.tabPanel, this);
27184     },
27185
27186     hideAction : function(){
27187         this.bodyEl.hide();
27188         this.bodyEl.setStyle("position", "absolute");
27189         this.bodyEl.setLeft("-20000px");
27190         this.bodyEl.setTop("-20000px");
27191     },
27192
27193     showAction : function(){
27194         this.bodyEl.setStyle("position", "relative");
27195         this.bodyEl.setTop("");
27196         this.bodyEl.setLeft("");
27197         this.bodyEl.show();
27198     },
27199
27200     /**
27201      * Set the tooltip for the tab.
27202      * @param {String} tooltip The tab's tooltip
27203      */
27204     setTooltip : function(text){
27205         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27206             this.textEl.dom.qtip = text;
27207             this.textEl.dom.removeAttribute('title');
27208         }else{
27209             this.textEl.dom.title = text;
27210         }
27211     },
27212
27213     onTabClick : function(e){
27214         e.preventDefault();
27215         this.tabPanel.activate(this.id);
27216     },
27217
27218     onTabMouseDown : function(e){
27219         e.preventDefault();
27220         this.tabPanel.activate(this.id);
27221     },
27222
27223     getWidth : function(){
27224         return this.inner.getWidth();
27225     },
27226
27227     setWidth : function(width){
27228         var iwidth = width - this.pnode.getPadding("lr");
27229         this.inner.setWidth(iwidth);
27230         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27231         this.pnode.setWidth(width);
27232     },
27233
27234     /**
27235      * Show or hide the tab
27236      * @param {Boolean} hidden True to hide or false to show.
27237      */
27238     setHidden : function(hidden){
27239         this.hidden = hidden;
27240         this.pnode.setStyle("display", hidden ? "none" : "");
27241     },
27242
27243     /**
27244      * Returns true if this tab is "hidden"
27245      * @return {Boolean}
27246      */
27247     isHidden : function(){
27248         return this.hidden;
27249     },
27250
27251     /**
27252      * Returns the text for this tab
27253      * @return {String}
27254      */
27255     getText : function(){
27256         return this.text;
27257     },
27258
27259     autoSize : function(){
27260         //this.el.beginMeasure();
27261         this.textEl.setWidth(1);
27262         /*
27263          *  #2804 [new] Tabs in Roojs
27264          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27265          */
27266         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27267         //this.el.endMeasure();
27268     },
27269
27270     /**
27271      * Sets the text for the tab (Note: this also sets the tooltip text)
27272      * @param {String} text The tab's text and tooltip
27273      */
27274     setText : function(text){
27275         this.text = text;
27276         this.textEl.update(text);
27277         this.setTooltip(text);
27278         if(!this.tabPanel.resizeTabs){
27279             this.autoSize();
27280         }
27281     },
27282     /**
27283      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27284      */
27285     activate : function(){
27286         this.tabPanel.activate(this.id);
27287     },
27288
27289     /**
27290      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27291      */
27292     disable : function(){
27293         if(this.tabPanel.active != this){
27294             this.disabled = true;
27295             this.pnode.addClass("disabled");
27296         }
27297     },
27298
27299     /**
27300      * Enables this TabPanelItem if it was previously disabled.
27301      */
27302     enable : function(){
27303         this.disabled = false;
27304         this.pnode.removeClass("disabled");
27305     },
27306
27307     /**
27308      * Sets the content for this TabPanelItem.
27309      * @param {String} content The content
27310      * @param {Boolean} loadScripts true to look for and load scripts
27311      */
27312     setContent : function(content, loadScripts){
27313         this.bodyEl.update(content, loadScripts);
27314     },
27315
27316     /**
27317      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27318      * @return {Roo.UpdateManager} The UpdateManager
27319      */
27320     getUpdateManager : function(){
27321         return this.bodyEl.getUpdateManager();
27322     },
27323
27324     /**
27325      * Set a URL to be used to load the content for this TabPanelItem.
27326      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27327      * @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)
27328      * @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)
27329      * @return {Roo.UpdateManager} The UpdateManager
27330      */
27331     setUrl : function(url, params, loadOnce){
27332         if(this.refreshDelegate){
27333             this.un('activate', this.refreshDelegate);
27334         }
27335         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27336         this.on("activate", this.refreshDelegate);
27337         return this.bodyEl.getUpdateManager();
27338     },
27339
27340     /** @private */
27341     _handleRefresh : function(url, params, loadOnce){
27342         if(!loadOnce || !this.loaded){
27343             var updater = this.bodyEl.getUpdateManager();
27344             updater.update(url, params, this._setLoaded.createDelegate(this));
27345         }
27346     },
27347
27348     /**
27349      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27350      *   Will fail silently if the setUrl method has not been called.
27351      *   This does not activate the panel, just updates its content.
27352      */
27353     refresh : function(){
27354         if(this.refreshDelegate){
27355            this.loaded = false;
27356            this.refreshDelegate();
27357         }
27358     },
27359
27360     /** @private */
27361     _setLoaded : function(){
27362         this.loaded = true;
27363     },
27364
27365     /** @private */
27366     closeClick : function(e){
27367         var o = {};
27368         e.stopEvent();
27369         this.fireEvent("beforeclose", this, o);
27370         if(o.cancel !== true){
27371             this.tabPanel.removeTab(this.id);
27372         }
27373     },
27374     /**
27375      * The text displayed in the tooltip for the close icon.
27376      * @type String
27377      */
27378     closeText : "Close this tab"
27379 });
27380
27381 /** @private */
27382 Roo.TabPanel.prototype.createStrip = function(container){
27383     var strip = document.createElement("div");
27384     strip.className = "x-tabs-wrap";
27385     container.appendChild(strip);
27386     return strip;
27387 };
27388 /** @private */
27389 Roo.TabPanel.prototype.createStripList = function(strip){
27390     // div wrapper for retard IE
27391     // returns the "tr" element.
27392     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27393         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27394         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27395     return strip.firstChild.firstChild.firstChild.firstChild;
27396 };
27397 /** @private */
27398 Roo.TabPanel.prototype.createBody = function(container){
27399     var body = document.createElement("div");
27400     Roo.id(body, "tab-body");
27401     Roo.fly(body).addClass("x-tabs-body");
27402     container.appendChild(body);
27403     return body;
27404 };
27405 /** @private */
27406 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27407     var body = Roo.getDom(id);
27408     if(!body){
27409         body = document.createElement("div");
27410         body.id = id;
27411     }
27412     Roo.fly(body).addClass("x-tabs-item-body");
27413     bodyEl.insertBefore(body, bodyEl.firstChild);
27414     return body;
27415 };
27416 /** @private */
27417 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27418     var td = document.createElement("td");
27419     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27420     //stripEl.appendChild(td);
27421     if(closable){
27422         td.className = "x-tabs-closable";
27423         if(!this.closeTpl){
27424             this.closeTpl = new Roo.Template(
27425                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27426                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27427                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27428             );
27429         }
27430         var el = this.closeTpl.overwrite(td, {"text": text});
27431         var close = el.getElementsByTagName("div")[0];
27432         var inner = el.getElementsByTagName("em")[0];
27433         return {"el": el, "close": close, "inner": inner};
27434     } else {
27435         if(!this.tabTpl){
27436             this.tabTpl = new Roo.Template(
27437                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27438                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27439             );
27440         }
27441         var el = this.tabTpl.overwrite(td, {"text": text});
27442         var inner = el.getElementsByTagName("em")[0];
27443         return {"el": el, "inner": inner};
27444     }
27445 };/*
27446  * Based on:
27447  * Ext JS Library 1.1.1
27448  * Copyright(c) 2006-2007, Ext JS, LLC.
27449  *
27450  * Originally Released Under LGPL - original licence link has changed is not relivant.
27451  *
27452  * Fork - LGPL
27453  * <script type="text/javascript">
27454  */
27455
27456 /**
27457  * @class Roo.Button
27458  * @extends Roo.util.Observable
27459  * Simple Button class
27460  * @cfg {String} text The button text
27461  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27462  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27463  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27464  * @cfg {Object} scope The scope of the handler
27465  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27466  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27467  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27468  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27469  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27470  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27471    applies if enableToggle = true)
27472  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27473  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27474   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27475  * @constructor
27476  * Create a new button
27477  * @param {Object} config The config object
27478  */
27479 Roo.Button = function(renderTo, config)
27480 {
27481     if (!config) {
27482         config = renderTo;
27483         renderTo = config.renderTo || false;
27484     }
27485     
27486     Roo.apply(this, config);
27487     this.addEvents({
27488         /**
27489              * @event click
27490              * Fires when this button is clicked
27491              * @param {Button} this
27492              * @param {EventObject} e The click event
27493              */
27494             "click" : true,
27495         /**
27496              * @event toggle
27497              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27498              * @param {Button} this
27499              * @param {Boolean} pressed
27500              */
27501             "toggle" : true,
27502         /**
27503              * @event mouseover
27504              * Fires when the mouse hovers over the button
27505              * @param {Button} this
27506              * @param {Event} e The event object
27507              */
27508         'mouseover' : true,
27509         /**
27510              * @event mouseout
27511              * Fires when the mouse exits the button
27512              * @param {Button} this
27513              * @param {Event} e The event object
27514              */
27515         'mouseout': true,
27516          /**
27517              * @event render
27518              * Fires when the button is rendered
27519              * @param {Button} this
27520              */
27521         'render': true
27522     });
27523     if(this.menu){
27524         this.menu = Roo.menu.MenuMgr.get(this.menu);
27525     }
27526     // register listeners first!!  - so render can be captured..
27527     Roo.util.Observable.call(this);
27528     if(renderTo){
27529         this.render(renderTo);
27530     }
27531     
27532   
27533 };
27534
27535 Roo.extend(Roo.Button, Roo.util.Observable, {
27536     /**
27537      * 
27538      */
27539     
27540     /**
27541      * Read-only. True if this button is hidden
27542      * @type Boolean
27543      */
27544     hidden : false,
27545     /**
27546      * Read-only. True if this button is disabled
27547      * @type Boolean
27548      */
27549     disabled : false,
27550     /**
27551      * Read-only. True if this button is pressed (only if enableToggle = true)
27552      * @type Boolean
27553      */
27554     pressed : false,
27555
27556     /**
27557      * @cfg {Number} tabIndex 
27558      * The DOM tabIndex for this button (defaults to undefined)
27559      */
27560     tabIndex : undefined,
27561
27562     /**
27563      * @cfg {Boolean} enableToggle
27564      * True to enable pressed/not pressed toggling (defaults to false)
27565      */
27566     enableToggle: false,
27567     /**
27568      * @cfg {Mixed} menu
27569      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27570      */
27571     menu : undefined,
27572     /**
27573      * @cfg {String} menuAlign
27574      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27575      */
27576     menuAlign : "tl-bl?",
27577
27578     /**
27579      * @cfg {String} iconCls
27580      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27581      */
27582     iconCls : undefined,
27583     /**
27584      * @cfg {String} type
27585      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27586      */
27587     type : 'button',
27588
27589     // private
27590     menuClassTarget: 'tr',
27591
27592     /**
27593      * @cfg {String} clickEvent
27594      * The type of event to map to the button's event handler (defaults to 'click')
27595      */
27596     clickEvent : 'click',
27597
27598     /**
27599      * @cfg {Boolean} handleMouseEvents
27600      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27601      */
27602     handleMouseEvents : true,
27603
27604     /**
27605      * @cfg {String} tooltipType
27606      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27607      */
27608     tooltipType : 'qtip',
27609
27610     /**
27611      * @cfg {String} cls
27612      * A CSS class to apply to the button's main element.
27613      */
27614     
27615     /**
27616      * @cfg {Roo.Template} template (Optional)
27617      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27618      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27619      * require code modifications if required elements (e.g. a button) aren't present.
27620      */
27621
27622     // private
27623     render : function(renderTo){
27624         var btn;
27625         if(this.hideParent){
27626             this.parentEl = Roo.get(renderTo);
27627         }
27628         if(!this.dhconfig){
27629             if(!this.template){
27630                 if(!Roo.Button.buttonTemplate){
27631                     // hideous table template
27632                     Roo.Button.buttonTemplate = new Roo.Template(
27633                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27634                         '<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>',
27635                         "</tr></tbody></table>");
27636                 }
27637                 this.template = Roo.Button.buttonTemplate;
27638             }
27639             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27640             var btnEl = btn.child("button:first");
27641             btnEl.on('focus', this.onFocus, this);
27642             btnEl.on('blur', this.onBlur, this);
27643             if(this.cls){
27644                 btn.addClass(this.cls);
27645             }
27646             if(this.icon){
27647                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27648             }
27649             if(this.iconCls){
27650                 btnEl.addClass(this.iconCls);
27651                 if(!this.cls){
27652                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27653                 }
27654             }
27655             if(this.tabIndex !== undefined){
27656                 btnEl.dom.tabIndex = this.tabIndex;
27657             }
27658             if(this.tooltip){
27659                 if(typeof this.tooltip == 'object'){
27660                     Roo.QuickTips.tips(Roo.apply({
27661                           target: btnEl.id
27662                     }, this.tooltip));
27663                 } else {
27664                     btnEl.dom[this.tooltipType] = this.tooltip;
27665                 }
27666             }
27667         }else{
27668             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27669         }
27670         this.el = btn;
27671         if(this.id){
27672             this.el.dom.id = this.el.id = this.id;
27673         }
27674         if(this.menu){
27675             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27676             this.menu.on("show", this.onMenuShow, this);
27677             this.menu.on("hide", this.onMenuHide, this);
27678         }
27679         btn.addClass("x-btn");
27680         if(Roo.isIE && !Roo.isIE7){
27681             this.autoWidth.defer(1, this);
27682         }else{
27683             this.autoWidth();
27684         }
27685         if(this.handleMouseEvents){
27686             btn.on("mouseover", this.onMouseOver, this);
27687             btn.on("mouseout", this.onMouseOut, this);
27688             btn.on("mousedown", this.onMouseDown, this);
27689         }
27690         btn.on(this.clickEvent, this.onClick, this);
27691         //btn.on("mouseup", this.onMouseUp, this);
27692         if(this.hidden){
27693             this.hide();
27694         }
27695         if(this.disabled){
27696             this.disable();
27697         }
27698         Roo.ButtonToggleMgr.register(this);
27699         if(this.pressed){
27700             this.el.addClass("x-btn-pressed");
27701         }
27702         if(this.repeat){
27703             var repeater = new Roo.util.ClickRepeater(btn,
27704                 typeof this.repeat == "object" ? this.repeat : {}
27705             );
27706             repeater.on("click", this.onClick,  this);
27707         }
27708         
27709         this.fireEvent('render', this);
27710         
27711     },
27712     /**
27713      * Returns the button's underlying element
27714      * @return {Roo.Element} The element
27715      */
27716     getEl : function(){
27717         return this.el;  
27718     },
27719     
27720     /**
27721      * Destroys this Button and removes any listeners.
27722      */
27723     destroy : function(){
27724         Roo.ButtonToggleMgr.unregister(this);
27725         this.el.removeAllListeners();
27726         this.purgeListeners();
27727         this.el.remove();
27728     },
27729
27730     // private
27731     autoWidth : function(){
27732         if(this.el){
27733             this.el.setWidth("auto");
27734             if(Roo.isIE7 && Roo.isStrict){
27735                 var ib = this.el.child('button');
27736                 if(ib && ib.getWidth() > 20){
27737                     ib.clip();
27738                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27739                 }
27740             }
27741             if(this.minWidth){
27742                 if(this.hidden){
27743                     this.el.beginMeasure();
27744                 }
27745                 if(this.el.getWidth() < this.minWidth){
27746                     this.el.setWidth(this.minWidth);
27747                 }
27748                 if(this.hidden){
27749                     this.el.endMeasure();
27750                 }
27751             }
27752         }
27753     },
27754
27755     /**
27756      * Assigns this button's click handler
27757      * @param {Function} handler The function to call when the button is clicked
27758      * @param {Object} scope (optional) Scope for the function passed in
27759      */
27760     setHandler : function(handler, scope){
27761         this.handler = handler;
27762         this.scope = scope;  
27763     },
27764     
27765     /**
27766      * Sets this button's text
27767      * @param {String} text The button text
27768      */
27769     setText : function(text){
27770         this.text = text;
27771         if(this.el){
27772             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27773         }
27774         this.autoWidth();
27775     },
27776     
27777     /**
27778      * Gets the text for this button
27779      * @return {String} The button text
27780      */
27781     getText : function(){
27782         return this.text;  
27783     },
27784     
27785     /**
27786      * Show this button
27787      */
27788     show: function(){
27789         this.hidden = false;
27790         if(this.el){
27791             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27792         }
27793     },
27794     
27795     /**
27796      * Hide this button
27797      */
27798     hide: function(){
27799         this.hidden = true;
27800         if(this.el){
27801             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27802         }
27803     },
27804     
27805     /**
27806      * Convenience function for boolean show/hide
27807      * @param {Boolean} visible True to show, false to hide
27808      */
27809     setVisible: function(visible){
27810         if(visible) {
27811             this.show();
27812         }else{
27813             this.hide();
27814         }
27815     },
27816     
27817     /**
27818      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27819      * @param {Boolean} state (optional) Force a particular state
27820      */
27821     toggle : function(state){
27822         state = state === undefined ? !this.pressed : state;
27823         if(state != this.pressed){
27824             if(state){
27825                 this.el.addClass("x-btn-pressed");
27826                 this.pressed = true;
27827                 this.fireEvent("toggle", this, true);
27828             }else{
27829                 this.el.removeClass("x-btn-pressed");
27830                 this.pressed = false;
27831                 this.fireEvent("toggle", this, false);
27832             }
27833             if(this.toggleHandler){
27834                 this.toggleHandler.call(this.scope || this, this, state);
27835             }
27836         }
27837     },
27838     
27839     /**
27840      * Focus the button
27841      */
27842     focus : function(){
27843         this.el.child('button:first').focus();
27844     },
27845     
27846     /**
27847      * Disable this button
27848      */
27849     disable : function(){
27850         if(this.el){
27851             this.el.addClass("x-btn-disabled");
27852         }
27853         this.disabled = true;
27854     },
27855     
27856     /**
27857      * Enable this button
27858      */
27859     enable : function(){
27860         if(this.el){
27861             this.el.removeClass("x-btn-disabled");
27862         }
27863         this.disabled = false;
27864     },
27865
27866     /**
27867      * Convenience function for boolean enable/disable
27868      * @param {Boolean} enabled True to enable, false to disable
27869      */
27870     setDisabled : function(v){
27871         this[v !== true ? "enable" : "disable"]();
27872     },
27873
27874     // private
27875     onClick : function(e)
27876     {
27877         if(e){
27878             e.preventDefault();
27879         }
27880         if(e.button != 0){
27881             return;
27882         }
27883         if(!this.disabled){
27884             if(this.enableToggle){
27885                 this.toggle();
27886             }
27887             if(this.menu && !this.menu.isVisible()){
27888                 this.menu.show(this.el, this.menuAlign);
27889             }
27890             this.fireEvent("click", this, e);
27891             if(this.handler){
27892                 this.el.removeClass("x-btn-over");
27893                 this.handler.call(this.scope || this, this, e);
27894             }
27895         }
27896     },
27897     // private
27898     onMouseOver : function(e){
27899         if(!this.disabled){
27900             this.el.addClass("x-btn-over");
27901             this.fireEvent('mouseover', this, e);
27902         }
27903     },
27904     // private
27905     onMouseOut : function(e){
27906         if(!e.within(this.el,  true)){
27907             this.el.removeClass("x-btn-over");
27908             this.fireEvent('mouseout', this, e);
27909         }
27910     },
27911     // private
27912     onFocus : function(e){
27913         if(!this.disabled){
27914             this.el.addClass("x-btn-focus");
27915         }
27916     },
27917     // private
27918     onBlur : function(e){
27919         this.el.removeClass("x-btn-focus");
27920     },
27921     // private
27922     onMouseDown : function(e){
27923         if(!this.disabled && e.button == 0){
27924             this.el.addClass("x-btn-click");
27925             Roo.get(document).on('mouseup', this.onMouseUp, this);
27926         }
27927     },
27928     // private
27929     onMouseUp : function(e){
27930         if(e.button == 0){
27931             this.el.removeClass("x-btn-click");
27932             Roo.get(document).un('mouseup', this.onMouseUp, this);
27933         }
27934     },
27935     // private
27936     onMenuShow : function(e){
27937         this.el.addClass("x-btn-menu-active");
27938     },
27939     // private
27940     onMenuHide : function(e){
27941         this.el.removeClass("x-btn-menu-active");
27942     }   
27943 });
27944
27945 // Private utility class used by Button
27946 Roo.ButtonToggleMgr = function(){
27947    var groups = {};
27948    
27949    function toggleGroup(btn, state){
27950        if(state){
27951            var g = groups[btn.toggleGroup];
27952            for(var i = 0, l = g.length; i < l; i++){
27953                if(g[i] != btn){
27954                    g[i].toggle(false);
27955                }
27956            }
27957        }
27958    }
27959    
27960    return {
27961        register : function(btn){
27962            if(!btn.toggleGroup){
27963                return;
27964            }
27965            var g = groups[btn.toggleGroup];
27966            if(!g){
27967                g = groups[btn.toggleGroup] = [];
27968            }
27969            g.push(btn);
27970            btn.on("toggle", toggleGroup);
27971        },
27972        
27973        unregister : function(btn){
27974            if(!btn.toggleGroup){
27975                return;
27976            }
27977            var g = groups[btn.toggleGroup];
27978            if(g){
27979                g.remove(btn);
27980                btn.un("toggle", toggleGroup);
27981            }
27982        }
27983    };
27984 }();/*
27985  * Based on:
27986  * Ext JS Library 1.1.1
27987  * Copyright(c) 2006-2007, Ext JS, LLC.
27988  *
27989  * Originally Released Under LGPL - original licence link has changed is not relivant.
27990  *
27991  * Fork - LGPL
27992  * <script type="text/javascript">
27993  */
27994  
27995 /**
27996  * @class Roo.SplitButton
27997  * @extends Roo.Button
27998  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27999  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28000  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28001  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28002  * @cfg {String} arrowTooltip The title attribute of the arrow
28003  * @constructor
28004  * Create a new menu button
28005  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28006  * @param {Object} config The config object
28007  */
28008 Roo.SplitButton = function(renderTo, config){
28009     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28010     /**
28011      * @event arrowclick
28012      * Fires when this button's arrow is clicked
28013      * @param {SplitButton} this
28014      * @param {EventObject} e The click event
28015      */
28016     this.addEvents({"arrowclick":true});
28017 };
28018
28019 Roo.extend(Roo.SplitButton, Roo.Button, {
28020     render : function(renderTo){
28021         // this is one sweet looking template!
28022         var tpl = new Roo.Template(
28023             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28024             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28025             '<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>',
28026             "</tbody></table></td><td>",
28027             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28028             '<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>',
28029             "</tbody></table></td></tr></table>"
28030         );
28031         var btn = tpl.append(renderTo, [this.text, this.type], true);
28032         var btnEl = btn.child("button");
28033         if(this.cls){
28034             btn.addClass(this.cls);
28035         }
28036         if(this.icon){
28037             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28038         }
28039         if(this.iconCls){
28040             btnEl.addClass(this.iconCls);
28041             if(!this.cls){
28042                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28043             }
28044         }
28045         this.el = btn;
28046         if(this.handleMouseEvents){
28047             btn.on("mouseover", this.onMouseOver, this);
28048             btn.on("mouseout", this.onMouseOut, this);
28049             btn.on("mousedown", this.onMouseDown, this);
28050             btn.on("mouseup", this.onMouseUp, this);
28051         }
28052         btn.on(this.clickEvent, this.onClick, this);
28053         if(this.tooltip){
28054             if(typeof this.tooltip == 'object'){
28055                 Roo.QuickTips.tips(Roo.apply({
28056                       target: btnEl.id
28057                 }, this.tooltip));
28058             } else {
28059                 btnEl.dom[this.tooltipType] = this.tooltip;
28060             }
28061         }
28062         if(this.arrowTooltip){
28063             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28064         }
28065         if(this.hidden){
28066             this.hide();
28067         }
28068         if(this.disabled){
28069             this.disable();
28070         }
28071         if(this.pressed){
28072             this.el.addClass("x-btn-pressed");
28073         }
28074         if(Roo.isIE && !Roo.isIE7){
28075             this.autoWidth.defer(1, this);
28076         }else{
28077             this.autoWidth();
28078         }
28079         if(this.menu){
28080             this.menu.on("show", this.onMenuShow, this);
28081             this.menu.on("hide", this.onMenuHide, this);
28082         }
28083         this.fireEvent('render', this);
28084     },
28085
28086     // private
28087     autoWidth : function(){
28088         if(this.el){
28089             var tbl = this.el.child("table:first");
28090             var tbl2 = this.el.child("table:last");
28091             this.el.setWidth("auto");
28092             tbl.setWidth("auto");
28093             if(Roo.isIE7 && Roo.isStrict){
28094                 var ib = this.el.child('button:first');
28095                 if(ib && ib.getWidth() > 20){
28096                     ib.clip();
28097                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28098                 }
28099             }
28100             if(this.minWidth){
28101                 if(this.hidden){
28102                     this.el.beginMeasure();
28103                 }
28104                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28105                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28106                 }
28107                 if(this.hidden){
28108                     this.el.endMeasure();
28109                 }
28110             }
28111             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28112         } 
28113     },
28114     /**
28115      * Sets this button's click handler
28116      * @param {Function} handler The function to call when the button is clicked
28117      * @param {Object} scope (optional) Scope for the function passed above
28118      */
28119     setHandler : function(handler, scope){
28120         this.handler = handler;
28121         this.scope = scope;  
28122     },
28123     
28124     /**
28125      * Sets this button's arrow click handler
28126      * @param {Function} handler The function to call when the arrow is clicked
28127      * @param {Object} scope (optional) Scope for the function passed above
28128      */
28129     setArrowHandler : function(handler, scope){
28130         this.arrowHandler = handler;
28131         this.scope = scope;  
28132     },
28133     
28134     /**
28135      * Focus the button
28136      */
28137     focus : function(){
28138         if(this.el){
28139             this.el.child("button:first").focus();
28140         }
28141     },
28142
28143     // private
28144     onClick : function(e){
28145         e.preventDefault();
28146         if(!this.disabled){
28147             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28148                 if(this.menu && !this.menu.isVisible()){
28149                     this.menu.show(this.el, this.menuAlign);
28150                 }
28151                 this.fireEvent("arrowclick", this, e);
28152                 if(this.arrowHandler){
28153                     this.arrowHandler.call(this.scope || this, this, e);
28154                 }
28155             }else{
28156                 this.fireEvent("click", this, e);
28157                 if(this.handler){
28158                     this.handler.call(this.scope || this, this, e);
28159                 }
28160             }
28161         }
28162     },
28163     // private
28164     onMouseDown : function(e){
28165         if(!this.disabled){
28166             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28167         }
28168     },
28169     // private
28170     onMouseUp : function(e){
28171         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28172     }   
28173 });
28174
28175
28176 // backwards compat
28177 Roo.MenuButton = Roo.SplitButton;/*
28178  * Based on:
28179  * Ext JS Library 1.1.1
28180  * Copyright(c) 2006-2007, Ext JS, LLC.
28181  *
28182  * Originally Released Under LGPL - original licence link has changed is not relivant.
28183  *
28184  * Fork - LGPL
28185  * <script type="text/javascript">
28186  */
28187
28188 /**
28189  * @class Roo.Toolbar
28190  * Basic Toolbar class.
28191  * @constructor
28192  * Creates a new Toolbar
28193  * @param {Object} container The config object
28194  */ 
28195 Roo.Toolbar = function(container, buttons, config)
28196 {
28197     /// old consturctor format still supported..
28198     if(container instanceof Array){ // omit the container for later rendering
28199         buttons = container;
28200         config = buttons;
28201         container = null;
28202     }
28203     if (typeof(container) == 'object' && container.xtype) {
28204         config = container;
28205         container = config.container;
28206         buttons = config.buttons || []; // not really - use items!!
28207     }
28208     var xitems = [];
28209     if (config && config.items) {
28210         xitems = config.items;
28211         delete config.items;
28212     }
28213     Roo.apply(this, config);
28214     this.buttons = buttons;
28215     
28216     if(container){
28217         this.render(container);
28218     }
28219     this.xitems = xitems;
28220     Roo.each(xitems, function(b) {
28221         this.add(b);
28222     }, this);
28223     
28224 };
28225
28226 Roo.Toolbar.prototype = {
28227     /**
28228      * @cfg {Array} items
28229      * array of button configs or elements to add (will be converted to a MixedCollection)
28230      */
28231     
28232     /**
28233      * @cfg {String/HTMLElement/Element} container
28234      * The id or element that will contain the toolbar
28235      */
28236     // private
28237     render : function(ct){
28238         this.el = Roo.get(ct);
28239         if(this.cls){
28240             this.el.addClass(this.cls);
28241         }
28242         // using a table allows for vertical alignment
28243         // 100% width is needed by Safari...
28244         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28245         this.tr = this.el.child("tr", true);
28246         var autoId = 0;
28247         this.items = new Roo.util.MixedCollection(false, function(o){
28248             return o.id || ("item" + (++autoId));
28249         });
28250         if(this.buttons){
28251             this.add.apply(this, this.buttons);
28252             delete this.buttons;
28253         }
28254     },
28255
28256     /**
28257      * Adds element(s) to the toolbar -- this function takes a variable number of 
28258      * arguments of mixed type and adds them to the toolbar.
28259      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28260      * <ul>
28261      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28262      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28263      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28264      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28265      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28266      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28267      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28268      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28269      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28270      * </ul>
28271      * @param {Mixed} arg2
28272      * @param {Mixed} etc.
28273      */
28274     add : function(){
28275         var a = arguments, l = a.length;
28276         for(var i = 0; i < l; i++){
28277             this._add(a[i]);
28278         }
28279     },
28280     // private..
28281     _add : function(el) {
28282         
28283         if (el.xtype) {
28284             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28285         }
28286         
28287         if (el.applyTo){ // some kind of form field
28288             return this.addField(el);
28289         } 
28290         if (el.render){ // some kind of Toolbar.Item
28291             return this.addItem(el);
28292         }
28293         if (typeof el == "string"){ // string
28294             if(el == "separator" || el == "-"){
28295                 return this.addSeparator();
28296             }
28297             if (el == " "){
28298                 return this.addSpacer();
28299             }
28300             if(el == "->"){
28301                 return this.addFill();
28302             }
28303             return this.addText(el);
28304             
28305         }
28306         if(el.tagName){ // element
28307             return this.addElement(el);
28308         }
28309         if(typeof el == "object"){ // must be button config?
28310             return this.addButton(el);
28311         }
28312         // and now what?!?!
28313         return false;
28314         
28315     },
28316     
28317     /**
28318      * Add an Xtype element
28319      * @param {Object} xtype Xtype Object
28320      * @return {Object} created Object
28321      */
28322     addxtype : function(e){
28323         return this.add(e);  
28324     },
28325     
28326     /**
28327      * Returns the Element for this toolbar.
28328      * @return {Roo.Element}
28329      */
28330     getEl : function(){
28331         return this.el;  
28332     },
28333     
28334     /**
28335      * Adds a separator
28336      * @return {Roo.Toolbar.Item} The separator item
28337      */
28338     addSeparator : function(){
28339         return this.addItem(new Roo.Toolbar.Separator());
28340     },
28341
28342     /**
28343      * Adds a spacer element
28344      * @return {Roo.Toolbar.Spacer} The spacer item
28345      */
28346     addSpacer : function(){
28347         return this.addItem(new Roo.Toolbar.Spacer());
28348     },
28349
28350     /**
28351      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28352      * @return {Roo.Toolbar.Fill} The fill item
28353      */
28354     addFill : function(){
28355         return this.addItem(new Roo.Toolbar.Fill());
28356     },
28357
28358     /**
28359      * Adds any standard HTML element to the toolbar
28360      * @param {String/HTMLElement/Element} el The element or id of the element to add
28361      * @return {Roo.Toolbar.Item} The element's item
28362      */
28363     addElement : function(el){
28364         return this.addItem(new Roo.Toolbar.Item(el));
28365     },
28366     /**
28367      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28368      * @type Roo.util.MixedCollection  
28369      */
28370     items : false,
28371      
28372     /**
28373      * Adds any Toolbar.Item or subclass
28374      * @param {Roo.Toolbar.Item} item
28375      * @return {Roo.Toolbar.Item} The item
28376      */
28377     addItem : function(item){
28378         var td = this.nextBlock();
28379         item.render(td);
28380         this.items.add(item);
28381         return item;
28382     },
28383     
28384     /**
28385      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28386      * @param {Object/Array} config A button config or array of configs
28387      * @return {Roo.Toolbar.Button/Array}
28388      */
28389     addButton : function(config){
28390         if(config instanceof Array){
28391             var buttons = [];
28392             for(var i = 0, len = config.length; i < len; i++) {
28393                 buttons.push(this.addButton(config[i]));
28394             }
28395             return buttons;
28396         }
28397         var b = config;
28398         if(!(config instanceof Roo.Toolbar.Button)){
28399             b = config.split ?
28400                 new Roo.Toolbar.SplitButton(config) :
28401                 new Roo.Toolbar.Button(config);
28402         }
28403         var td = this.nextBlock();
28404         b.render(td);
28405         this.items.add(b);
28406         return b;
28407     },
28408     
28409     /**
28410      * Adds text to the toolbar
28411      * @param {String} text The text to add
28412      * @return {Roo.Toolbar.Item} The element's item
28413      */
28414     addText : function(text){
28415         return this.addItem(new Roo.Toolbar.TextItem(text));
28416     },
28417     
28418     /**
28419      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28420      * @param {Number} index The index where the item is to be inserted
28421      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28422      * @return {Roo.Toolbar.Button/Item}
28423      */
28424     insertButton : function(index, item){
28425         if(item instanceof Array){
28426             var buttons = [];
28427             for(var i = 0, len = item.length; i < len; i++) {
28428                buttons.push(this.insertButton(index + i, item[i]));
28429             }
28430             return buttons;
28431         }
28432         if (!(item instanceof Roo.Toolbar.Button)){
28433            item = new Roo.Toolbar.Button(item);
28434         }
28435         var td = document.createElement("td");
28436         this.tr.insertBefore(td, this.tr.childNodes[index]);
28437         item.render(td);
28438         this.items.insert(index, item);
28439         return item;
28440     },
28441     
28442     /**
28443      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28444      * @param {Object} config
28445      * @return {Roo.Toolbar.Item} The element's item
28446      */
28447     addDom : function(config, returnEl){
28448         var td = this.nextBlock();
28449         Roo.DomHelper.overwrite(td, config);
28450         var ti = new Roo.Toolbar.Item(td.firstChild);
28451         ti.render(td);
28452         this.items.add(ti);
28453         return ti;
28454     },
28455
28456     /**
28457      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28458      * @type Roo.util.MixedCollection  
28459      */
28460     fields : false,
28461     
28462     /**
28463      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28464      * Note: the field should not have been rendered yet. For a field that has already been
28465      * rendered, use {@link #addElement}.
28466      * @param {Roo.form.Field} field
28467      * @return {Roo.ToolbarItem}
28468      */
28469      
28470       
28471     addField : function(field) {
28472         if (!this.fields) {
28473             var autoId = 0;
28474             this.fields = new Roo.util.MixedCollection(false, function(o){
28475                 return o.id || ("item" + (++autoId));
28476             });
28477
28478         }
28479         
28480         var td = this.nextBlock();
28481         field.render(td);
28482         var ti = new Roo.Toolbar.Item(td.firstChild);
28483         ti.render(td);
28484         this.items.add(ti);
28485         this.fields.add(field);
28486         return ti;
28487     },
28488     /**
28489      * Hide the toolbar
28490      * @method hide
28491      */
28492      
28493       
28494     hide : function()
28495     {
28496         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28497         this.el.child('div').hide();
28498     },
28499     /**
28500      * Show the toolbar
28501      * @method show
28502      */
28503     show : function()
28504     {
28505         this.el.child('div').show();
28506     },
28507       
28508     // private
28509     nextBlock : function(){
28510         var td = document.createElement("td");
28511         this.tr.appendChild(td);
28512         return td;
28513     },
28514
28515     // private
28516     destroy : function(){
28517         if(this.items){ // rendered?
28518             Roo.destroy.apply(Roo, this.items.items);
28519         }
28520         if(this.fields){ // rendered?
28521             Roo.destroy.apply(Roo, this.fields.items);
28522         }
28523         Roo.Element.uncache(this.el, this.tr);
28524     }
28525 };
28526
28527 /**
28528  * @class Roo.Toolbar.Item
28529  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28530  * @constructor
28531  * Creates a new Item
28532  * @param {HTMLElement} el 
28533  */
28534 Roo.Toolbar.Item = function(el){
28535     var cfg = {};
28536     if (typeof (el.xtype) != 'undefined') {
28537         cfg = el;
28538         el = cfg.el;
28539     }
28540     
28541     this.el = Roo.getDom(el);
28542     this.id = Roo.id(this.el);
28543     this.hidden = false;
28544     
28545     this.addEvents({
28546          /**
28547              * @event render
28548              * Fires when the button is rendered
28549              * @param {Button} this
28550              */
28551         'render': true
28552     });
28553     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28554 };
28555 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28556 //Roo.Toolbar.Item.prototype = {
28557     
28558     /**
28559      * Get this item's HTML Element
28560      * @return {HTMLElement}
28561      */
28562     getEl : function(){
28563        return this.el;  
28564     },
28565
28566     // private
28567     render : function(td){
28568         
28569          this.td = td;
28570         td.appendChild(this.el);
28571         
28572         this.fireEvent('render', this);
28573     },
28574     
28575     /**
28576      * Removes and destroys this item.
28577      */
28578     destroy : function(){
28579         this.td.parentNode.removeChild(this.td);
28580     },
28581     
28582     /**
28583      * Shows this item.
28584      */
28585     show: function(){
28586         this.hidden = false;
28587         this.td.style.display = "";
28588     },
28589     
28590     /**
28591      * Hides this item.
28592      */
28593     hide: function(){
28594         this.hidden = true;
28595         this.td.style.display = "none";
28596     },
28597     
28598     /**
28599      * Convenience function for boolean show/hide.
28600      * @param {Boolean} visible true to show/false to hide
28601      */
28602     setVisible: function(visible){
28603         if(visible) {
28604             this.show();
28605         }else{
28606             this.hide();
28607         }
28608     },
28609     
28610     /**
28611      * Try to focus this item.
28612      */
28613     focus : function(){
28614         Roo.fly(this.el).focus();
28615     },
28616     
28617     /**
28618      * Disables this item.
28619      */
28620     disable : function(){
28621         Roo.fly(this.td).addClass("x-item-disabled");
28622         this.disabled = true;
28623         this.el.disabled = true;
28624     },
28625     
28626     /**
28627      * Enables this item.
28628      */
28629     enable : function(){
28630         Roo.fly(this.td).removeClass("x-item-disabled");
28631         this.disabled = false;
28632         this.el.disabled = false;
28633     }
28634 });
28635
28636
28637 /**
28638  * @class Roo.Toolbar.Separator
28639  * @extends Roo.Toolbar.Item
28640  * A simple toolbar separator class
28641  * @constructor
28642  * Creates a new Separator
28643  */
28644 Roo.Toolbar.Separator = function(cfg){
28645     
28646     var s = document.createElement("span");
28647     s.className = "ytb-sep";
28648     if (cfg) {
28649         cfg.el = s;
28650     }
28651     
28652     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28653 };
28654 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28655     enable:Roo.emptyFn,
28656     disable:Roo.emptyFn,
28657     focus:Roo.emptyFn
28658 });
28659
28660 /**
28661  * @class Roo.Toolbar.Spacer
28662  * @extends Roo.Toolbar.Item
28663  * A simple element that adds extra horizontal space to a toolbar.
28664  * @constructor
28665  * Creates a new Spacer
28666  */
28667 Roo.Toolbar.Spacer = function(cfg){
28668     var s = document.createElement("div");
28669     s.className = "ytb-spacer";
28670     if (cfg) {
28671         cfg.el = s;
28672     }
28673     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28674 };
28675 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28676     enable:Roo.emptyFn,
28677     disable:Roo.emptyFn,
28678     focus:Roo.emptyFn
28679 });
28680
28681 /**
28682  * @class Roo.Toolbar.Fill
28683  * @extends Roo.Toolbar.Spacer
28684  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28685  * @constructor
28686  * Creates a new Spacer
28687  */
28688 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28689     // private
28690     render : function(td){
28691         td.style.width = '100%';
28692         Roo.Toolbar.Fill.superclass.render.call(this, td);
28693     }
28694 });
28695
28696 /**
28697  * @class Roo.Toolbar.TextItem
28698  * @extends Roo.Toolbar.Item
28699  * A simple class that renders text directly into a toolbar.
28700  * @constructor
28701  * Creates a new TextItem
28702  * @param {String} text
28703  */
28704 Roo.Toolbar.TextItem = function(cfg){
28705     var  text = cfg || "";
28706     if (typeof(cfg) == 'object') {
28707         text = cfg.text || "";
28708     }  else {
28709         cfg = null;
28710     }
28711     var s = document.createElement("span");
28712     s.className = "ytb-text";
28713     s.innerHTML = text;
28714     if (cfg) {
28715         cfg.el  = s;
28716     }
28717     
28718     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28719 };
28720 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28721     
28722      
28723     enable:Roo.emptyFn,
28724     disable:Roo.emptyFn,
28725     focus:Roo.emptyFn
28726 });
28727
28728 /**
28729  * @class Roo.Toolbar.Button
28730  * @extends Roo.Button
28731  * A button that renders into a toolbar.
28732  * @constructor
28733  * Creates a new Button
28734  * @param {Object} config A standard {@link Roo.Button} config object
28735  */
28736 Roo.Toolbar.Button = function(config){
28737     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28738 };
28739 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28740     render : function(td){
28741         this.td = td;
28742         Roo.Toolbar.Button.superclass.render.call(this, td);
28743     },
28744     
28745     /**
28746      * Removes and destroys this button
28747      */
28748     destroy : function(){
28749         Roo.Toolbar.Button.superclass.destroy.call(this);
28750         this.td.parentNode.removeChild(this.td);
28751     },
28752     
28753     /**
28754      * Shows this button
28755      */
28756     show: function(){
28757         this.hidden = false;
28758         this.td.style.display = "";
28759     },
28760     
28761     /**
28762      * Hides this button
28763      */
28764     hide: function(){
28765         this.hidden = true;
28766         this.td.style.display = "none";
28767     },
28768
28769     /**
28770      * Disables this item
28771      */
28772     disable : function(){
28773         Roo.fly(this.td).addClass("x-item-disabled");
28774         this.disabled = true;
28775     },
28776
28777     /**
28778      * Enables this item
28779      */
28780     enable : function(){
28781         Roo.fly(this.td).removeClass("x-item-disabled");
28782         this.disabled = false;
28783     }
28784 });
28785 // backwards compat
28786 Roo.ToolbarButton = Roo.Toolbar.Button;
28787
28788 /**
28789  * @class Roo.Toolbar.SplitButton
28790  * @extends Roo.SplitButton
28791  * A menu button that renders into a toolbar.
28792  * @constructor
28793  * Creates a new SplitButton
28794  * @param {Object} config A standard {@link Roo.SplitButton} config object
28795  */
28796 Roo.Toolbar.SplitButton = function(config){
28797     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28798 };
28799 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28800     render : function(td){
28801         this.td = td;
28802         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28803     },
28804     
28805     /**
28806      * Removes and destroys this button
28807      */
28808     destroy : function(){
28809         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28810         this.td.parentNode.removeChild(this.td);
28811     },
28812     
28813     /**
28814      * Shows this button
28815      */
28816     show: function(){
28817         this.hidden = false;
28818         this.td.style.display = "";
28819     },
28820     
28821     /**
28822      * Hides this button
28823      */
28824     hide: function(){
28825         this.hidden = true;
28826         this.td.style.display = "none";
28827     }
28828 });
28829
28830 // backwards compat
28831 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28832  * Based on:
28833  * Ext JS Library 1.1.1
28834  * Copyright(c) 2006-2007, Ext JS, LLC.
28835  *
28836  * Originally Released Under LGPL - original licence link has changed is not relivant.
28837  *
28838  * Fork - LGPL
28839  * <script type="text/javascript">
28840  */
28841  
28842 /**
28843  * @class Roo.PagingToolbar
28844  * @extends Roo.Toolbar
28845  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28846  * @constructor
28847  * Create a new PagingToolbar
28848  * @param {Object} config The config object
28849  */
28850 Roo.PagingToolbar = function(el, ds, config)
28851 {
28852     // old args format still supported... - xtype is prefered..
28853     if (typeof(el) == 'object' && el.xtype) {
28854         // created from xtype...
28855         config = el;
28856         ds = el.dataSource;
28857         el = config.container;
28858     }
28859     var items = [];
28860     if (config.items) {
28861         items = config.items;
28862         config.items = [];
28863     }
28864     
28865     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28866     this.ds = ds;
28867     this.cursor = 0;
28868     this.renderButtons(this.el);
28869     this.bind(ds);
28870     
28871     // supprot items array.
28872    
28873     Roo.each(items, function(e) {
28874         this.add(Roo.factory(e));
28875     },this);
28876     
28877 };
28878
28879 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28880     /**
28881      * @cfg {Roo.data.Store} dataSource
28882      * The underlying data store providing the paged data
28883      */
28884     /**
28885      * @cfg {String/HTMLElement/Element} container
28886      * container The id or element that will contain the toolbar
28887      */
28888     /**
28889      * @cfg {Boolean} displayInfo
28890      * True to display the displayMsg (defaults to false)
28891      */
28892     /**
28893      * @cfg {Number} pageSize
28894      * The number of records to display per page (defaults to 20)
28895      */
28896     pageSize: 20,
28897     /**
28898      * @cfg {String} displayMsg
28899      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28900      */
28901     displayMsg : 'Displaying {0} - {1} of {2}',
28902     /**
28903      * @cfg {String} emptyMsg
28904      * The message to display when no records are found (defaults to "No data to display")
28905      */
28906     emptyMsg : 'No data to display',
28907     /**
28908      * Customizable piece of the default paging text (defaults to "Page")
28909      * @type String
28910      */
28911     beforePageText : "Page",
28912     /**
28913      * Customizable piece of the default paging text (defaults to "of %0")
28914      * @type String
28915      */
28916     afterPageText : "of {0}",
28917     /**
28918      * Customizable piece of the default paging text (defaults to "First Page")
28919      * @type String
28920      */
28921     firstText : "First Page",
28922     /**
28923      * Customizable piece of the default paging text (defaults to "Previous Page")
28924      * @type String
28925      */
28926     prevText : "Previous Page",
28927     /**
28928      * Customizable piece of the default paging text (defaults to "Next Page")
28929      * @type String
28930      */
28931     nextText : "Next Page",
28932     /**
28933      * Customizable piece of the default paging text (defaults to "Last Page")
28934      * @type String
28935      */
28936     lastText : "Last Page",
28937     /**
28938      * Customizable piece of the default paging text (defaults to "Refresh")
28939      * @type String
28940      */
28941     refreshText : "Refresh",
28942
28943     // private
28944     renderButtons : function(el){
28945         Roo.PagingToolbar.superclass.render.call(this, el);
28946         this.first = this.addButton({
28947             tooltip: this.firstText,
28948             cls: "x-btn-icon x-grid-page-first",
28949             disabled: true,
28950             handler: this.onClick.createDelegate(this, ["first"])
28951         });
28952         this.prev = this.addButton({
28953             tooltip: this.prevText,
28954             cls: "x-btn-icon x-grid-page-prev",
28955             disabled: true,
28956             handler: this.onClick.createDelegate(this, ["prev"])
28957         });
28958         //this.addSeparator();
28959         this.add(this.beforePageText);
28960         this.field = Roo.get(this.addDom({
28961            tag: "input",
28962            type: "text",
28963            size: "3",
28964            value: "1",
28965            cls: "x-grid-page-number"
28966         }).el);
28967         this.field.on("keydown", this.onPagingKeydown, this);
28968         this.field.on("focus", function(){this.dom.select();});
28969         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28970         this.field.setHeight(18);
28971         //this.addSeparator();
28972         this.next = this.addButton({
28973             tooltip: this.nextText,
28974             cls: "x-btn-icon x-grid-page-next",
28975             disabled: true,
28976             handler: this.onClick.createDelegate(this, ["next"])
28977         });
28978         this.last = this.addButton({
28979             tooltip: this.lastText,
28980             cls: "x-btn-icon x-grid-page-last",
28981             disabled: true,
28982             handler: this.onClick.createDelegate(this, ["last"])
28983         });
28984         //this.addSeparator();
28985         this.loading = this.addButton({
28986             tooltip: this.refreshText,
28987             cls: "x-btn-icon x-grid-loading",
28988             handler: this.onClick.createDelegate(this, ["refresh"])
28989         });
28990
28991         if(this.displayInfo){
28992             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28993         }
28994     },
28995
28996     // private
28997     updateInfo : function(){
28998         if(this.displayEl){
28999             var count = this.ds.getCount();
29000             var msg = count == 0 ?
29001                 this.emptyMsg :
29002                 String.format(
29003                     this.displayMsg,
29004                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29005                 );
29006             this.displayEl.update(msg);
29007         }
29008     },
29009
29010     // private
29011     onLoad : function(ds, r, o){
29012        this.cursor = o.params ? o.params.start : 0;
29013        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29014
29015        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29016        this.field.dom.value = ap;
29017        this.first.setDisabled(ap == 1);
29018        this.prev.setDisabled(ap == 1);
29019        this.next.setDisabled(ap == ps);
29020        this.last.setDisabled(ap == ps);
29021        this.loading.enable();
29022        this.updateInfo();
29023     },
29024
29025     // private
29026     getPageData : function(){
29027         var total = this.ds.getTotalCount();
29028         return {
29029             total : total,
29030             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29031             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29032         };
29033     },
29034
29035     // private
29036     onLoadError : function(){
29037         this.loading.enable();
29038     },
29039
29040     // private
29041     onPagingKeydown : function(e){
29042         var k = e.getKey();
29043         var d = this.getPageData();
29044         if(k == e.RETURN){
29045             var v = this.field.dom.value, pageNum;
29046             if(!v || isNaN(pageNum = parseInt(v, 10))){
29047                 this.field.dom.value = d.activePage;
29048                 return;
29049             }
29050             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29051             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29052             e.stopEvent();
29053         }
29054         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))
29055         {
29056           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29057           this.field.dom.value = pageNum;
29058           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29059           e.stopEvent();
29060         }
29061         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29062         {
29063           var v = this.field.dom.value, pageNum; 
29064           var increment = (e.shiftKey) ? 10 : 1;
29065           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29066             increment *= -1;
29067           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29068             this.field.dom.value = d.activePage;
29069             return;
29070           }
29071           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29072           {
29073             this.field.dom.value = parseInt(v, 10) + increment;
29074             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29075             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29076           }
29077           e.stopEvent();
29078         }
29079     },
29080
29081     // private
29082     beforeLoad : function(){
29083         if(this.loading){
29084             this.loading.disable();
29085         }
29086     },
29087
29088     // private
29089     onClick : function(which){
29090         var ds = this.ds;
29091         switch(which){
29092             case "first":
29093                 ds.load({params:{start: 0, limit: this.pageSize}});
29094             break;
29095             case "prev":
29096                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29097             break;
29098             case "next":
29099                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29100             break;
29101             case "last":
29102                 var total = ds.getTotalCount();
29103                 var extra = total % this.pageSize;
29104                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29105                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29106             break;
29107             case "refresh":
29108                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29109             break;
29110         }
29111     },
29112
29113     /**
29114      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29115      * @param {Roo.data.Store} store The data store to unbind
29116      */
29117     unbind : function(ds){
29118         ds.un("beforeload", this.beforeLoad, this);
29119         ds.un("load", this.onLoad, this);
29120         ds.un("loadexception", this.onLoadError, this);
29121         ds.un("remove", this.updateInfo, this);
29122         ds.un("add", this.updateInfo, this);
29123         this.ds = undefined;
29124     },
29125
29126     /**
29127      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29128      * @param {Roo.data.Store} store The data store to bind
29129      */
29130     bind : function(ds){
29131         ds.on("beforeload", this.beforeLoad, this);
29132         ds.on("load", this.onLoad, this);
29133         ds.on("loadexception", this.onLoadError, this);
29134         ds.on("remove", this.updateInfo, this);
29135         ds.on("add", this.updateInfo, this);
29136         this.ds = ds;
29137     }
29138 });/*
29139  * Based on:
29140  * Ext JS Library 1.1.1
29141  * Copyright(c) 2006-2007, Ext JS, LLC.
29142  *
29143  * Originally Released Under LGPL - original licence link has changed is not relivant.
29144  *
29145  * Fork - LGPL
29146  * <script type="text/javascript">
29147  */
29148
29149 /**
29150  * @class Roo.Resizable
29151  * @extends Roo.util.Observable
29152  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29153  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29154  * 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
29155  * the element will be wrapped for you automatically.</p>
29156  * <p>Here is the list of valid resize handles:</p>
29157  * <pre>
29158 Value   Description
29159 ------  -------------------
29160  'n'     north
29161  's'     south
29162  'e'     east
29163  'w'     west
29164  'nw'    northwest
29165  'sw'    southwest
29166  'se'    southeast
29167  'ne'    northeast
29168  'hd'    horizontal drag
29169  'all'   all
29170 </pre>
29171  * <p>Here's an example showing the creation of a typical Resizable:</p>
29172  * <pre><code>
29173 var resizer = new Roo.Resizable("element-id", {
29174     handles: 'all',
29175     minWidth: 200,
29176     minHeight: 100,
29177     maxWidth: 500,
29178     maxHeight: 400,
29179     pinned: true
29180 });
29181 resizer.on("resize", myHandler);
29182 </code></pre>
29183  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29184  * resizer.east.setDisplayed(false);</p>
29185  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29186  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29187  * resize operation's new size (defaults to [0, 0])
29188  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29189  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29190  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29191  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29192  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29193  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29194  * @cfg {Number} width The width of the element in pixels (defaults to null)
29195  * @cfg {Number} height The height of the element in pixels (defaults to null)
29196  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29197  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29198  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29199  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29200  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29201  * in favor of the handles config option (defaults to false)
29202  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29203  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29204  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29205  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29206  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29207  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29208  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29209  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29210  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29211  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29212  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29213  * @constructor
29214  * Create a new resizable component
29215  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29216  * @param {Object} config configuration options
29217   */
29218 Roo.Resizable = function(el, config)
29219 {
29220     this.el = Roo.get(el);
29221
29222     if(config && config.wrap){
29223         config.resizeChild = this.el;
29224         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29225         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29226         this.el.setStyle("overflow", "hidden");
29227         this.el.setPositioning(config.resizeChild.getPositioning());
29228         config.resizeChild.clearPositioning();
29229         if(!config.width || !config.height){
29230             var csize = config.resizeChild.getSize();
29231             this.el.setSize(csize.width, csize.height);
29232         }
29233         if(config.pinned && !config.adjustments){
29234             config.adjustments = "auto";
29235         }
29236     }
29237
29238     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29239     this.proxy.unselectable();
29240     this.proxy.enableDisplayMode('block');
29241
29242     Roo.apply(this, config);
29243
29244     if(this.pinned){
29245         this.disableTrackOver = true;
29246         this.el.addClass("x-resizable-pinned");
29247     }
29248     // if the element isn't positioned, make it relative
29249     var position = this.el.getStyle("position");
29250     if(position != "absolute" && position != "fixed"){
29251         this.el.setStyle("position", "relative");
29252     }
29253     if(!this.handles){ // no handles passed, must be legacy style
29254         this.handles = 's,e,se';
29255         if(this.multiDirectional){
29256             this.handles += ',n,w';
29257         }
29258     }
29259     if(this.handles == "all"){
29260         this.handles = "n s e w ne nw se sw";
29261     }
29262     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29263     var ps = Roo.Resizable.positions;
29264     for(var i = 0, len = hs.length; i < len; i++){
29265         if(hs[i] && ps[hs[i]]){
29266             var pos = ps[hs[i]];
29267             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29268         }
29269     }
29270     // legacy
29271     this.corner = this.southeast;
29272     
29273     // updateBox = the box can move..
29274     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29275         this.updateBox = true;
29276     }
29277
29278     this.activeHandle = null;
29279
29280     if(this.resizeChild){
29281         if(typeof this.resizeChild == "boolean"){
29282             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29283         }else{
29284             this.resizeChild = Roo.get(this.resizeChild, true);
29285         }
29286     }
29287     
29288     if(this.adjustments == "auto"){
29289         var rc = this.resizeChild;
29290         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29291         if(rc && (hw || hn)){
29292             rc.position("relative");
29293             rc.setLeft(hw ? hw.el.getWidth() : 0);
29294             rc.setTop(hn ? hn.el.getHeight() : 0);
29295         }
29296         this.adjustments = [
29297             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29298             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29299         ];
29300     }
29301
29302     if(this.draggable){
29303         this.dd = this.dynamic ?
29304             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29305         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29306     }
29307
29308     // public events
29309     this.addEvents({
29310         /**
29311          * @event beforeresize
29312          * Fired before resize is allowed. Set enabled to false to cancel resize.
29313          * @param {Roo.Resizable} this
29314          * @param {Roo.EventObject} e The mousedown event
29315          */
29316         "beforeresize" : true,
29317         /**
29318          * @event resizing
29319          * Fired a resizing.
29320          * @param {Roo.Resizable} this
29321          * @param {Number} x The new x position
29322          * @param {Number} y The new y position
29323          * @param {Number} w The new w width
29324          * @param {Number} h The new h hight
29325          * @param {Roo.EventObject} e The mouseup event
29326          */
29327         "resizing" : true,
29328         /**
29329          * @event resize
29330          * Fired after a resize.
29331          * @param {Roo.Resizable} this
29332          * @param {Number} width The new width
29333          * @param {Number} height The new height
29334          * @param {Roo.EventObject} e The mouseup event
29335          */
29336         "resize" : true
29337     });
29338
29339     if(this.width !== null && this.height !== null){
29340         this.resizeTo(this.width, this.height);
29341     }else{
29342         this.updateChildSize();
29343     }
29344     if(Roo.isIE){
29345         this.el.dom.style.zoom = 1;
29346     }
29347     Roo.Resizable.superclass.constructor.call(this);
29348 };
29349
29350 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29351         resizeChild : false,
29352         adjustments : [0, 0],
29353         minWidth : 5,
29354         minHeight : 5,
29355         maxWidth : 10000,
29356         maxHeight : 10000,
29357         enabled : true,
29358         animate : false,
29359         duration : .35,
29360         dynamic : false,
29361         handles : false,
29362         multiDirectional : false,
29363         disableTrackOver : false,
29364         easing : 'easeOutStrong',
29365         widthIncrement : 0,
29366         heightIncrement : 0,
29367         pinned : false,
29368         width : null,
29369         height : null,
29370         preserveRatio : false,
29371         transparent: false,
29372         minX: 0,
29373         minY: 0,
29374         draggable: false,
29375
29376         /**
29377          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29378          */
29379         constrainTo: undefined,
29380         /**
29381          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29382          */
29383         resizeRegion: undefined,
29384
29385
29386     /**
29387      * Perform a manual resize
29388      * @param {Number} width
29389      * @param {Number} height
29390      */
29391     resizeTo : function(width, height){
29392         this.el.setSize(width, height);
29393         this.updateChildSize();
29394         this.fireEvent("resize", this, width, height, null);
29395     },
29396
29397     // private
29398     startSizing : function(e, handle){
29399         this.fireEvent("beforeresize", this, e);
29400         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29401
29402             if(!this.overlay){
29403                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29404                 this.overlay.unselectable();
29405                 this.overlay.enableDisplayMode("block");
29406                 this.overlay.on("mousemove", this.onMouseMove, this);
29407                 this.overlay.on("mouseup", this.onMouseUp, this);
29408             }
29409             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29410
29411             this.resizing = true;
29412             this.startBox = this.el.getBox();
29413             this.startPoint = e.getXY();
29414             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29415                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29416
29417             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29418             this.overlay.show();
29419
29420             if(this.constrainTo) {
29421                 var ct = Roo.get(this.constrainTo);
29422                 this.resizeRegion = ct.getRegion().adjust(
29423                     ct.getFrameWidth('t'),
29424                     ct.getFrameWidth('l'),
29425                     -ct.getFrameWidth('b'),
29426                     -ct.getFrameWidth('r')
29427                 );
29428             }
29429
29430             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29431             this.proxy.show();
29432             this.proxy.setBox(this.startBox);
29433             if(!this.dynamic){
29434                 this.proxy.setStyle('visibility', 'visible');
29435             }
29436         }
29437     },
29438
29439     // private
29440     onMouseDown : function(handle, e){
29441         if(this.enabled){
29442             e.stopEvent();
29443             this.activeHandle = handle;
29444             this.startSizing(e, handle);
29445         }
29446     },
29447
29448     // private
29449     onMouseUp : function(e){
29450         var size = this.resizeElement();
29451         this.resizing = false;
29452         this.handleOut();
29453         this.overlay.hide();
29454         this.proxy.hide();
29455         this.fireEvent("resize", this, size.width, size.height, e);
29456     },
29457
29458     // private
29459     updateChildSize : function(){
29460         
29461         if(this.resizeChild){
29462             var el = this.el;
29463             var child = this.resizeChild;
29464             var adj = this.adjustments;
29465             if(el.dom.offsetWidth){
29466                 var b = el.getSize(true);
29467                 child.setSize(b.width+adj[0], b.height+adj[1]);
29468             }
29469             // Second call here for IE
29470             // The first call enables instant resizing and
29471             // the second call corrects scroll bars if they
29472             // exist
29473             if(Roo.isIE){
29474                 setTimeout(function(){
29475                     if(el.dom.offsetWidth){
29476                         var b = el.getSize(true);
29477                         child.setSize(b.width+adj[0], b.height+adj[1]);
29478                     }
29479                 }, 10);
29480             }
29481         }
29482     },
29483
29484     // private
29485     snap : function(value, inc, min){
29486         if(!inc || !value) return value;
29487         var newValue = value;
29488         var m = value % inc;
29489         if(m > 0){
29490             if(m > (inc/2)){
29491                 newValue = value + (inc-m);
29492             }else{
29493                 newValue = value - m;
29494             }
29495         }
29496         return Math.max(min, newValue);
29497     },
29498
29499     // private
29500     resizeElement : function(){
29501         var box = this.proxy.getBox();
29502         if(this.updateBox){
29503             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29504         }else{
29505             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29506         }
29507         this.updateChildSize();
29508         if(!this.dynamic){
29509             this.proxy.hide();
29510         }
29511         return box;
29512     },
29513
29514     // private
29515     constrain : function(v, diff, m, mx){
29516         if(v - diff < m){
29517             diff = v - m;
29518         }else if(v - diff > mx){
29519             diff = mx - v;
29520         }
29521         return diff;
29522     },
29523
29524     // private
29525     onMouseMove : function(e){
29526         
29527         if(this.enabled){
29528             try{// try catch so if something goes wrong the user doesn't get hung
29529
29530             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29531                 return;
29532             }
29533
29534             //var curXY = this.startPoint;
29535             var curSize = this.curSize || this.startBox;
29536             var x = this.startBox.x, y = this.startBox.y;
29537             var ox = x, oy = y;
29538             var w = curSize.width, h = curSize.height;
29539             var ow = w, oh = h;
29540             var mw = this.minWidth, mh = this.minHeight;
29541             var mxw = this.maxWidth, mxh = this.maxHeight;
29542             var wi = this.widthIncrement;
29543             var hi = this.heightIncrement;
29544
29545             var eventXY = e.getXY();
29546             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29547             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29548
29549             var pos = this.activeHandle.position;
29550
29551             switch(pos){
29552                 case "east":
29553                     w += diffX;
29554                     w = Math.min(Math.max(mw, w), mxw);
29555                     break;
29556              
29557                 case "south":
29558                     h += diffY;
29559                     h = Math.min(Math.max(mh, h), mxh);
29560                     break;
29561                 case "southeast":
29562                     w += diffX;
29563                     h += diffY;
29564                     w = Math.min(Math.max(mw, w), mxw);
29565                     h = Math.min(Math.max(mh, h), mxh);
29566                     break;
29567                 case "north":
29568                     diffY = this.constrain(h, diffY, mh, mxh);
29569                     y += diffY;
29570                     h -= diffY;
29571                     break;
29572                 case "hdrag":
29573                     
29574                     if (wi) {
29575                         var adiffX = Math.abs(diffX);
29576                         var sub = (adiffX % wi); // how much 
29577                         if (sub > (wi/2)) { // far enough to snap
29578                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29579                         } else {
29580                             // remove difference.. 
29581                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29582                         }
29583                     }
29584                     x += diffX;
29585                     x = Math.max(this.minX, x);
29586                     break;
29587                 case "west":
29588                     diffX = this.constrain(w, diffX, mw, mxw);
29589                     x += diffX;
29590                     w -= diffX;
29591                     break;
29592                 case "northeast":
29593                     w += diffX;
29594                     w = Math.min(Math.max(mw, w), mxw);
29595                     diffY = this.constrain(h, diffY, mh, mxh);
29596                     y += diffY;
29597                     h -= diffY;
29598                     break;
29599                 case "northwest":
29600                     diffX = this.constrain(w, diffX, mw, mxw);
29601                     diffY = this.constrain(h, diffY, mh, mxh);
29602                     y += diffY;
29603                     h -= diffY;
29604                     x += diffX;
29605                     w -= diffX;
29606                     break;
29607                case "southwest":
29608                     diffX = this.constrain(w, diffX, mw, mxw);
29609                     h += diffY;
29610                     h = Math.min(Math.max(mh, h), mxh);
29611                     x += diffX;
29612                     w -= diffX;
29613                     break;
29614             }
29615
29616             var sw = this.snap(w, wi, mw);
29617             var sh = this.snap(h, hi, mh);
29618             if(sw != w || sh != h){
29619                 switch(pos){
29620                     case "northeast":
29621                         y -= sh - h;
29622                     break;
29623                     case "north":
29624                         y -= sh - h;
29625                         break;
29626                     case "southwest":
29627                         x -= sw - w;
29628                     break;
29629                     case "west":
29630                         x -= sw - w;
29631                         break;
29632                     case "northwest":
29633                         x -= sw - w;
29634                         y -= sh - h;
29635                     break;
29636                 }
29637                 w = sw;
29638                 h = sh;
29639             }
29640
29641             if(this.preserveRatio){
29642                 switch(pos){
29643                     case "southeast":
29644                     case "east":
29645                         h = oh * (w/ow);
29646                         h = Math.min(Math.max(mh, h), mxh);
29647                         w = ow * (h/oh);
29648                        break;
29649                     case "south":
29650                         w = ow * (h/oh);
29651                         w = Math.min(Math.max(mw, w), mxw);
29652                         h = oh * (w/ow);
29653                         break;
29654                     case "northeast":
29655                         w = ow * (h/oh);
29656                         w = Math.min(Math.max(mw, w), mxw);
29657                         h = oh * (w/ow);
29658                     break;
29659                     case "north":
29660                         var tw = w;
29661                         w = ow * (h/oh);
29662                         w = Math.min(Math.max(mw, w), mxw);
29663                         h = oh * (w/ow);
29664                         x += (tw - w) / 2;
29665                         break;
29666                     case "southwest":
29667                         h = oh * (w/ow);
29668                         h = Math.min(Math.max(mh, h), mxh);
29669                         var tw = w;
29670                         w = ow * (h/oh);
29671                         x += tw - w;
29672                         break;
29673                     case "west":
29674                         var th = h;
29675                         h = oh * (w/ow);
29676                         h = Math.min(Math.max(mh, h), mxh);
29677                         y += (th - h) / 2;
29678                         var tw = w;
29679                         w = ow * (h/oh);
29680                         x += tw - w;
29681                        break;
29682                     case "northwest":
29683                         var tw = w;
29684                         var th = h;
29685                         h = oh * (w/ow);
29686                         h = Math.min(Math.max(mh, h), mxh);
29687                         w = ow * (h/oh);
29688                         y += th - h;
29689                         x += tw - w;
29690                        break;
29691
29692                 }
29693             }
29694             if (pos == 'hdrag') {
29695                 w = ow;
29696             }
29697             this.proxy.setBounds(x, y, w, h);
29698             if(this.dynamic){
29699                 this.resizeElement();
29700             }
29701             }catch(e){}
29702         }
29703         this.fireEvent("resizing", this, x, y, w, h, e);
29704     },
29705
29706     // private
29707     handleOver : function(){
29708         if(this.enabled){
29709             this.el.addClass("x-resizable-over");
29710         }
29711     },
29712
29713     // private
29714     handleOut : function(){
29715         if(!this.resizing){
29716             this.el.removeClass("x-resizable-over");
29717         }
29718     },
29719
29720     /**
29721      * Returns the element this component is bound to.
29722      * @return {Roo.Element}
29723      */
29724     getEl : function(){
29725         return this.el;
29726     },
29727
29728     /**
29729      * Returns the resizeChild element (or null).
29730      * @return {Roo.Element}
29731      */
29732     getResizeChild : function(){
29733         return this.resizeChild;
29734     },
29735     groupHandler : function()
29736     {
29737         
29738     },
29739     /**
29740      * Destroys this resizable. If the element was wrapped and
29741      * removeEl is not true then the element remains.
29742      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29743      */
29744     destroy : function(removeEl){
29745         this.proxy.remove();
29746         if(this.overlay){
29747             this.overlay.removeAllListeners();
29748             this.overlay.remove();
29749         }
29750         var ps = Roo.Resizable.positions;
29751         for(var k in ps){
29752             if(typeof ps[k] != "function" && this[ps[k]]){
29753                 var h = this[ps[k]];
29754                 h.el.removeAllListeners();
29755                 h.el.remove();
29756             }
29757         }
29758         if(removeEl){
29759             this.el.update("");
29760             this.el.remove();
29761         }
29762     }
29763 });
29764
29765 // private
29766 // hash to map config positions to true positions
29767 Roo.Resizable.positions = {
29768     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29769     hd: "hdrag"
29770 };
29771
29772 // private
29773 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29774     if(!this.tpl){
29775         // only initialize the template if resizable is used
29776         var tpl = Roo.DomHelper.createTemplate(
29777             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29778         );
29779         tpl.compile();
29780         Roo.Resizable.Handle.prototype.tpl = tpl;
29781     }
29782     this.position = pos;
29783     this.rz = rz;
29784     // show north drag fro topdra
29785     var handlepos = pos == 'hdrag' ? 'north' : pos;
29786     
29787     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29788     if (pos == 'hdrag') {
29789         this.el.setStyle('cursor', 'pointer');
29790     }
29791     this.el.unselectable();
29792     if(transparent){
29793         this.el.setOpacity(0);
29794     }
29795     this.el.on("mousedown", this.onMouseDown, this);
29796     if(!disableTrackOver){
29797         this.el.on("mouseover", this.onMouseOver, this);
29798         this.el.on("mouseout", this.onMouseOut, this);
29799     }
29800 };
29801
29802 // private
29803 Roo.Resizable.Handle.prototype = {
29804     afterResize : function(rz){
29805         Roo.log('after?');
29806         // do nothing
29807     },
29808     // private
29809     onMouseDown : function(e){
29810         this.rz.onMouseDown(this, e);
29811     },
29812     // private
29813     onMouseOver : function(e){
29814         this.rz.handleOver(this, e);
29815     },
29816     // private
29817     onMouseOut : function(e){
29818         this.rz.handleOut(this, e);
29819     }
29820 };/*
29821  * Based on:
29822  * Ext JS Library 1.1.1
29823  * Copyright(c) 2006-2007, Ext JS, LLC.
29824  *
29825  * Originally Released Under LGPL - original licence link has changed is not relivant.
29826  *
29827  * Fork - LGPL
29828  * <script type="text/javascript">
29829  */
29830
29831 /**
29832  * @class Roo.Editor
29833  * @extends Roo.Component
29834  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29835  * @constructor
29836  * Create a new Editor
29837  * @param {Roo.form.Field} field The Field object (or descendant)
29838  * @param {Object} config The config object
29839  */
29840 Roo.Editor = function(field, config){
29841     Roo.Editor.superclass.constructor.call(this, config);
29842     this.field = field;
29843     this.addEvents({
29844         /**
29845              * @event beforestartedit
29846              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29847              * false from the handler of this event.
29848              * @param {Editor} this
29849              * @param {Roo.Element} boundEl The underlying element bound to this editor
29850              * @param {Mixed} value The field value being set
29851              */
29852         "beforestartedit" : true,
29853         /**
29854              * @event startedit
29855              * Fires when this editor is displayed
29856              * @param {Roo.Element} boundEl The underlying element bound to this editor
29857              * @param {Mixed} value The starting field value
29858              */
29859         "startedit" : true,
29860         /**
29861              * @event beforecomplete
29862              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29863              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29864              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29865              * event will not fire since no edit actually occurred.
29866              * @param {Editor} this
29867              * @param {Mixed} value The current field value
29868              * @param {Mixed} startValue The original field value
29869              */
29870         "beforecomplete" : true,
29871         /**
29872              * @event complete
29873              * Fires after editing is complete and any changed value has been written to the underlying field.
29874              * @param {Editor} this
29875              * @param {Mixed} value The current field value
29876              * @param {Mixed} startValue The original field value
29877              */
29878         "complete" : true,
29879         /**
29880          * @event specialkey
29881          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29882          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29883          * @param {Roo.form.Field} this
29884          * @param {Roo.EventObject} e The event object
29885          */
29886         "specialkey" : true
29887     });
29888 };
29889
29890 Roo.extend(Roo.Editor, Roo.Component, {
29891     /**
29892      * @cfg {Boolean/String} autosize
29893      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29894      * or "height" to adopt the height only (defaults to false)
29895      */
29896     /**
29897      * @cfg {Boolean} revertInvalid
29898      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29899      * validation fails (defaults to true)
29900      */
29901     /**
29902      * @cfg {Boolean} ignoreNoChange
29903      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29904      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29905      * will never be ignored.
29906      */
29907     /**
29908      * @cfg {Boolean} hideEl
29909      * False to keep the bound element visible while the editor is displayed (defaults to true)
29910      */
29911     /**
29912      * @cfg {Mixed} value
29913      * The data value of the underlying field (defaults to "")
29914      */
29915     value : "",
29916     /**
29917      * @cfg {String} alignment
29918      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29919      */
29920     alignment: "c-c?",
29921     /**
29922      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29923      * for bottom-right shadow (defaults to "frame")
29924      */
29925     shadow : "frame",
29926     /**
29927      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29928      */
29929     constrain : false,
29930     /**
29931      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29932      */
29933     completeOnEnter : false,
29934     /**
29935      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29936      */
29937     cancelOnEsc : false,
29938     /**
29939      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29940      */
29941     updateEl : false,
29942
29943     // private
29944     onRender : function(ct, position){
29945         this.el = new Roo.Layer({
29946             shadow: this.shadow,
29947             cls: "x-editor",
29948             parentEl : ct,
29949             shim : this.shim,
29950             shadowOffset:4,
29951             id: this.id,
29952             constrain: this.constrain
29953         });
29954         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29955         if(this.field.msgTarget != 'title'){
29956             this.field.msgTarget = 'qtip';
29957         }
29958         this.field.render(this.el);
29959         if(Roo.isGecko){
29960             this.field.el.dom.setAttribute('autocomplete', 'off');
29961         }
29962         this.field.on("specialkey", this.onSpecialKey, this);
29963         if(this.swallowKeys){
29964             this.field.el.swallowEvent(['keydown','keypress']);
29965         }
29966         this.field.show();
29967         this.field.on("blur", this.onBlur, this);
29968         if(this.field.grow){
29969             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29970         }
29971     },
29972
29973     onSpecialKey : function(field, e)
29974     {
29975         //Roo.log('editor onSpecialKey');
29976         if(this.completeOnEnter && e.getKey() == e.ENTER){
29977             e.stopEvent();
29978             this.completeEdit();
29979             return;
29980         }
29981         // do not fire special key otherwise it might hide close the editor...
29982         if(e.getKey() == e.ENTER){    
29983             return;
29984         }
29985         if(this.cancelOnEsc && e.getKey() == e.ESC){
29986             this.cancelEdit();
29987             return;
29988         } 
29989         this.fireEvent('specialkey', field, e);
29990     
29991     },
29992
29993     /**
29994      * Starts the editing process and shows the editor.
29995      * @param {String/HTMLElement/Element} el The element to edit
29996      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29997       * to the innerHTML of el.
29998      */
29999     startEdit : function(el, value){
30000         if(this.editing){
30001             this.completeEdit();
30002         }
30003         this.boundEl = Roo.get(el);
30004         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30005         if(!this.rendered){
30006             this.render(this.parentEl || document.body);
30007         }
30008         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30009             return;
30010         }
30011         this.startValue = v;
30012         this.field.setValue(v);
30013         if(this.autoSize){
30014             var sz = this.boundEl.getSize();
30015             switch(this.autoSize){
30016                 case "width":
30017                 this.setSize(sz.width,  "");
30018                 break;
30019                 case "height":
30020                 this.setSize("",  sz.height);
30021                 break;
30022                 default:
30023                 this.setSize(sz.width,  sz.height);
30024             }
30025         }
30026         this.el.alignTo(this.boundEl, this.alignment);
30027         this.editing = true;
30028         if(Roo.QuickTips){
30029             Roo.QuickTips.disable();
30030         }
30031         this.show();
30032     },
30033
30034     /**
30035      * Sets the height and width of this editor.
30036      * @param {Number} width The new width
30037      * @param {Number} height The new height
30038      */
30039     setSize : function(w, h){
30040         this.field.setSize(w, h);
30041         if(this.el){
30042             this.el.sync();
30043         }
30044     },
30045
30046     /**
30047      * Realigns the editor to the bound field based on the current alignment config value.
30048      */
30049     realign : function(){
30050         this.el.alignTo(this.boundEl, this.alignment);
30051     },
30052
30053     /**
30054      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30055      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30056      */
30057     completeEdit : function(remainVisible){
30058         if(!this.editing){
30059             return;
30060         }
30061         var v = this.getValue();
30062         if(this.revertInvalid !== false && !this.field.isValid()){
30063             v = this.startValue;
30064             this.cancelEdit(true);
30065         }
30066         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30067             this.editing = false;
30068             this.hide();
30069             return;
30070         }
30071         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30072             this.editing = false;
30073             if(this.updateEl && this.boundEl){
30074                 this.boundEl.update(v);
30075             }
30076             if(remainVisible !== true){
30077                 this.hide();
30078             }
30079             this.fireEvent("complete", this, v, this.startValue);
30080         }
30081     },
30082
30083     // private
30084     onShow : function(){
30085         this.el.show();
30086         if(this.hideEl !== false){
30087             this.boundEl.hide();
30088         }
30089         this.field.show();
30090         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30091             this.fixIEFocus = true;
30092             this.deferredFocus.defer(50, this);
30093         }else{
30094             this.field.focus();
30095         }
30096         this.fireEvent("startedit", this.boundEl, this.startValue);
30097     },
30098
30099     deferredFocus : function(){
30100         if(this.editing){
30101             this.field.focus();
30102         }
30103     },
30104
30105     /**
30106      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30107      * reverted to the original starting value.
30108      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30109      * cancel (defaults to false)
30110      */
30111     cancelEdit : function(remainVisible){
30112         if(this.editing){
30113             this.setValue(this.startValue);
30114             if(remainVisible !== true){
30115                 this.hide();
30116             }
30117         }
30118     },
30119
30120     // private
30121     onBlur : function(){
30122         if(this.allowBlur !== true && this.editing){
30123             this.completeEdit();
30124         }
30125     },
30126
30127     // private
30128     onHide : function(){
30129         if(this.editing){
30130             this.completeEdit();
30131             return;
30132         }
30133         this.field.blur();
30134         if(this.field.collapse){
30135             this.field.collapse();
30136         }
30137         this.el.hide();
30138         if(this.hideEl !== false){
30139             this.boundEl.show();
30140         }
30141         if(Roo.QuickTips){
30142             Roo.QuickTips.enable();
30143         }
30144     },
30145
30146     /**
30147      * Sets the data value of the editor
30148      * @param {Mixed} value Any valid value supported by the underlying field
30149      */
30150     setValue : function(v){
30151         this.field.setValue(v);
30152     },
30153
30154     /**
30155      * Gets the data value of the editor
30156      * @return {Mixed} The data value
30157      */
30158     getValue : function(){
30159         return this.field.getValue();
30160     }
30161 });/*
30162  * Based on:
30163  * Ext JS Library 1.1.1
30164  * Copyright(c) 2006-2007, Ext JS, LLC.
30165  *
30166  * Originally Released Under LGPL - original licence link has changed is not relivant.
30167  *
30168  * Fork - LGPL
30169  * <script type="text/javascript">
30170  */
30171  
30172 /**
30173  * @class Roo.BasicDialog
30174  * @extends Roo.util.Observable
30175  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30176  * <pre><code>
30177 var dlg = new Roo.BasicDialog("my-dlg", {
30178     height: 200,
30179     width: 300,
30180     minHeight: 100,
30181     minWidth: 150,
30182     modal: true,
30183     proxyDrag: true,
30184     shadow: true
30185 });
30186 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30187 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30188 dlg.addButton('Cancel', dlg.hide, dlg);
30189 dlg.show();
30190 </code></pre>
30191   <b>A Dialog should always be a direct child of the body element.</b>
30192  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30193  * @cfg {String} title Default text to display in the title bar (defaults to null)
30194  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30195  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30196  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30197  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30198  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30199  * (defaults to null with no animation)
30200  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30201  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30202  * property for valid values (defaults to 'all')
30203  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30204  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30205  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30206  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30207  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30208  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30209  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30210  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30211  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30212  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30213  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30214  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30215  * draggable = true (defaults to false)
30216  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30217  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30218  * shadow (defaults to false)
30219  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30220  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30221  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30222  * @cfg {Array} buttons Array of buttons
30223  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30224  * @constructor
30225  * Create a new BasicDialog.
30226  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30227  * @param {Object} config Configuration options
30228  */
30229 Roo.BasicDialog = function(el, config){
30230     this.el = Roo.get(el);
30231     var dh = Roo.DomHelper;
30232     if(!this.el && config && config.autoCreate){
30233         if(typeof config.autoCreate == "object"){
30234             if(!config.autoCreate.id){
30235                 config.autoCreate.id = el;
30236             }
30237             this.el = dh.append(document.body,
30238                         config.autoCreate, true);
30239         }else{
30240             this.el = dh.append(document.body,
30241                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30242         }
30243     }
30244     el = this.el;
30245     el.setDisplayed(true);
30246     el.hide = this.hideAction;
30247     this.id = el.id;
30248     el.addClass("x-dlg");
30249
30250     Roo.apply(this, config);
30251
30252     this.proxy = el.createProxy("x-dlg-proxy");
30253     this.proxy.hide = this.hideAction;
30254     this.proxy.setOpacity(.5);
30255     this.proxy.hide();
30256
30257     if(config.width){
30258         el.setWidth(config.width);
30259     }
30260     if(config.height){
30261         el.setHeight(config.height);
30262     }
30263     this.size = el.getSize();
30264     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30265         this.xy = [config.x,config.y];
30266     }else{
30267         this.xy = el.getCenterXY(true);
30268     }
30269     /** The header element @type Roo.Element */
30270     this.header = el.child("> .x-dlg-hd");
30271     /** The body element @type Roo.Element */
30272     this.body = el.child("> .x-dlg-bd");
30273     /** The footer element @type Roo.Element */
30274     this.footer = el.child("> .x-dlg-ft");
30275
30276     if(!this.header){
30277         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30278     }
30279     if(!this.body){
30280         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30281     }
30282
30283     this.header.unselectable();
30284     if(this.title){
30285         this.header.update(this.title);
30286     }
30287     // this element allows the dialog to be focused for keyboard event
30288     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30289     this.focusEl.swallowEvent("click", true);
30290
30291     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30292
30293     // wrap the body and footer for special rendering
30294     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30295     if(this.footer){
30296         this.bwrap.dom.appendChild(this.footer.dom);
30297     }
30298
30299     this.bg = this.el.createChild({
30300         tag: "div", cls:"x-dlg-bg",
30301         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30302     });
30303     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30304
30305
30306     if(this.autoScroll !== false && !this.autoTabs){
30307         this.body.setStyle("overflow", "auto");
30308     }
30309
30310     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30311
30312     if(this.closable !== false){
30313         this.el.addClass("x-dlg-closable");
30314         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30315         this.close.on("click", this.closeClick, this);
30316         this.close.addClassOnOver("x-dlg-close-over");
30317     }
30318     if(this.collapsible !== false){
30319         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30320         this.collapseBtn.on("click", this.collapseClick, this);
30321         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30322         this.header.on("dblclick", this.collapseClick, this);
30323     }
30324     if(this.resizable !== false){
30325         this.el.addClass("x-dlg-resizable");
30326         this.resizer = new Roo.Resizable(el, {
30327             minWidth: this.minWidth || 80,
30328             minHeight:this.minHeight || 80,
30329             handles: this.resizeHandles || "all",
30330             pinned: true
30331         });
30332         this.resizer.on("beforeresize", this.beforeResize, this);
30333         this.resizer.on("resize", this.onResize, this);
30334     }
30335     if(this.draggable !== false){
30336         el.addClass("x-dlg-draggable");
30337         if (!this.proxyDrag) {
30338             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30339         }
30340         else {
30341             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30342         }
30343         dd.setHandleElId(this.header.id);
30344         dd.endDrag = this.endMove.createDelegate(this);
30345         dd.startDrag = this.startMove.createDelegate(this);
30346         dd.onDrag = this.onDrag.createDelegate(this);
30347         dd.scroll = false;
30348         this.dd = dd;
30349     }
30350     if(this.modal){
30351         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30352         this.mask.enableDisplayMode("block");
30353         this.mask.hide();
30354         this.el.addClass("x-dlg-modal");
30355     }
30356     if(this.shadow){
30357         this.shadow = new Roo.Shadow({
30358             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30359             offset : this.shadowOffset
30360         });
30361     }else{
30362         this.shadowOffset = 0;
30363     }
30364     if(Roo.useShims && this.shim !== false){
30365         this.shim = this.el.createShim();
30366         this.shim.hide = this.hideAction;
30367         this.shim.hide();
30368     }else{
30369         this.shim = false;
30370     }
30371     if(this.autoTabs){
30372         this.initTabs();
30373     }
30374     if (this.buttons) { 
30375         var bts= this.buttons;
30376         this.buttons = [];
30377         Roo.each(bts, function(b) {
30378             this.addButton(b);
30379         }, this);
30380     }
30381     
30382     
30383     this.addEvents({
30384         /**
30385          * @event keydown
30386          * Fires when a key is pressed
30387          * @param {Roo.BasicDialog} this
30388          * @param {Roo.EventObject} e
30389          */
30390         "keydown" : true,
30391         /**
30392          * @event move
30393          * Fires when this dialog is moved by the user.
30394          * @param {Roo.BasicDialog} this
30395          * @param {Number} x The new page X
30396          * @param {Number} y The new page Y
30397          */
30398         "move" : true,
30399         /**
30400          * @event resize
30401          * Fires when this dialog is resized by the user.
30402          * @param {Roo.BasicDialog} this
30403          * @param {Number} width The new width
30404          * @param {Number} height The new height
30405          */
30406         "resize" : true,
30407         /**
30408          * @event beforehide
30409          * Fires before this dialog is hidden.
30410          * @param {Roo.BasicDialog} this
30411          */
30412         "beforehide" : true,
30413         /**
30414          * @event hide
30415          * Fires when this dialog is hidden.
30416          * @param {Roo.BasicDialog} this
30417          */
30418         "hide" : true,
30419         /**
30420          * @event beforeshow
30421          * Fires before this dialog is shown.
30422          * @param {Roo.BasicDialog} this
30423          */
30424         "beforeshow" : true,
30425         /**
30426          * @event show
30427          * Fires when this dialog is shown.
30428          * @param {Roo.BasicDialog} this
30429          */
30430         "show" : true
30431     });
30432     el.on("keydown", this.onKeyDown, this);
30433     el.on("mousedown", this.toFront, this);
30434     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30435     this.el.hide();
30436     Roo.DialogManager.register(this);
30437     Roo.BasicDialog.superclass.constructor.call(this);
30438 };
30439
30440 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30441     shadowOffset: Roo.isIE ? 6 : 5,
30442     minHeight: 80,
30443     minWidth: 200,
30444     minButtonWidth: 75,
30445     defaultButton: null,
30446     buttonAlign: "right",
30447     tabTag: 'div',
30448     firstShow: true,
30449
30450     /**
30451      * Sets the dialog title text
30452      * @param {String} text The title text to display
30453      * @return {Roo.BasicDialog} this
30454      */
30455     setTitle : function(text){
30456         this.header.update(text);
30457         return this;
30458     },
30459
30460     // private
30461     closeClick : function(){
30462         this.hide();
30463     },
30464
30465     // private
30466     collapseClick : function(){
30467         this[this.collapsed ? "expand" : "collapse"]();
30468     },
30469
30470     /**
30471      * Collapses the dialog to its minimized state (only the title bar is visible).
30472      * Equivalent to the user clicking the collapse dialog button.
30473      */
30474     collapse : function(){
30475         if(!this.collapsed){
30476             this.collapsed = true;
30477             this.el.addClass("x-dlg-collapsed");
30478             this.restoreHeight = this.el.getHeight();
30479             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30480         }
30481     },
30482
30483     /**
30484      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30485      * clicking the expand dialog button.
30486      */
30487     expand : function(){
30488         if(this.collapsed){
30489             this.collapsed = false;
30490             this.el.removeClass("x-dlg-collapsed");
30491             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30492         }
30493     },
30494
30495     /**
30496      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30497      * @return {Roo.TabPanel} The tabs component
30498      */
30499     initTabs : function(){
30500         var tabs = this.getTabs();
30501         while(tabs.getTab(0)){
30502             tabs.removeTab(0);
30503         }
30504         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30505             var dom = el.dom;
30506             tabs.addTab(Roo.id(dom), dom.title);
30507             dom.title = "";
30508         });
30509         tabs.activate(0);
30510         return tabs;
30511     },
30512
30513     // private
30514     beforeResize : function(){
30515         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30516     },
30517
30518     // private
30519     onResize : function(){
30520         this.refreshSize();
30521         this.syncBodyHeight();
30522         this.adjustAssets();
30523         this.focus();
30524         this.fireEvent("resize", this, this.size.width, this.size.height);
30525     },
30526
30527     // private
30528     onKeyDown : function(e){
30529         if(this.isVisible()){
30530             this.fireEvent("keydown", this, e);
30531         }
30532     },
30533
30534     /**
30535      * Resizes the dialog.
30536      * @param {Number} width
30537      * @param {Number} height
30538      * @return {Roo.BasicDialog} this
30539      */
30540     resizeTo : function(width, height){
30541         this.el.setSize(width, height);
30542         this.size = {width: width, height: height};
30543         this.syncBodyHeight();
30544         if(this.fixedcenter){
30545             this.center();
30546         }
30547         if(this.isVisible()){
30548             this.constrainXY();
30549             this.adjustAssets();
30550         }
30551         this.fireEvent("resize", this, width, height);
30552         return this;
30553     },
30554
30555
30556     /**
30557      * Resizes the dialog to fit the specified content size.
30558      * @param {Number} width
30559      * @param {Number} height
30560      * @return {Roo.BasicDialog} this
30561      */
30562     setContentSize : function(w, h){
30563         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30564         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30565         //if(!this.el.isBorderBox()){
30566             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30567             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30568         //}
30569         if(this.tabs){
30570             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30571             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30572         }
30573         this.resizeTo(w, h);
30574         return this;
30575     },
30576
30577     /**
30578      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30579      * executed in response to a particular key being pressed while the dialog is active.
30580      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30581      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30582      * @param {Function} fn The function to call
30583      * @param {Object} scope (optional) The scope of the function
30584      * @return {Roo.BasicDialog} this
30585      */
30586     addKeyListener : function(key, fn, scope){
30587         var keyCode, shift, ctrl, alt;
30588         if(typeof key == "object" && !(key instanceof Array)){
30589             keyCode = key["key"];
30590             shift = key["shift"];
30591             ctrl = key["ctrl"];
30592             alt = key["alt"];
30593         }else{
30594             keyCode = key;
30595         }
30596         var handler = function(dlg, e){
30597             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30598                 var k = e.getKey();
30599                 if(keyCode instanceof Array){
30600                     for(var i = 0, len = keyCode.length; i < len; i++){
30601                         if(keyCode[i] == k){
30602                           fn.call(scope || window, dlg, k, e);
30603                           return;
30604                         }
30605                     }
30606                 }else{
30607                     if(k == keyCode){
30608                         fn.call(scope || window, dlg, k, e);
30609                     }
30610                 }
30611             }
30612         };
30613         this.on("keydown", handler);
30614         return this;
30615     },
30616
30617     /**
30618      * Returns the TabPanel component (creates it if it doesn't exist).
30619      * Note: If you wish to simply check for the existence of tabs without creating them,
30620      * check for a null 'tabs' property.
30621      * @return {Roo.TabPanel} The tabs component
30622      */
30623     getTabs : function(){
30624         if(!this.tabs){
30625             this.el.addClass("x-dlg-auto-tabs");
30626             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30627             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30628         }
30629         return this.tabs;
30630     },
30631
30632     /**
30633      * Adds a button to the footer section of the dialog.
30634      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30635      * object or a valid Roo.DomHelper element config
30636      * @param {Function} handler The function called when the button is clicked
30637      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30638      * @return {Roo.Button} The new button
30639      */
30640     addButton : function(config, handler, scope){
30641         var dh = Roo.DomHelper;
30642         if(!this.footer){
30643             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30644         }
30645         if(!this.btnContainer){
30646             var tb = this.footer.createChild({
30647
30648                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30649                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30650             }, null, true);
30651             this.btnContainer = tb.firstChild.firstChild.firstChild;
30652         }
30653         var bconfig = {
30654             handler: handler,
30655             scope: scope,
30656             minWidth: this.minButtonWidth,
30657             hideParent:true
30658         };
30659         if(typeof config == "string"){
30660             bconfig.text = config;
30661         }else{
30662             if(config.tag){
30663                 bconfig.dhconfig = config;
30664             }else{
30665                 Roo.apply(bconfig, config);
30666             }
30667         }
30668         var fc = false;
30669         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30670             bconfig.position = Math.max(0, bconfig.position);
30671             fc = this.btnContainer.childNodes[bconfig.position];
30672         }
30673          
30674         var btn = new Roo.Button(
30675             fc ? 
30676                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30677                 : this.btnContainer.appendChild(document.createElement("td")),
30678             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30679             bconfig
30680         );
30681         this.syncBodyHeight();
30682         if(!this.buttons){
30683             /**
30684              * Array of all the buttons that have been added to this dialog via addButton
30685              * @type Array
30686              */
30687             this.buttons = [];
30688         }
30689         this.buttons.push(btn);
30690         return btn;
30691     },
30692
30693     /**
30694      * Sets the default button to be focused when the dialog is displayed.
30695      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30696      * @return {Roo.BasicDialog} this
30697      */
30698     setDefaultButton : function(btn){
30699         this.defaultButton = btn;
30700         return this;
30701     },
30702
30703     // private
30704     getHeaderFooterHeight : function(safe){
30705         var height = 0;
30706         if(this.header){
30707            height += this.header.getHeight();
30708         }
30709         if(this.footer){
30710            var fm = this.footer.getMargins();
30711             height += (this.footer.getHeight()+fm.top+fm.bottom);
30712         }
30713         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30714         height += this.centerBg.getPadding("tb");
30715         return height;
30716     },
30717
30718     // private
30719     syncBodyHeight : function()
30720     {
30721         var bd = this.body, // the text
30722             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30723             bw = this.bwrap;
30724         var height = this.size.height - this.getHeaderFooterHeight(false);
30725         bd.setHeight(height-bd.getMargins("tb"));
30726         var hh = this.header.getHeight();
30727         var h = this.size.height-hh;
30728         cb.setHeight(h);
30729         
30730         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30731         bw.setHeight(h-cb.getPadding("tb"));
30732         
30733         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30734         bd.setWidth(bw.getWidth(true));
30735         if(this.tabs){
30736             this.tabs.syncHeight();
30737             if(Roo.isIE){
30738                 this.tabs.el.repaint();
30739             }
30740         }
30741     },
30742
30743     /**
30744      * Restores the previous state of the dialog if Roo.state is configured.
30745      * @return {Roo.BasicDialog} this
30746      */
30747     restoreState : function(){
30748         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30749         if(box && box.width){
30750             this.xy = [box.x, box.y];
30751             this.resizeTo(box.width, box.height);
30752         }
30753         return this;
30754     },
30755
30756     // private
30757     beforeShow : function(){
30758         this.expand();
30759         if(this.fixedcenter){
30760             this.xy = this.el.getCenterXY(true);
30761         }
30762         if(this.modal){
30763             Roo.get(document.body).addClass("x-body-masked");
30764             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30765             this.mask.show();
30766         }
30767         this.constrainXY();
30768     },
30769
30770     // private
30771     animShow : function(){
30772         var b = Roo.get(this.animateTarget).getBox();
30773         this.proxy.setSize(b.width, b.height);
30774         this.proxy.setLocation(b.x, b.y);
30775         this.proxy.show();
30776         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30777                     true, .35, this.showEl.createDelegate(this));
30778     },
30779
30780     /**
30781      * Shows the dialog.
30782      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30783      * @return {Roo.BasicDialog} this
30784      */
30785     show : function(animateTarget){
30786         if (this.fireEvent("beforeshow", this) === false){
30787             return;
30788         }
30789         if(this.syncHeightBeforeShow){
30790             this.syncBodyHeight();
30791         }else if(this.firstShow){
30792             this.firstShow = false;
30793             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30794         }
30795         this.animateTarget = animateTarget || this.animateTarget;
30796         if(!this.el.isVisible()){
30797             this.beforeShow();
30798             if(this.animateTarget && Roo.get(this.animateTarget)){
30799                 this.animShow();
30800             }else{
30801                 this.showEl();
30802             }
30803         }
30804         return this;
30805     },
30806
30807     // private
30808     showEl : function(){
30809         this.proxy.hide();
30810         this.el.setXY(this.xy);
30811         this.el.show();
30812         this.adjustAssets(true);
30813         this.toFront();
30814         this.focus();
30815         // IE peekaboo bug - fix found by Dave Fenwick
30816         if(Roo.isIE){
30817             this.el.repaint();
30818         }
30819         this.fireEvent("show", this);
30820     },
30821
30822     /**
30823      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30824      * dialog itself will receive focus.
30825      */
30826     focus : function(){
30827         if(this.defaultButton){
30828             this.defaultButton.focus();
30829         }else{
30830             this.focusEl.focus();
30831         }
30832     },
30833
30834     // private
30835     constrainXY : function(){
30836         if(this.constraintoviewport !== false){
30837             if(!this.viewSize){
30838                 if(this.container){
30839                     var s = this.container.getSize();
30840                     this.viewSize = [s.width, s.height];
30841                 }else{
30842                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30843                 }
30844             }
30845             var s = Roo.get(this.container||document).getScroll();
30846
30847             var x = this.xy[0], y = this.xy[1];
30848             var w = this.size.width, h = this.size.height;
30849             var vw = this.viewSize[0], vh = this.viewSize[1];
30850             // only move it if it needs it
30851             var moved = false;
30852             // first validate right/bottom
30853             if(x + w > vw+s.left){
30854                 x = vw - w;
30855                 moved = true;
30856             }
30857             if(y + h > vh+s.top){
30858                 y = vh - h;
30859                 moved = true;
30860             }
30861             // then make sure top/left isn't negative
30862             if(x < s.left){
30863                 x = s.left;
30864                 moved = true;
30865             }
30866             if(y < s.top){
30867                 y = s.top;
30868                 moved = true;
30869             }
30870             if(moved){
30871                 // cache xy
30872                 this.xy = [x, y];
30873                 if(this.isVisible()){
30874                     this.el.setLocation(x, y);
30875                     this.adjustAssets();
30876                 }
30877             }
30878         }
30879     },
30880
30881     // private
30882     onDrag : function(){
30883         if(!this.proxyDrag){
30884             this.xy = this.el.getXY();
30885             this.adjustAssets();
30886         }
30887     },
30888
30889     // private
30890     adjustAssets : function(doShow){
30891         var x = this.xy[0], y = this.xy[1];
30892         var w = this.size.width, h = this.size.height;
30893         if(doShow === true){
30894             if(this.shadow){
30895                 this.shadow.show(this.el);
30896             }
30897             if(this.shim){
30898                 this.shim.show();
30899             }
30900         }
30901         if(this.shadow && this.shadow.isVisible()){
30902             this.shadow.show(this.el);
30903         }
30904         if(this.shim && this.shim.isVisible()){
30905             this.shim.setBounds(x, y, w, h);
30906         }
30907     },
30908
30909     // private
30910     adjustViewport : function(w, h){
30911         if(!w || !h){
30912             w = Roo.lib.Dom.getViewWidth();
30913             h = Roo.lib.Dom.getViewHeight();
30914         }
30915         // cache the size
30916         this.viewSize = [w, h];
30917         if(this.modal && this.mask.isVisible()){
30918             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30919             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30920         }
30921         if(this.isVisible()){
30922             this.constrainXY();
30923         }
30924     },
30925
30926     /**
30927      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30928      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30929      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30930      */
30931     destroy : function(removeEl){
30932         if(this.isVisible()){
30933             this.animateTarget = null;
30934             this.hide();
30935         }
30936         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30937         if(this.tabs){
30938             this.tabs.destroy(removeEl);
30939         }
30940         Roo.destroy(
30941              this.shim,
30942              this.proxy,
30943              this.resizer,
30944              this.close,
30945              this.mask
30946         );
30947         if(this.dd){
30948             this.dd.unreg();
30949         }
30950         if(this.buttons){
30951            for(var i = 0, len = this.buttons.length; i < len; i++){
30952                this.buttons[i].destroy();
30953            }
30954         }
30955         this.el.removeAllListeners();
30956         if(removeEl === true){
30957             this.el.update("");
30958             this.el.remove();
30959         }
30960         Roo.DialogManager.unregister(this);
30961     },
30962
30963     // private
30964     startMove : function(){
30965         if(this.proxyDrag){
30966             this.proxy.show();
30967         }
30968         if(this.constraintoviewport !== false){
30969             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30970         }
30971     },
30972
30973     // private
30974     endMove : function(){
30975         if(!this.proxyDrag){
30976             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30977         }else{
30978             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30979             this.proxy.hide();
30980         }
30981         this.refreshSize();
30982         this.adjustAssets();
30983         this.focus();
30984         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30985     },
30986
30987     /**
30988      * Brings this dialog to the front of any other visible dialogs
30989      * @return {Roo.BasicDialog} this
30990      */
30991     toFront : function(){
30992         Roo.DialogManager.bringToFront(this);
30993         return this;
30994     },
30995
30996     /**
30997      * Sends this dialog to the back (under) of any other visible dialogs
30998      * @return {Roo.BasicDialog} this
30999      */
31000     toBack : function(){
31001         Roo.DialogManager.sendToBack(this);
31002         return this;
31003     },
31004
31005     /**
31006      * Centers this dialog in the viewport
31007      * @return {Roo.BasicDialog} this
31008      */
31009     center : function(){
31010         var xy = this.el.getCenterXY(true);
31011         this.moveTo(xy[0], xy[1]);
31012         return this;
31013     },
31014
31015     /**
31016      * Moves the dialog's top-left corner to the specified point
31017      * @param {Number} x
31018      * @param {Number} y
31019      * @return {Roo.BasicDialog} this
31020      */
31021     moveTo : function(x, y){
31022         this.xy = [x,y];
31023         if(this.isVisible()){
31024             this.el.setXY(this.xy);
31025             this.adjustAssets();
31026         }
31027         return this;
31028     },
31029
31030     /**
31031      * Aligns the dialog to the specified element
31032      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31033      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31034      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31035      * @return {Roo.BasicDialog} this
31036      */
31037     alignTo : function(element, position, offsets){
31038         this.xy = this.el.getAlignToXY(element, position, offsets);
31039         if(this.isVisible()){
31040             this.el.setXY(this.xy);
31041             this.adjustAssets();
31042         }
31043         return this;
31044     },
31045
31046     /**
31047      * Anchors an element to another element and realigns it when the window is resized.
31048      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31049      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31050      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31051      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31052      * is a number, it is used as the buffer delay (defaults to 50ms).
31053      * @return {Roo.BasicDialog} this
31054      */
31055     anchorTo : function(el, alignment, offsets, monitorScroll){
31056         var action = function(){
31057             this.alignTo(el, alignment, offsets);
31058         };
31059         Roo.EventManager.onWindowResize(action, this);
31060         var tm = typeof monitorScroll;
31061         if(tm != 'undefined'){
31062             Roo.EventManager.on(window, 'scroll', action, this,
31063                 {buffer: tm == 'number' ? monitorScroll : 50});
31064         }
31065         action.call(this);
31066         return this;
31067     },
31068
31069     /**
31070      * Returns true if the dialog is visible
31071      * @return {Boolean}
31072      */
31073     isVisible : function(){
31074         return this.el.isVisible();
31075     },
31076
31077     // private
31078     animHide : function(callback){
31079         var b = Roo.get(this.animateTarget).getBox();
31080         this.proxy.show();
31081         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31082         this.el.hide();
31083         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31084                     this.hideEl.createDelegate(this, [callback]));
31085     },
31086
31087     /**
31088      * Hides the dialog.
31089      * @param {Function} callback (optional) Function to call when the dialog is hidden
31090      * @return {Roo.BasicDialog} this
31091      */
31092     hide : function(callback){
31093         if (this.fireEvent("beforehide", this) === false){
31094             return;
31095         }
31096         if(this.shadow){
31097             this.shadow.hide();
31098         }
31099         if(this.shim) {
31100           this.shim.hide();
31101         }
31102         // sometimes animateTarget seems to get set.. causing problems...
31103         // this just double checks..
31104         if(this.animateTarget && Roo.get(this.animateTarget)) {
31105            this.animHide(callback);
31106         }else{
31107             this.el.hide();
31108             this.hideEl(callback);
31109         }
31110         return this;
31111     },
31112
31113     // private
31114     hideEl : function(callback){
31115         this.proxy.hide();
31116         if(this.modal){
31117             this.mask.hide();
31118             Roo.get(document.body).removeClass("x-body-masked");
31119         }
31120         this.fireEvent("hide", this);
31121         if(typeof callback == "function"){
31122             callback();
31123         }
31124     },
31125
31126     // private
31127     hideAction : function(){
31128         this.setLeft("-10000px");
31129         this.setTop("-10000px");
31130         this.setStyle("visibility", "hidden");
31131     },
31132
31133     // private
31134     refreshSize : function(){
31135         this.size = this.el.getSize();
31136         this.xy = this.el.getXY();
31137         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31138     },
31139
31140     // private
31141     // z-index is managed by the DialogManager and may be overwritten at any time
31142     setZIndex : function(index){
31143         if(this.modal){
31144             this.mask.setStyle("z-index", index);
31145         }
31146         if(this.shim){
31147             this.shim.setStyle("z-index", ++index);
31148         }
31149         if(this.shadow){
31150             this.shadow.setZIndex(++index);
31151         }
31152         this.el.setStyle("z-index", ++index);
31153         if(this.proxy){
31154             this.proxy.setStyle("z-index", ++index);
31155         }
31156         if(this.resizer){
31157             this.resizer.proxy.setStyle("z-index", ++index);
31158         }
31159
31160         this.lastZIndex = index;
31161     },
31162
31163     /**
31164      * Returns the element for this dialog
31165      * @return {Roo.Element} The underlying dialog Element
31166      */
31167     getEl : function(){
31168         return this.el;
31169     }
31170 });
31171
31172 /**
31173  * @class Roo.DialogManager
31174  * Provides global access to BasicDialogs that have been created and
31175  * support for z-indexing (layering) multiple open dialogs.
31176  */
31177 Roo.DialogManager = function(){
31178     var list = {};
31179     var accessList = [];
31180     var front = null;
31181
31182     // private
31183     var sortDialogs = function(d1, d2){
31184         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31185     };
31186
31187     // private
31188     var orderDialogs = function(){
31189         accessList.sort(sortDialogs);
31190         var seed = Roo.DialogManager.zseed;
31191         for(var i = 0, len = accessList.length; i < len; i++){
31192             var dlg = accessList[i];
31193             if(dlg){
31194                 dlg.setZIndex(seed + (i*10));
31195             }
31196         }
31197     };
31198
31199     return {
31200         /**
31201          * The starting z-index for BasicDialogs (defaults to 9000)
31202          * @type Number The z-index value
31203          */
31204         zseed : 9000,
31205
31206         // private
31207         register : function(dlg){
31208             list[dlg.id] = dlg;
31209             accessList.push(dlg);
31210         },
31211
31212         // private
31213         unregister : function(dlg){
31214             delete list[dlg.id];
31215             var i=0;
31216             var len=0;
31217             if(!accessList.indexOf){
31218                 for(  i = 0, len = accessList.length; i < len; i++){
31219                     if(accessList[i] == dlg){
31220                         accessList.splice(i, 1);
31221                         return;
31222                     }
31223                 }
31224             }else{
31225                  i = accessList.indexOf(dlg);
31226                 if(i != -1){
31227                     accessList.splice(i, 1);
31228                 }
31229             }
31230         },
31231
31232         /**
31233          * Gets a registered dialog by id
31234          * @param {String/Object} id The id of the dialog or a dialog
31235          * @return {Roo.BasicDialog} this
31236          */
31237         get : function(id){
31238             return typeof id == "object" ? id : list[id];
31239         },
31240
31241         /**
31242          * Brings the specified dialog to the front
31243          * @param {String/Object} dlg The id of the dialog or a dialog
31244          * @return {Roo.BasicDialog} this
31245          */
31246         bringToFront : function(dlg){
31247             dlg = this.get(dlg);
31248             if(dlg != front){
31249                 front = dlg;
31250                 dlg._lastAccess = new Date().getTime();
31251                 orderDialogs();
31252             }
31253             return dlg;
31254         },
31255
31256         /**
31257          * Sends the specified dialog to the back
31258          * @param {String/Object} dlg The id of the dialog or a dialog
31259          * @return {Roo.BasicDialog} this
31260          */
31261         sendToBack : function(dlg){
31262             dlg = this.get(dlg);
31263             dlg._lastAccess = -(new Date().getTime());
31264             orderDialogs();
31265             return dlg;
31266         },
31267
31268         /**
31269          * Hides all dialogs
31270          */
31271         hideAll : function(){
31272             for(var id in list){
31273                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31274                     list[id].hide();
31275                 }
31276             }
31277         }
31278     };
31279 }();
31280
31281 /**
31282  * @class Roo.LayoutDialog
31283  * @extends Roo.BasicDialog
31284  * Dialog which provides adjustments for working with a layout in a Dialog.
31285  * Add your necessary layout config options to the dialog's config.<br>
31286  * Example usage (including a nested layout):
31287  * <pre><code>
31288 if(!dialog){
31289     dialog = new Roo.LayoutDialog("download-dlg", {
31290         modal: true,
31291         width:600,
31292         height:450,
31293         shadow:true,
31294         minWidth:500,
31295         minHeight:350,
31296         autoTabs:true,
31297         proxyDrag:true,
31298         // layout config merges with the dialog config
31299         center:{
31300             tabPosition: "top",
31301             alwaysShowTabs: true
31302         }
31303     });
31304     dialog.addKeyListener(27, dialog.hide, dialog);
31305     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31306     dialog.addButton("Build It!", this.getDownload, this);
31307
31308     // we can even add nested layouts
31309     var innerLayout = new Roo.BorderLayout("dl-inner", {
31310         east: {
31311             initialSize: 200,
31312             autoScroll:true,
31313             split:true
31314         },
31315         center: {
31316             autoScroll:true
31317         }
31318     });
31319     innerLayout.beginUpdate();
31320     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31321     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31322     innerLayout.endUpdate(true);
31323
31324     var layout = dialog.getLayout();
31325     layout.beginUpdate();
31326     layout.add("center", new Roo.ContentPanel("standard-panel",
31327                         {title: "Download the Source", fitToFrame:true}));
31328     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31329                {title: "Build your own roo.js"}));
31330     layout.getRegion("center").showPanel(sp);
31331     layout.endUpdate();
31332 }
31333 </code></pre>
31334     * @constructor
31335     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31336     * @param {Object} config configuration options
31337   */
31338 Roo.LayoutDialog = function(el, cfg){
31339     
31340     var config=  cfg;
31341     if (typeof(cfg) == 'undefined') {
31342         config = Roo.apply({}, el);
31343         // not sure why we use documentElement here.. - it should always be body.
31344         // IE7 borks horribly if we use documentElement.
31345         // webkit also does not like documentElement - it creates a body element...
31346         el = Roo.get( document.body || document.documentElement ).createChild();
31347         //config.autoCreate = true;
31348     }
31349     
31350     
31351     config.autoTabs = false;
31352     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31353     this.body.setStyle({overflow:"hidden", position:"relative"});
31354     this.layout = new Roo.BorderLayout(this.body.dom, config);
31355     this.layout.monitorWindowResize = false;
31356     this.el.addClass("x-dlg-auto-layout");
31357     // fix case when center region overwrites center function
31358     this.center = Roo.BasicDialog.prototype.center;
31359     this.on("show", this.layout.layout, this.layout, true);
31360     if (config.items) {
31361         var xitems = config.items;
31362         delete config.items;
31363         Roo.each(xitems, this.addxtype, this);
31364     }
31365     
31366     
31367 };
31368 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31369     /**
31370      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31371      * @deprecated
31372      */
31373     endUpdate : function(){
31374         this.layout.endUpdate();
31375     },
31376
31377     /**
31378      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31379      *  @deprecated
31380      */
31381     beginUpdate : function(){
31382         this.layout.beginUpdate();
31383     },
31384
31385     /**
31386      * Get the BorderLayout for this dialog
31387      * @return {Roo.BorderLayout}
31388      */
31389     getLayout : function(){
31390         return this.layout;
31391     },
31392
31393     showEl : function(){
31394         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31395         if(Roo.isIE7){
31396             this.layout.layout();
31397         }
31398     },
31399
31400     // private
31401     // Use the syncHeightBeforeShow config option to control this automatically
31402     syncBodyHeight : function(){
31403         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31404         if(this.layout){this.layout.layout();}
31405     },
31406     
31407       /**
31408      * Add an xtype element (actually adds to the layout.)
31409      * @return {Object} xdata xtype object data.
31410      */
31411     
31412     addxtype : function(c) {
31413         return this.layout.addxtype(c);
31414     }
31415 });/*
31416  * Based on:
31417  * Ext JS Library 1.1.1
31418  * Copyright(c) 2006-2007, Ext JS, LLC.
31419  *
31420  * Originally Released Under LGPL - original licence link has changed is not relivant.
31421  *
31422  * Fork - LGPL
31423  * <script type="text/javascript">
31424  */
31425  
31426 /**
31427  * @class Roo.MessageBox
31428  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31429  * Example usage:
31430  *<pre><code>
31431 // Basic alert:
31432 Roo.Msg.alert('Status', 'Changes saved successfully.');
31433
31434 // Prompt for user data:
31435 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31436     if (btn == 'ok'){
31437         // process text value...
31438     }
31439 });
31440
31441 // Show a dialog using config options:
31442 Roo.Msg.show({
31443    title:'Save Changes?',
31444    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31445    buttons: Roo.Msg.YESNOCANCEL,
31446    fn: processResult,
31447    animEl: 'elId'
31448 });
31449 </code></pre>
31450  * @singleton
31451  */
31452 Roo.MessageBox = function(){
31453     var dlg, opt, mask, waitTimer;
31454     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31455     var buttons, activeTextEl, bwidth;
31456
31457     // private
31458     var handleButton = function(button){
31459         dlg.hide();
31460         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31461     };
31462
31463     // private
31464     var handleHide = function(){
31465         if(opt && opt.cls){
31466             dlg.el.removeClass(opt.cls);
31467         }
31468         if(waitTimer){
31469             Roo.TaskMgr.stop(waitTimer);
31470             waitTimer = null;
31471         }
31472     };
31473
31474     // private
31475     var updateButtons = function(b){
31476         var width = 0;
31477         if(!b){
31478             buttons["ok"].hide();
31479             buttons["cancel"].hide();
31480             buttons["yes"].hide();
31481             buttons["no"].hide();
31482             dlg.footer.dom.style.display = 'none';
31483             return width;
31484         }
31485         dlg.footer.dom.style.display = '';
31486         for(var k in buttons){
31487             if(typeof buttons[k] != "function"){
31488                 if(b[k]){
31489                     buttons[k].show();
31490                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31491                     width += buttons[k].el.getWidth()+15;
31492                 }else{
31493                     buttons[k].hide();
31494                 }
31495             }
31496         }
31497         return width;
31498     };
31499
31500     // private
31501     var handleEsc = function(d, k, e){
31502         if(opt && opt.closable !== false){
31503             dlg.hide();
31504         }
31505         if(e){
31506             e.stopEvent();
31507         }
31508     };
31509
31510     return {
31511         /**
31512          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31513          * @return {Roo.BasicDialog} The BasicDialog element
31514          */
31515         getDialog : function(){
31516            if(!dlg){
31517                 dlg = new Roo.BasicDialog("x-msg-box", {
31518                     autoCreate : true,
31519                     shadow: true,
31520                     draggable: true,
31521                     resizable:false,
31522                     constraintoviewport:false,
31523                     fixedcenter:true,
31524                     collapsible : false,
31525                     shim:true,
31526                     modal: true,
31527                     width:400, height:100,
31528                     buttonAlign:"center",
31529                     closeClick : function(){
31530                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31531                             handleButton("no");
31532                         }else{
31533                             handleButton("cancel");
31534                         }
31535                     }
31536                 });
31537                 dlg.on("hide", handleHide);
31538                 mask = dlg.mask;
31539                 dlg.addKeyListener(27, handleEsc);
31540                 buttons = {};
31541                 var bt = this.buttonText;
31542                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31543                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31544                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31545                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31546                 bodyEl = dlg.body.createChild({
31547
31548                     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>'
31549                 });
31550                 msgEl = bodyEl.dom.firstChild;
31551                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31552                 textboxEl.enableDisplayMode();
31553                 textboxEl.addKeyListener([10,13], function(){
31554                     if(dlg.isVisible() && opt && opt.buttons){
31555                         if(opt.buttons.ok){
31556                             handleButton("ok");
31557                         }else if(opt.buttons.yes){
31558                             handleButton("yes");
31559                         }
31560                     }
31561                 });
31562                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31563                 textareaEl.enableDisplayMode();
31564                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31565                 progressEl.enableDisplayMode();
31566                 var pf = progressEl.dom.firstChild;
31567                 if (pf) {
31568                     pp = Roo.get(pf.firstChild);
31569                     pp.setHeight(pf.offsetHeight);
31570                 }
31571                 
31572             }
31573             return dlg;
31574         },
31575
31576         /**
31577          * Updates the message box body text
31578          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31579          * the XHTML-compliant non-breaking space character '&amp;#160;')
31580          * @return {Roo.MessageBox} This message box
31581          */
31582         updateText : function(text){
31583             if(!dlg.isVisible() && !opt.width){
31584                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31585             }
31586             msgEl.innerHTML = text || '&#160;';
31587       
31588             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31589             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31590             var w = Math.max(
31591                     Math.min(opt.width || cw , this.maxWidth), 
31592                     Math.max(opt.minWidth || this.minWidth, bwidth)
31593             );
31594             if(opt.prompt){
31595                 activeTextEl.setWidth(w);
31596             }
31597             if(dlg.isVisible()){
31598                 dlg.fixedcenter = false;
31599             }
31600             // to big, make it scroll. = But as usual stupid IE does not support
31601             // !important..
31602             
31603             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31604                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31605                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31606             } else {
31607                 bodyEl.dom.style.height = '';
31608                 bodyEl.dom.style.overflowY = '';
31609             }
31610             if (cw > w) {
31611                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31612             } else {
31613                 bodyEl.dom.style.overflowX = '';
31614             }
31615             
31616             dlg.setContentSize(w, bodyEl.getHeight());
31617             if(dlg.isVisible()){
31618                 dlg.fixedcenter = true;
31619             }
31620             return this;
31621         },
31622
31623         /**
31624          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31625          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31626          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31627          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31628          * @return {Roo.MessageBox} This message box
31629          */
31630         updateProgress : function(value, text){
31631             if(text){
31632                 this.updateText(text);
31633             }
31634             if (pp) { // weird bug on my firefox - for some reason this is not defined
31635                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31636             }
31637             return this;
31638         },        
31639
31640         /**
31641          * Returns true if the message box is currently displayed
31642          * @return {Boolean} True if the message box is visible, else false
31643          */
31644         isVisible : function(){
31645             return dlg && dlg.isVisible();  
31646         },
31647
31648         /**
31649          * Hides the message box if it is displayed
31650          */
31651         hide : function(){
31652             if(this.isVisible()){
31653                 dlg.hide();
31654             }  
31655         },
31656
31657         /**
31658          * Displays a new message box, or reinitializes an existing message box, based on the config options
31659          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31660          * The following config object properties are supported:
31661          * <pre>
31662 Property    Type             Description
31663 ----------  ---------------  ------------------------------------------------------------------------------------
31664 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31665                                    closes (defaults to undefined)
31666 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31667                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31668 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31669                                    progress and wait dialogs will ignore this property and always hide the
31670                                    close button as they can only be closed programmatically.
31671 cls               String           A custom CSS class to apply to the message box element
31672 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31673                                    displayed (defaults to 75)
31674 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31675                                    function will be btn (the name of the button that was clicked, if applicable,
31676                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31677                                    Progress and wait dialogs will ignore this option since they do not respond to
31678                                    user actions and can only be closed programmatically, so any required function
31679                                    should be called by the same code after it closes the dialog.
31680 icon              String           A CSS class that provides a background image to be used as an icon for
31681                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31682 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31683 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31684 modal             Boolean          False to allow user interaction with the page while the message box is
31685                                    displayed (defaults to true)
31686 msg               String           A string that will replace the existing message box body text (defaults
31687                                    to the XHTML-compliant non-breaking space character '&#160;')
31688 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31689 progress          Boolean          True to display a progress bar (defaults to false)
31690 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31691 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31692 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31693 title             String           The title text
31694 value             String           The string value to set into the active textbox element if displayed
31695 wait              Boolean          True to display a progress bar (defaults to false)
31696 width             Number           The width of the dialog in pixels
31697 </pre>
31698          *
31699          * Example usage:
31700          * <pre><code>
31701 Roo.Msg.show({
31702    title: 'Address',
31703    msg: 'Please enter your address:',
31704    width: 300,
31705    buttons: Roo.MessageBox.OKCANCEL,
31706    multiline: true,
31707    fn: saveAddress,
31708    animEl: 'addAddressBtn'
31709 });
31710 </code></pre>
31711          * @param {Object} config Configuration options
31712          * @return {Roo.MessageBox} This message box
31713          */
31714         show : function(options)
31715         {
31716             
31717             // this causes nightmares if you show one dialog after another
31718             // especially on callbacks..
31719              
31720             if(this.isVisible()){
31721                 
31722                 this.hide();
31723                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31724                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31725                 Roo.log("New Dialog Message:" +  options.msg )
31726                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31727                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31728                 
31729             }
31730             var d = this.getDialog();
31731             opt = options;
31732             d.setTitle(opt.title || "&#160;");
31733             d.close.setDisplayed(opt.closable !== false);
31734             activeTextEl = textboxEl;
31735             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31736             if(opt.prompt){
31737                 if(opt.multiline){
31738                     textboxEl.hide();
31739                     textareaEl.show();
31740                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31741                         opt.multiline : this.defaultTextHeight);
31742                     activeTextEl = textareaEl;
31743                 }else{
31744                     textboxEl.show();
31745                     textareaEl.hide();
31746                 }
31747             }else{
31748                 textboxEl.hide();
31749                 textareaEl.hide();
31750             }
31751             progressEl.setDisplayed(opt.progress === true);
31752             this.updateProgress(0);
31753             activeTextEl.dom.value = opt.value || "";
31754             if(opt.prompt){
31755                 dlg.setDefaultButton(activeTextEl);
31756             }else{
31757                 var bs = opt.buttons;
31758                 var db = null;
31759                 if(bs && bs.ok){
31760                     db = buttons["ok"];
31761                 }else if(bs && bs.yes){
31762                     db = buttons["yes"];
31763                 }
31764                 dlg.setDefaultButton(db);
31765             }
31766             bwidth = updateButtons(opt.buttons);
31767             this.updateText(opt.msg);
31768             if(opt.cls){
31769                 d.el.addClass(opt.cls);
31770             }
31771             d.proxyDrag = opt.proxyDrag === true;
31772             d.modal = opt.modal !== false;
31773             d.mask = opt.modal !== false ? mask : false;
31774             if(!d.isVisible()){
31775                 // force it to the end of the z-index stack so it gets a cursor in FF
31776                 document.body.appendChild(dlg.el.dom);
31777                 d.animateTarget = null;
31778                 d.show(options.animEl);
31779             }
31780             return this;
31781         },
31782
31783         /**
31784          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31785          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31786          * and closing the message box when the process is complete.
31787          * @param {String} title The title bar text
31788          * @param {String} msg The message box body text
31789          * @return {Roo.MessageBox} This message box
31790          */
31791         progress : function(title, msg){
31792             this.show({
31793                 title : title,
31794                 msg : msg,
31795                 buttons: false,
31796                 progress:true,
31797                 closable:false,
31798                 minWidth: this.minProgressWidth,
31799                 modal : true
31800             });
31801             return this;
31802         },
31803
31804         /**
31805          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31806          * If a callback function is passed it will be called after the user clicks the button, and the
31807          * id of the button that was clicked will be passed as the only parameter to the callback
31808          * (could also be the top-right close button).
31809          * @param {String} title The title bar text
31810          * @param {String} msg The message box body text
31811          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31812          * @param {Object} scope (optional) The scope of the callback function
31813          * @return {Roo.MessageBox} This message box
31814          */
31815         alert : function(title, msg, fn, scope){
31816             this.show({
31817                 title : title,
31818                 msg : msg,
31819                 buttons: this.OK,
31820                 fn: fn,
31821                 scope : scope,
31822                 modal : true
31823             });
31824             return this;
31825         },
31826
31827         /**
31828          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31829          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31830          * You are responsible for closing the message box when the process is complete.
31831          * @param {String} msg The message box body text
31832          * @param {String} title (optional) The title bar text
31833          * @return {Roo.MessageBox} This message box
31834          */
31835         wait : function(msg, title){
31836             this.show({
31837                 title : title,
31838                 msg : msg,
31839                 buttons: false,
31840                 closable:false,
31841                 progress:true,
31842                 modal:true,
31843                 width:300,
31844                 wait:true
31845             });
31846             waitTimer = Roo.TaskMgr.start({
31847                 run: function(i){
31848                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31849                 },
31850                 interval: 1000
31851             });
31852             return this;
31853         },
31854
31855         /**
31856          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31857          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31858          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31859          * @param {String} title The title bar text
31860          * @param {String} msg The message box body text
31861          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31862          * @param {Object} scope (optional) The scope of the callback function
31863          * @return {Roo.MessageBox} This message box
31864          */
31865         confirm : function(title, msg, fn, scope){
31866             this.show({
31867                 title : title,
31868                 msg : msg,
31869                 buttons: this.YESNO,
31870                 fn: fn,
31871                 scope : scope,
31872                 modal : true
31873             });
31874             return this;
31875         },
31876
31877         /**
31878          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31879          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31880          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31881          * (could also be the top-right close button) and the text that was entered will be passed as the two
31882          * parameters to the callback.
31883          * @param {String} title The title bar text
31884          * @param {String} msg The message box body text
31885          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31886          * @param {Object} scope (optional) The scope of the callback function
31887          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31888          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31889          * @return {Roo.MessageBox} This message box
31890          */
31891         prompt : function(title, msg, fn, scope, multiline){
31892             this.show({
31893                 title : title,
31894                 msg : msg,
31895                 buttons: this.OKCANCEL,
31896                 fn: fn,
31897                 minWidth:250,
31898                 scope : scope,
31899                 prompt:true,
31900                 multiline: multiline,
31901                 modal : true
31902             });
31903             return this;
31904         },
31905
31906         /**
31907          * Button config that displays a single OK button
31908          * @type Object
31909          */
31910         OK : {ok:true},
31911         /**
31912          * Button config that displays Yes and No buttons
31913          * @type Object
31914          */
31915         YESNO : {yes:true, no:true},
31916         /**
31917          * Button config that displays OK and Cancel buttons
31918          * @type Object
31919          */
31920         OKCANCEL : {ok:true, cancel:true},
31921         /**
31922          * Button config that displays Yes, No and Cancel buttons
31923          * @type Object
31924          */
31925         YESNOCANCEL : {yes:true, no:true, cancel:true},
31926
31927         /**
31928          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31929          * @type Number
31930          */
31931         defaultTextHeight : 75,
31932         /**
31933          * The maximum width in pixels of the message box (defaults to 600)
31934          * @type Number
31935          */
31936         maxWidth : 600,
31937         /**
31938          * The minimum width in pixels of the message box (defaults to 100)
31939          * @type Number
31940          */
31941         minWidth : 100,
31942         /**
31943          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31944          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31945          * @type Number
31946          */
31947         minProgressWidth : 250,
31948         /**
31949          * An object containing the default button text strings that can be overriden for localized language support.
31950          * Supported properties are: ok, cancel, yes and no.
31951          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31952          * @type Object
31953          */
31954         buttonText : {
31955             ok : "OK",
31956             cancel : "Cancel",
31957             yes : "Yes",
31958             no : "No"
31959         }
31960     };
31961 }();
31962
31963 /**
31964  * Shorthand for {@link Roo.MessageBox}
31965  */
31966 Roo.Msg = Roo.MessageBox;/*
31967  * Based on:
31968  * Ext JS Library 1.1.1
31969  * Copyright(c) 2006-2007, Ext JS, LLC.
31970  *
31971  * Originally Released Under LGPL - original licence link has changed is not relivant.
31972  *
31973  * Fork - LGPL
31974  * <script type="text/javascript">
31975  */
31976 /**
31977  * @class Roo.QuickTips
31978  * Provides attractive and customizable tooltips for any element.
31979  * @singleton
31980  */
31981 Roo.QuickTips = function(){
31982     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31983     var ce, bd, xy, dd;
31984     var visible = false, disabled = true, inited = false;
31985     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31986     
31987     var onOver = function(e){
31988         if(disabled){
31989             return;
31990         }
31991         var t = e.getTarget();
31992         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31993             return;
31994         }
31995         if(ce && t == ce.el){
31996             clearTimeout(hideProc);
31997             return;
31998         }
31999         if(t && tagEls[t.id]){
32000             tagEls[t.id].el = t;
32001             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32002             return;
32003         }
32004         var ttp, et = Roo.fly(t);
32005         var ns = cfg.namespace;
32006         if(tm.interceptTitles && t.title){
32007             ttp = t.title;
32008             t.qtip = ttp;
32009             t.removeAttribute("title");
32010             e.preventDefault();
32011         }else{
32012             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32013         }
32014         if(ttp){
32015             showProc = show.defer(tm.showDelay, tm, [{
32016                 el: t, 
32017                 text: ttp, 
32018                 width: et.getAttributeNS(ns, cfg.width),
32019                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32020                 title: et.getAttributeNS(ns, cfg.title),
32021                     cls: et.getAttributeNS(ns, cfg.cls)
32022             }]);
32023         }
32024     };
32025     
32026     var onOut = function(e){
32027         clearTimeout(showProc);
32028         var t = e.getTarget();
32029         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32030             hideProc = setTimeout(hide, tm.hideDelay);
32031         }
32032     };
32033     
32034     var onMove = function(e){
32035         if(disabled){
32036             return;
32037         }
32038         xy = e.getXY();
32039         xy[1] += 18;
32040         if(tm.trackMouse && ce){
32041             el.setXY(xy);
32042         }
32043     };
32044     
32045     var onDown = function(e){
32046         clearTimeout(showProc);
32047         clearTimeout(hideProc);
32048         if(!e.within(el)){
32049             if(tm.hideOnClick){
32050                 hide();
32051                 tm.disable();
32052                 tm.enable.defer(100, tm);
32053             }
32054         }
32055     };
32056     
32057     var getPad = function(){
32058         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32059     };
32060
32061     var show = function(o){
32062         if(disabled){
32063             return;
32064         }
32065         clearTimeout(dismissProc);
32066         ce = o;
32067         if(removeCls){ // in case manually hidden
32068             el.removeClass(removeCls);
32069             removeCls = null;
32070         }
32071         if(ce.cls){
32072             el.addClass(ce.cls);
32073             removeCls = ce.cls;
32074         }
32075         if(ce.title){
32076             tipTitle.update(ce.title);
32077             tipTitle.show();
32078         }else{
32079             tipTitle.update('');
32080             tipTitle.hide();
32081         }
32082         el.dom.style.width  = tm.maxWidth+'px';
32083         //tipBody.dom.style.width = '';
32084         tipBodyText.update(o.text);
32085         var p = getPad(), w = ce.width;
32086         if(!w){
32087             var td = tipBodyText.dom;
32088             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32089             if(aw > tm.maxWidth){
32090                 w = tm.maxWidth;
32091             }else if(aw < tm.minWidth){
32092                 w = tm.minWidth;
32093             }else{
32094                 w = aw;
32095             }
32096         }
32097         //tipBody.setWidth(w);
32098         el.setWidth(parseInt(w, 10) + p);
32099         if(ce.autoHide === false){
32100             close.setDisplayed(true);
32101             if(dd){
32102                 dd.unlock();
32103             }
32104         }else{
32105             close.setDisplayed(false);
32106             if(dd){
32107                 dd.lock();
32108             }
32109         }
32110         if(xy){
32111             el.avoidY = xy[1]-18;
32112             el.setXY(xy);
32113         }
32114         if(tm.animate){
32115             el.setOpacity(.1);
32116             el.setStyle("visibility", "visible");
32117             el.fadeIn({callback: afterShow});
32118         }else{
32119             afterShow();
32120         }
32121     };
32122     
32123     var afterShow = function(){
32124         if(ce){
32125             el.show();
32126             esc.enable();
32127             if(tm.autoDismiss && ce.autoHide !== false){
32128                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32129             }
32130         }
32131     };
32132     
32133     var hide = function(noanim){
32134         clearTimeout(dismissProc);
32135         clearTimeout(hideProc);
32136         ce = null;
32137         if(el.isVisible()){
32138             esc.disable();
32139             if(noanim !== true && tm.animate){
32140                 el.fadeOut({callback: afterHide});
32141             }else{
32142                 afterHide();
32143             } 
32144         }
32145     };
32146     
32147     var afterHide = function(){
32148         el.hide();
32149         if(removeCls){
32150             el.removeClass(removeCls);
32151             removeCls = null;
32152         }
32153     };
32154     
32155     return {
32156         /**
32157         * @cfg {Number} minWidth
32158         * The minimum width of the quick tip (defaults to 40)
32159         */
32160        minWidth : 40,
32161         /**
32162         * @cfg {Number} maxWidth
32163         * The maximum width of the quick tip (defaults to 300)
32164         */
32165        maxWidth : 300,
32166         /**
32167         * @cfg {Boolean} interceptTitles
32168         * True to automatically use the element's DOM title value if available (defaults to false)
32169         */
32170        interceptTitles : false,
32171         /**
32172         * @cfg {Boolean} trackMouse
32173         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32174         */
32175        trackMouse : false,
32176         /**
32177         * @cfg {Boolean} hideOnClick
32178         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32179         */
32180        hideOnClick : true,
32181         /**
32182         * @cfg {Number} showDelay
32183         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32184         */
32185        showDelay : 500,
32186         /**
32187         * @cfg {Number} hideDelay
32188         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32189         */
32190        hideDelay : 200,
32191         /**
32192         * @cfg {Boolean} autoHide
32193         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32194         * Used in conjunction with hideDelay.
32195         */
32196        autoHide : true,
32197         /**
32198         * @cfg {Boolean}
32199         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32200         * (defaults to true).  Used in conjunction with autoDismissDelay.
32201         */
32202        autoDismiss : true,
32203         /**
32204         * @cfg {Number}
32205         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32206         */
32207        autoDismissDelay : 5000,
32208        /**
32209         * @cfg {Boolean} animate
32210         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32211         */
32212        animate : false,
32213
32214        /**
32215         * @cfg {String} title
32216         * Title text to display (defaults to '').  This can be any valid HTML markup.
32217         */
32218         title: '',
32219        /**
32220         * @cfg {String} text
32221         * Body text to display (defaults to '').  This can be any valid HTML markup.
32222         */
32223         text : '',
32224        /**
32225         * @cfg {String} cls
32226         * A CSS class to apply to the base quick tip element (defaults to '').
32227         */
32228         cls : '',
32229        /**
32230         * @cfg {Number} width
32231         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32232         * minWidth or maxWidth.
32233         */
32234         width : null,
32235
32236     /**
32237      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32238      * or display QuickTips in a page.
32239      */
32240        init : function(){
32241           tm = Roo.QuickTips;
32242           cfg = tm.tagConfig;
32243           if(!inited){
32244               if(!Roo.isReady){ // allow calling of init() before onReady
32245                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32246                   return;
32247               }
32248               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32249               el.fxDefaults = {stopFx: true};
32250               // maximum custom styling
32251               //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>');
32252               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>');              
32253               tipTitle = el.child('h3');
32254               tipTitle.enableDisplayMode("block");
32255               tipBody = el.child('div.x-tip-bd');
32256               tipBodyText = el.child('div.x-tip-bd-inner');
32257               //bdLeft = el.child('div.x-tip-bd-left');
32258               //bdRight = el.child('div.x-tip-bd-right');
32259               close = el.child('div.x-tip-close');
32260               close.enableDisplayMode("block");
32261               close.on("click", hide);
32262               var d = Roo.get(document);
32263               d.on("mousedown", onDown);
32264               d.on("mouseover", onOver);
32265               d.on("mouseout", onOut);
32266               d.on("mousemove", onMove);
32267               esc = d.addKeyListener(27, hide);
32268               esc.disable();
32269               if(Roo.dd.DD){
32270                   dd = el.initDD("default", null, {
32271                       onDrag : function(){
32272                           el.sync();  
32273                       }
32274                   });
32275                   dd.setHandleElId(tipTitle.id);
32276                   dd.lock();
32277               }
32278               inited = true;
32279           }
32280           this.enable(); 
32281        },
32282
32283     /**
32284      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32285      * are supported:
32286      * <pre>
32287 Property    Type                   Description
32288 ----------  ---------------------  ------------------------------------------------------------------------
32289 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32290      * </ul>
32291      * @param {Object} config The config object
32292      */
32293        register : function(config){
32294            var cs = config instanceof Array ? config : arguments;
32295            for(var i = 0, len = cs.length; i < len; i++) {
32296                var c = cs[i];
32297                var target = c.target;
32298                if(target){
32299                    if(target instanceof Array){
32300                        for(var j = 0, jlen = target.length; j < jlen; j++){
32301                            tagEls[target[j]] = c;
32302                        }
32303                    }else{
32304                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32305                    }
32306                }
32307            }
32308        },
32309
32310     /**
32311      * Removes this quick tip from its element and destroys it.
32312      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32313      */
32314        unregister : function(el){
32315            delete tagEls[Roo.id(el)];
32316        },
32317
32318     /**
32319      * Enable this quick tip.
32320      */
32321        enable : function(){
32322            if(inited && disabled){
32323                locks.pop();
32324                if(locks.length < 1){
32325                    disabled = false;
32326                }
32327            }
32328        },
32329
32330     /**
32331      * Disable this quick tip.
32332      */
32333        disable : function(){
32334           disabled = true;
32335           clearTimeout(showProc);
32336           clearTimeout(hideProc);
32337           clearTimeout(dismissProc);
32338           if(ce){
32339               hide(true);
32340           }
32341           locks.push(1);
32342        },
32343
32344     /**
32345      * Returns true if the quick tip is enabled, else false.
32346      */
32347        isEnabled : function(){
32348             return !disabled;
32349        },
32350
32351         // private
32352        tagConfig : {
32353            namespace : "ext",
32354            attribute : "qtip",
32355            width : "width",
32356            target : "target",
32357            title : "qtitle",
32358            hide : "hide",
32359            cls : "qclass"
32360        }
32361    };
32362 }();
32363
32364 // backwards compat
32365 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32366  * Based on:
32367  * Ext JS Library 1.1.1
32368  * Copyright(c) 2006-2007, Ext JS, LLC.
32369  *
32370  * Originally Released Under LGPL - original licence link has changed is not relivant.
32371  *
32372  * Fork - LGPL
32373  * <script type="text/javascript">
32374  */
32375  
32376
32377 /**
32378  * @class Roo.tree.TreePanel
32379  * @extends Roo.data.Tree
32380
32381  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32382  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32383  * @cfg {Boolean} enableDD true to enable drag and drop
32384  * @cfg {Boolean} enableDrag true to enable just drag
32385  * @cfg {Boolean} enableDrop true to enable just drop
32386  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32387  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32388  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32389  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32390  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32391  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32392  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32393  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32394  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32395  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32396  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32397  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32398  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32399  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32400  * @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>
32401  * @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>
32402  * 
32403  * @constructor
32404  * @param {String/HTMLElement/Element} el The container element
32405  * @param {Object} config
32406  */
32407 Roo.tree.TreePanel = function(el, config){
32408     var root = false;
32409     var loader = false;
32410     if (config.root) {
32411         root = config.root;
32412         delete config.root;
32413     }
32414     if (config.loader) {
32415         loader = config.loader;
32416         delete config.loader;
32417     }
32418     
32419     Roo.apply(this, config);
32420     Roo.tree.TreePanel.superclass.constructor.call(this);
32421     this.el = Roo.get(el);
32422     this.el.addClass('x-tree');
32423     //console.log(root);
32424     if (root) {
32425         this.setRootNode( Roo.factory(root, Roo.tree));
32426     }
32427     if (loader) {
32428         this.loader = Roo.factory(loader, Roo.tree);
32429     }
32430    /**
32431     * Read-only. The id of the container element becomes this TreePanel's id.
32432     */
32433     this.id = this.el.id;
32434     this.addEvents({
32435         /**
32436         * @event beforeload
32437         * Fires before a node is loaded, return false to cancel
32438         * @param {Node} node The node being loaded
32439         */
32440         "beforeload" : true,
32441         /**
32442         * @event load
32443         * Fires when a node is loaded
32444         * @param {Node} node The node that was loaded
32445         */
32446         "load" : true,
32447         /**
32448         * @event textchange
32449         * Fires when the text for a node is changed
32450         * @param {Node} node The node
32451         * @param {String} text The new text
32452         * @param {String} oldText The old text
32453         */
32454         "textchange" : true,
32455         /**
32456         * @event beforeexpand
32457         * Fires before a node is expanded, return false to cancel.
32458         * @param {Node} node The node
32459         * @param {Boolean} deep
32460         * @param {Boolean} anim
32461         */
32462         "beforeexpand" : true,
32463         /**
32464         * @event beforecollapse
32465         * Fires before a node is collapsed, return false to cancel.
32466         * @param {Node} node The node
32467         * @param {Boolean} deep
32468         * @param {Boolean} anim
32469         */
32470         "beforecollapse" : true,
32471         /**
32472         * @event expand
32473         * Fires when a node is expanded
32474         * @param {Node} node The node
32475         */
32476         "expand" : true,
32477         /**
32478         * @event disabledchange
32479         * Fires when the disabled status of a node changes
32480         * @param {Node} node The node
32481         * @param {Boolean} disabled
32482         */
32483         "disabledchange" : true,
32484         /**
32485         * @event collapse
32486         * Fires when a node is collapsed
32487         * @param {Node} node The node
32488         */
32489         "collapse" : true,
32490         /**
32491         * @event beforeclick
32492         * Fires before click processing on a node. Return false to cancel the default action.
32493         * @param {Node} node The node
32494         * @param {Roo.EventObject} e The event object
32495         */
32496         "beforeclick":true,
32497         /**
32498         * @event checkchange
32499         * Fires when a node with a checkbox's checked property changes
32500         * @param {Node} this This node
32501         * @param {Boolean} checked
32502         */
32503         "checkchange":true,
32504         /**
32505         * @event click
32506         * Fires when a node is clicked
32507         * @param {Node} node The node
32508         * @param {Roo.EventObject} e The event object
32509         */
32510         "click":true,
32511         /**
32512         * @event dblclick
32513         * Fires when a node is double clicked
32514         * @param {Node} node The node
32515         * @param {Roo.EventObject} e The event object
32516         */
32517         "dblclick":true,
32518         /**
32519         * @event contextmenu
32520         * Fires when a node is right clicked
32521         * @param {Node} node The node
32522         * @param {Roo.EventObject} e The event object
32523         */
32524         "contextmenu":true,
32525         /**
32526         * @event beforechildrenrendered
32527         * Fires right before the child nodes for a node are rendered
32528         * @param {Node} node The node
32529         */
32530         "beforechildrenrendered":true,
32531         /**
32532         * @event startdrag
32533         * Fires when a node starts being dragged
32534         * @param {Roo.tree.TreePanel} this
32535         * @param {Roo.tree.TreeNode} node
32536         * @param {event} e The raw browser event
32537         */ 
32538        "startdrag" : true,
32539        /**
32540         * @event enddrag
32541         * Fires when a drag operation is complete
32542         * @param {Roo.tree.TreePanel} this
32543         * @param {Roo.tree.TreeNode} node
32544         * @param {event} e The raw browser event
32545         */
32546        "enddrag" : true,
32547        /**
32548         * @event dragdrop
32549         * Fires when a dragged node is dropped on a valid DD target
32550         * @param {Roo.tree.TreePanel} this
32551         * @param {Roo.tree.TreeNode} node
32552         * @param {DD} dd The dd it was dropped on
32553         * @param {event} e The raw browser event
32554         */
32555        "dragdrop" : true,
32556        /**
32557         * @event beforenodedrop
32558         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32559         * passed to handlers has the following properties:<br />
32560         * <ul style="padding:5px;padding-left:16px;">
32561         * <li>tree - The TreePanel</li>
32562         * <li>target - The node being targeted for the drop</li>
32563         * <li>data - The drag data from the drag source</li>
32564         * <li>point - The point of the drop - append, above or below</li>
32565         * <li>source - The drag source</li>
32566         * <li>rawEvent - Raw mouse event</li>
32567         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32568         * to be inserted by setting them on this object.</li>
32569         * <li>cancel - Set this to true to cancel the drop.</li>
32570         * </ul>
32571         * @param {Object} dropEvent
32572         */
32573        "beforenodedrop" : true,
32574        /**
32575         * @event nodedrop
32576         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32577         * passed to handlers has the following properties:<br />
32578         * <ul style="padding:5px;padding-left:16px;">
32579         * <li>tree - The TreePanel</li>
32580         * <li>target - The node being targeted for the drop</li>
32581         * <li>data - The drag data from the drag source</li>
32582         * <li>point - The point of the drop - append, above or below</li>
32583         * <li>source - The drag source</li>
32584         * <li>rawEvent - Raw mouse event</li>
32585         * <li>dropNode - Dropped node(s).</li>
32586         * </ul>
32587         * @param {Object} dropEvent
32588         */
32589        "nodedrop" : true,
32590         /**
32591         * @event nodedragover
32592         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32593         * passed to handlers has the following properties:<br />
32594         * <ul style="padding:5px;padding-left:16px;">
32595         * <li>tree - The TreePanel</li>
32596         * <li>target - The node being targeted for the drop</li>
32597         * <li>data - The drag data from the drag source</li>
32598         * <li>point - The point of the drop - append, above or below</li>
32599         * <li>source - The drag source</li>
32600         * <li>rawEvent - Raw mouse event</li>
32601         * <li>dropNode - Drop node(s) provided by the source.</li>
32602         * <li>cancel - Set this to true to signal drop not allowed.</li>
32603         * </ul>
32604         * @param {Object} dragOverEvent
32605         */
32606        "nodedragover" : true
32607         
32608     });
32609     if(this.singleExpand){
32610        this.on("beforeexpand", this.restrictExpand, this);
32611     }
32612     if (this.editor) {
32613         this.editor.tree = this;
32614         this.editor = Roo.factory(this.editor, Roo.tree);
32615     }
32616     
32617     if (this.selModel) {
32618         this.selModel = Roo.factory(this.selModel, Roo.tree);
32619     }
32620    
32621 };
32622 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32623     rootVisible : true,
32624     animate: Roo.enableFx,
32625     lines : true,
32626     enableDD : false,
32627     hlDrop : Roo.enableFx,
32628   
32629     renderer: false,
32630     
32631     rendererTip: false,
32632     // private
32633     restrictExpand : function(node){
32634         var p = node.parentNode;
32635         if(p){
32636             if(p.expandedChild && p.expandedChild.parentNode == p){
32637                 p.expandedChild.collapse();
32638             }
32639             p.expandedChild = node;
32640         }
32641     },
32642
32643     // private override
32644     setRootNode : function(node){
32645         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32646         if(!this.rootVisible){
32647             node.ui = new Roo.tree.RootTreeNodeUI(node);
32648         }
32649         return node;
32650     },
32651
32652     /**
32653      * Returns the container element for this TreePanel
32654      */
32655     getEl : function(){
32656         return this.el;
32657     },
32658
32659     /**
32660      * Returns the default TreeLoader for this TreePanel
32661      */
32662     getLoader : function(){
32663         return this.loader;
32664     },
32665
32666     /**
32667      * Expand all nodes
32668      */
32669     expandAll : function(){
32670         this.root.expand(true);
32671     },
32672
32673     /**
32674      * Collapse all nodes
32675      */
32676     collapseAll : function(){
32677         this.root.collapse(true);
32678     },
32679
32680     /**
32681      * Returns the selection model used by this TreePanel
32682      */
32683     getSelectionModel : function(){
32684         if(!this.selModel){
32685             this.selModel = new Roo.tree.DefaultSelectionModel();
32686         }
32687         return this.selModel;
32688     },
32689
32690     /**
32691      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32692      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32693      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32694      * @return {Array}
32695      */
32696     getChecked : function(a, startNode){
32697         startNode = startNode || this.root;
32698         var r = [];
32699         var f = function(){
32700             if(this.attributes.checked){
32701                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32702             }
32703         }
32704         startNode.cascade(f);
32705         return r;
32706     },
32707
32708     /**
32709      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32710      * @param {String} path
32711      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32712      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32713      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32714      */
32715     expandPath : function(path, attr, callback){
32716         attr = attr || "id";
32717         var keys = path.split(this.pathSeparator);
32718         var curNode = this.root;
32719         if(curNode.attributes[attr] != keys[1]){ // invalid root
32720             if(callback){
32721                 callback(false, null);
32722             }
32723             return;
32724         }
32725         var index = 1;
32726         var f = function(){
32727             if(++index == keys.length){
32728                 if(callback){
32729                     callback(true, curNode);
32730                 }
32731                 return;
32732             }
32733             var c = curNode.findChild(attr, keys[index]);
32734             if(!c){
32735                 if(callback){
32736                     callback(false, curNode);
32737                 }
32738                 return;
32739             }
32740             curNode = c;
32741             c.expand(false, false, f);
32742         };
32743         curNode.expand(false, false, f);
32744     },
32745
32746     /**
32747      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32748      * @param {String} path
32749      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32750      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32751      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32752      */
32753     selectPath : function(path, attr, callback){
32754         attr = attr || "id";
32755         var keys = path.split(this.pathSeparator);
32756         var v = keys.pop();
32757         if(keys.length > 0){
32758             var f = function(success, node){
32759                 if(success && node){
32760                     var n = node.findChild(attr, v);
32761                     if(n){
32762                         n.select();
32763                         if(callback){
32764                             callback(true, n);
32765                         }
32766                     }else if(callback){
32767                         callback(false, n);
32768                     }
32769                 }else{
32770                     if(callback){
32771                         callback(false, n);
32772                     }
32773                 }
32774             };
32775             this.expandPath(keys.join(this.pathSeparator), attr, f);
32776         }else{
32777             this.root.select();
32778             if(callback){
32779                 callback(true, this.root);
32780             }
32781         }
32782     },
32783
32784     getTreeEl : function(){
32785         return this.el;
32786     },
32787
32788     /**
32789      * Trigger rendering of this TreePanel
32790      */
32791     render : function(){
32792         if (this.innerCt) {
32793             return this; // stop it rendering more than once!!
32794         }
32795         
32796         this.innerCt = this.el.createChild({tag:"ul",
32797                cls:"x-tree-root-ct " +
32798                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32799
32800         if(this.containerScroll){
32801             Roo.dd.ScrollManager.register(this.el);
32802         }
32803         if((this.enableDD || this.enableDrop) && !this.dropZone){
32804            /**
32805             * The dropZone used by this tree if drop is enabled
32806             * @type Roo.tree.TreeDropZone
32807             */
32808              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32809                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32810            });
32811         }
32812         if((this.enableDD || this.enableDrag) && !this.dragZone){
32813            /**
32814             * The dragZone used by this tree if drag is enabled
32815             * @type Roo.tree.TreeDragZone
32816             */
32817             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32818                ddGroup: this.ddGroup || "TreeDD",
32819                scroll: this.ddScroll
32820            });
32821         }
32822         this.getSelectionModel().init(this);
32823         if (!this.root) {
32824             Roo.log("ROOT not set in tree");
32825             return this;
32826         }
32827         this.root.render();
32828         if(!this.rootVisible){
32829             this.root.renderChildren();
32830         }
32831         return this;
32832     }
32833 });/*
32834  * Based on:
32835  * Ext JS Library 1.1.1
32836  * Copyright(c) 2006-2007, Ext JS, LLC.
32837  *
32838  * Originally Released Under LGPL - original licence link has changed is not relivant.
32839  *
32840  * Fork - LGPL
32841  * <script type="text/javascript">
32842  */
32843  
32844
32845 /**
32846  * @class Roo.tree.DefaultSelectionModel
32847  * @extends Roo.util.Observable
32848  * The default single selection for a TreePanel.
32849  * @param {Object} cfg Configuration
32850  */
32851 Roo.tree.DefaultSelectionModel = function(cfg){
32852    this.selNode = null;
32853    
32854    
32855    
32856    this.addEvents({
32857        /**
32858         * @event selectionchange
32859         * Fires when the selected node changes
32860         * @param {DefaultSelectionModel} this
32861         * @param {TreeNode} node the new selection
32862         */
32863        "selectionchange" : true,
32864
32865        /**
32866         * @event beforeselect
32867         * Fires before the selected node changes, return false to cancel the change
32868         * @param {DefaultSelectionModel} this
32869         * @param {TreeNode} node the new selection
32870         * @param {TreeNode} node the old selection
32871         */
32872        "beforeselect" : true
32873    });
32874    
32875     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32876 };
32877
32878 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32879     init : function(tree){
32880         this.tree = tree;
32881         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32882         tree.on("click", this.onNodeClick, this);
32883     },
32884     
32885     onNodeClick : function(node, e){
32886         if (e.ctrlKey && this.selNode == node)  {
32887             this.unselect(node);
32888             return;
32889         }
32890         this.select(node);
32891     },
32892     
32893     /**
32894      * Select a node.
32895      * @param {TreeNode} node The node to select
32896      * @return {TreeNode} The selected node
32897      */
32898     select : function(node){
32899         var last = this.selNode;
32900         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32901             if(last){
32902                 last.ui.onSelectedChange(false);
32903             }
32904             this.selNode = node;
32905             node.ui.onSelectedChange(true);
32906             this.fireEvent("selectionchange", this, node, last);
32907         }
32908         return node;
32909     },
32910     
32911     /**
32912      * Deselect a node.
32913      * @param {TreeNode} node The node to unselect
32914      */
32915     unselect : function(node){
32916         if(this.selNode == node){
32917             this.clearSelections();
32918         }    
32919     },
32920     
32921     /**
32922      * Clear all selections
32923      */
32924     clearSelections : function(){
32925         var n = this.selNode;
32926         if(n){
32927             n.ui.onSelectedChange(false);
32928             this.selNode = null;
32929             this.fireEvent("selectionchange", this, null);
32930         }
32931         return n;
32932     },
32933     
32934     /**
32935      * Get the selected node
32936      * @return {TreeNode} The selected node
32937      */
32938     getSelectedNode : function(){
32939         return this.selNode;    
32940     },
32941     
32942     /**
32943      * Returns true if the node is selected
32944      * @param {TreeNode} node The node to check
32945      * @return {Boolean}
32946      */
32947     isSelected : function(node){
32948         return this.selNode == node;  
32949     },
32950
32951     /**
32952      * Selects the node above the selected node in the tree, intelligently walking the nodes
32953      * @return TreeNode The new selection
32954      */
32955     selectPrevious : function(){
32956         var s = this.selNode || this.lastSelNode;
32957         if(!s){
32958             return null;
32959         }
32960         var ps = s.previousSibling;
32961         if(ps){
32962             if(!ps.isExpanded() || ps.childNodes.length < 1){
32963                 return this.select(ps);
32964             } else{
32965                 var lc = ps.lastChild;
32966                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32967                     lc = lc.lastChild;
32968                 }
32969                 return this.select(lc);
32970             }
32971         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32972             return this.select(s.parentNode);
32973         }
32974         return null;
32975     },
32976
32977     /**
32978      * Selects the node above the selected node in the tree, intelligently walking the nodes
32979      * @return TreeNode The new selection
32980      */
32981     selectNext : function(){
32982         var s = this.selNode || this.lastSelNode;
32983         if(!s){
32984             return null;
32985         }
32986         if(s.firstChild && s.isExpanded()){
32987              return this.select(s.firstChild);
32988          }else if(s.nextSibling){
32989              return this.select(s.nextSibling);
32990          }else if(s.parentNode){
32991             var newS = null;
32992             s.parentNode.bubble(function(){
32993                 if(this.nextSibling){
32994                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32995                     return false;
32996                 }
32997             });
32998             return newS;
32999          }
33000         return null;
33001     },
33002
33003     onKeyDown : function(e){
33004         var s = this.selNode || this.lastSelNode;
33005         // undesirable, but required
33006         var sm = this;
33007         if(!s){
33008             return;
33009         }
33010         var k = e.getKey();
33011         switch(k){
33012              case e.DOWN:
33013                  e.stopEvent();
33014                  this.selectNext();
33015              break;
33016              case e.UP:
33017                  e.stopEvent();
33018                  this.selectPrevious();
33019              break;
33020              case e.RIGHT:
33021                  e.preventDefault();
33022                  if(s.hasChildNodes()){
33023                      if(!s.isExpanded()){
33024                          s.expand();
33025                      }else if(s.firstChild){
33026                          this.select(s.firstChild, e);
33027                      }
33028                  }
33029              break;
33030              case e.LEFT:
33031                  e.preventDefault();
33032                  if(s.hasChildNodes() && s.isExpanded()){
33033                      s.collapse();
33034                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33035                      this.select(s.parentNode, e);
33036                  }
33037              break;
33038         };
33039     }
33040 });
33041
33042 /**
33043  * @class Roo.tree.MultiSelectionModel
33044  * @extends Roo.util.Observable
33045  * Multi selection for a TreePanel.
33046  * @param {Object} cfg Configuration
33047  */
33048 Roo.tree.MultiSelectionModel = function(){
33049    this.selNodes = [];
33050    this.selMap = {};
33051    this.addEvents({
33052        /**
33053         * @event selectionchange
33054         * Fires when the selected nodes change
33055         * @param {MultiSelectionModel} this
33056         * @param {Array} nodes Array of the selected nodes
33057         */
33058        "selectionchange" : true
33059    });
33060    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33061    
33062 };
33063
33064 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33065     init : function(tree){
33066         this.tree = tree;
33067         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33068         tree.on("click", this.onNodeClick, this);
33069     },
33070     
33071     onNodeClick : function(node, e){
33072         this.select(node, e, e.ctrlKey);
33073     },
33074     
33075     /**
33076      * Select a node.
33077      * @param {TreeNode} node The node to select
33078      * @param {EventObject} e (optional) An event associated with the selection
33079      * @param {Boolean} keepExisting True to retain existing selections
33080      * @return {TreeNode} The selected node
33081      */
33082     select : function(node, e, keepExisting){
33083         if(keepExisting !== true){
33084             this.clearSelections(true);
33085         }
33086         if(this.isSelected(node)){
33087             this.lastSelNode = node;
33088             return node;
33089         }
33090         this.selNodes.push(node);
33091         this.selMap[node.id] = node;
33092         this.lastSelNode = node;
33093         node.ui.onSelectedChange(true);
33094         this.fireEvent("selectionchange", this, this.selNodes);
33095         return node;
33096     },
33097     
33098     /**
33099      * Deselect a node.
33100      * @param {TreeNode} node The node to unselect
33101      */
33102     unselect : function(node){
33103         if(this.selMap[node.id]){
33104             node.ui.onSelectedChange(false);
33105             var sn = this.selNodes;
33106             var index = -1;
33107             if(sn.indexOf){
33108                 index = sn.indexOf(node);
33109             }else{
33110                 for(var i = 0, len = sn.length; i < len; i++){
33111                     if(sn[i] == node){
33112                         index = i;
33113                         break;
33114                     }
33115                 }
33116             }
33117             if(index != -1){
33118                 this.selNodes.splice(index, 1);
33119             }
33120             delete this.selMap[node.id];
33121             this.fireEvent("selectionchange", this, this.selNodes);
33122         }
33123     },
33124     
33125     /**
33126      * Clear all selections
33127      */
33128     clearSelections : function(suppressEvent){
33129         var sn = this.selNodes;
33130         if(sn.length > 0){
33131             for(var i = 0, len = sn.length; i < len; i++){
33132                 sn[i].ui.onSelectedChange(false);
33133             }
33134             this.selNodes = [];
33135             this.selMap = {};
33136             if(suppressEvent !== true){
33137                 this.fireEvent("selectionchange", this, this.selNodes);
33138             }
33139         }
33140     },
33141     
33142     /**
33143      * Returns true if the node is selected
33144      * @param {TreeNode} node The node to check
33145      * @return {Boolean}
33146      */
33147     isSelected : function(node){
33148         return this.selMap[node.id] ? true : false;  
33149     },
33150     
33151     /**
33152      * Returns an array of the selected nodes
33153      * @return {Array}
33154      */
33155     getSelectedNodes : function(){
33156         return this.selNodes;    
33157     },
33158
33159     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33160
33161     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33162
33163     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33164 });/*
33165  * Based on:
33166  * Ext JS Library 1.1.1
33167  * Copyright(c) 2006-2007, Ext JS, LLC.
33168  *
33169  * Originally Released Under LGPL - original licence link has changed is not relivant.
33170  *
33171  * Fork - LGPL
33172  * <script type="text/javascript">
33173  */
33174  
33175 /**
33176  * @class Roo.tree.TreeNode
33177  * @extends Roo.data.Node
33178  * @cfg {String} text The text for this node
33179  * @cfg {Boolean} expanded true to start the node expanded
33180  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33181  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33182  * @cfg {Boolean} disabled true to start the node disabled
33183  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33184  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33185  * @cfg {String} cls A css class to be added to the node
33186  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33187  * @cfg {String} href URL of the link used for the node (defaults to #)
33188  * @cfg {String} hrefTarget target frame for the link
33189  * @cfg {String} qtip An Ext QuickTip for the node
33190  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33191  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33192  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33193  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33194  * (defaults to undefined with no checkbox rendered)
33195  * @constructor
33196  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33197  */
33198 Roo.tree.TreeNode = function(attributes){
33199     attributes = attributes || {};
33200     if(typeof attributes == "string"){
33201         attributes = {text: attributes};
33202     }
33203     this.childrenRendered = false;
33204     this.rendered = false;
33205     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33206     this.expanded = attributes.expanded === true;
33207     this.isTarget = attributes.isTarget !== false;
33208     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33209     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33210
33211     /**
33212      * Read-only. The text for this node. To change it use setText().
33213      * @type String
33214      */
33215     this.text = attributes.text;
33216     /**
33217      * True if this node is disabled.
33218      * @type Boolean
33219      */
33220     this.disabled = attributes.disabled === true;
33221
33222     this.addEvents({
33223         /**
33224         * @event textchange
33225         * Fires when the text for this node is changed
33226         * @param {Node} this This node
33227         * @param {String} text The new text
33228         * @param {String} oldText The old text
33229         */
33230         "textchange" : true,
33231         /**
33232         * @event beforeexpand
33233         * Fires before this node is expanded, return false to cancel.
33234         * @param {Node} this This node
33235         * @param {Boolean} deep
33236         * @param {Boolean} anim
33237         */
33238         "beforeexpand" : true,
33239         /**
33240         * @event beforecollapse
33241         * Fires before this node is collapsed, return false to cancel.
33242         * @param {Node} this This node
33243         * @param {Boolean} deep
33244         * @param {Boolean} anim
33245         */
33246         "beforecollapse" : true,
33247         /**
33248         * @event expand
33249         * Fires when this node is expanded
33250         * @param {Node} this This node
33251         */
33252         "expand" : true,
33253         /**
33254         * @event disabledchange
33255         * Fires when the disabled status of this node changes
33256         * @param {Node} this This node
33257         * @param {Boolean} disabled
33258         */
33259         "disabledchange" : true,
33260         /**
33261         * @event collapse
33262         * Fires when this node is collapsed
33263         * @param {Node} this This node
33264         */
33265         "collapse" : true,
33266         /**
33267         * @event beforeclick
33268         * Fires before click processing. Return false to cancel the default action.
33269         * @param {Node} this This node
33270         * @param {Roo.EventObject} e The event object
33271         */
33272         "beforeclick":true,
33273         /**
33274         * @event checkchange
33275         * Fires when a node with a checkbox's checked property changes
33276         * @param {Node} this This node
33277         * @param {Boolean} checked
33278         */
33279         "checkchange":true,
33280         /**
33281         * @event click
33282         * Fires when this node is clicked
33283         * @param {Node} this This node
33284         * @param {Roo.EventObject} e The event object
33285         */
33286         "click":true,
33287         /**
33288         * @event dblclick
33289         * Fires when this node is double clicked
33290         * @param {Node} this This node
33291         * @param {Roo.EventObject} e The event object
33292         */
33293         "dblclick":true,
33294         /**
33295         * @event contextmenu
33296         * Fires when this node is right clicked
33297         * @param {Node} this This node
33298         * @param {Roo.EventObject} e The event object
33299         */
33300         "contextmenu":true,
33301         /**
33302         * @event beforechildrenrendered
33303         * Fires right before the child nodes for this node are rendered
33304         * @param {Node} this This node
33305         */
33306         "beforechildrenrendered":true
33307     });
33308
33309     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33310
33311     /**
33312      * Read-only. The UI for this node
33313      * @type TreeNodeUI
33314      */
33315     this.ui = new uiClass(this);
33316     
33317     // finally support items[]
33318     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33319         return;
33320     }
33321     
33322     
33323     Roo.each(this.attributes.items, function(c) {
33324         this.appendChild(Roo.factory(c,Roo.Tree));
33325     }, this);
33326     delete this.attributes.items;
33327     
33328     
33329     
33330 };
33331 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33332     preventHScroll: true,
33333     /**
33334      * Returns true if this node is expanded
33335      * @return {Boolean}
33336      */
33337     isExpanded : function(){
33338         return this.expanded;
33339     },
33340
33341     /**
33342      * Returns the UI object for this node
33343      * @return {TreeNodeUI}
33344      */
33345     getUI : function(){
33346         return this.ui;
33347     },
33348
33349     // private override
33350     setFirstChild : function(node){
33351         var of = this.firstChild;
33352         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33353         if(this.childrenRendered && of && node != of){
33354             of.renderIndent(true, true);
33355         }
33356         if(this.rendered){
33357             this.renderIndent(true, true);
33358         }
33359     },
33360
33361     // private override
33362     setLastChild : function(node){
33363         var ol = this.lastChild;
33364         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33365         if(this.childrenRendered && ol && node != ol){
33366             ol.renderIndent(true, true);
33367         }
33368         if(this.rendered){
33369             this.renderIndent(true, true);
33370         }
33371     },
33372
33373     // these methods are overridden to provide lazy rendering support
33374     // private override
33375     appendChild : function()
33376     {
33377         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33378         if(node && this.childrenRendered){
33379             node.render();
33380         }
33381         this.ui.updateExpandIcon();
33382         return node;
33383     },
33384
33385     // private override
33386     removeChild : function(node){
33387         this.ownerTree.getSelectionModel().unselect(node);
33388         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33389         // if it's been rendered remove dom node
33390         if(this.childrenRendered){
33391             node.ui.remove();
33392         }
33393         if(this.childNodes.length < 1){
33394             this.collapse(false, false);
33395         }else{
33396             this.ui.updateExpandIcon();
33397         }
33398         if(!this.firstChild) {
33399             this.childrenRendered = false;
33400         }
33401         return node;
33402     },
33403
33404     // private override
33405     insertBefore : function(node, refNode){
33406         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33407         if(newNode && refNode && this.childrenRendered){
33408             node.render();
33409         }
33410         this.ui.updateExpandIcon();
33411         return newNode;
33412     },
33413
33414     /**
33415      * Sets the text for this node
33416      * @param {String} text
33417      */
33418     setText : function(text){
33419         var oldText = this.text;
33420         this.text = text;
33421         this.attributes.text = text;
33422         if(this.rendered){ // event without subscribing
33423             this.ui.onTextChange(this, text, oldText);
33424         }
33425         this.fireEvent("textchange", this, text, oldText);
33426     },
33427
33428     /**
33429      * Triggers selection of this node
33430      */
33431     select : function(){
33432         this.getOwnerTree().getSelectionModel().select(this);
33433     },
33434
33435     /**
33436      * Triggers deselection of this node
33437      */
33438     unselect : function(){
33439         this.getOwnerTree().getSelectionModel().unselect(this);
33440     },
33441
33442     /**
33443      * Returns true if this node is selected
33444      * @return {Boolean}
33445      */
33446     isSelected : function(){
33447         return this.getOwnerTree().getSelectionModel().isSelected(this);
33448     },
33449
33450     /**
33451      * Expand this node.
33452      * @param {Boolean} deep (optional) True to expand all children as well
33453      * @param {Boolean} anim (optional) false to cancel the default animation
33454      * @param {Function} callback (optional) A callback to be called when
33455      * expanding this node completes (does not wait for deep expand to complete).
33456      * Called with 1 parameter, this node.
33457      */
33458     expand : function(deep, anim, callback){
33459         if(!this.expanded){
33460             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33461                 return;
33462             }
33463             if(!this.childrenRendered){
33464                 this.renderChildren();
33465             }
33466             this.expanded = true;
33467             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33468                 this.ui.animExpand(function(){
33469                     this.fireEvent("expand", this);
33470                     if(typeof callback == "function"){
33471                         callback(this);
33472                     }
33473                     if(deep === true){
33474                         this.expandChildNodes(true);
33475                     }
33476                 }.createDelegate(this));
33477                 return;
33478             }else{
33479                 this.ui.expand();
33480                 this.fireEvent("expand", this);
33481                 if(typeof callback == "function"){
33482                     callback(this);
33483                 }
33484             }
33485         }else{
33486            if(typeof callback == "function"){
33487                callback(this);
33488            }
33489         }
33490         if(deep === true){
33491             this.expandChildNodes(true);
33492         }
33493     },
33494
33495     isHiddenRoot : function(){
33496         return this.isRoot && !this.getOwnerTree().rootVisible;
33497     },
33498
33499     /**
33500      * Collapse this node.
33501      * @param {Boolean} deep (optional) True to collapse all children as well
33502      * @param {Boolean} anim (optional) false to cancel the default animation
33503      */
33504     collapse : function(deep, anim){
33505         if(this.expanded && !this.isHiddenRoot()){
33506             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33507                 return;
33508             }
33509             this.expanded = false;
33510             if((this.getOwnerTree().animate && anim !== false) || anim){
33511                 this.ui.animCollapse(function(){
33512                     this.fireEvent("collapse", this);
33513                     if(deep === true){
33514                         this.collapseChildNodes(true);
33515                     }
33516                 }.createDelegate(this));
33517                 return;
33518             }else{
33519                 this.ui.collapse();
33520                 this.fireEvent("collapse", this);
33521             }
33522         }
33523         if(deep === true){
33524             var cs = this.childNodes;
33525             for(var i = 0, len = cs.length; i < len; i++) {
33526                 cs[i].collapse(true, false);
33527             }
33528         }
33529     },
33530
33531     // private
33532     delayedExpand : function(delay){
33533         if(!this.expandProcId){
33534             this.expandProcId = this.expand.defer(delay, this);
33535         }
33536     },
33537
33538     // private
33539     cancelExpand : function(){
33540         if(this.expandProcId){
33541             clearTimeout(this.expandProcId);
33542         }
33543         this.expandProcId = false;
33544     },
33545
33546     /**
33547      * Toggles expanded/collapsed state of the node
33548      */
33549     toggle : function(){
33550         if(this.expanded){
33551             this.collapse();
33552         }else{
33553             this.expand();
33554         }
33555     },
33556
33557     /**
33558      * Ensures all parent nodes are expanded
33559      */
33560     ensureVisible : function(callback){
33561         var tree = this.getOwnerTree();
33562         tree.expandPath(this.parentNode.getPath(), false, function(){
33563             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33564             Roo.callback(callback);
33565         }.createDelegate(this));
33566     },
33567
33568     /**
33569      * Expand all child nodes
33570      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33571      */
33572     expandChildNodes : function(deep){
33573         var cs = this.childNodes;
33574         for(var i = 0, len = cs.length; i < len; i++) {
33575                 cs[i].expand(deep);
33576         }
33577     },
33578
33579     /**
33580      * Collapse all child nodes
33581      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33582      */
33583     collapseChildNodes : function(deep){
33584         var cs = this.childNodes;
33585         for(var i = 0, len = cs.length; i < len; i++) {
33586                 cs[i].collapse(deep);
33587         }
33588     },
33589
33590     /**
33591      * Disables this node
33592      */
33593     disable : function(){
33594         this.disabled = true;
33595         this.unselect();
33596         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33597             this.ui.onDisableChange(this, true);
33598         }
33599         this.fireEvent("disabledchange", this, true);
33600     },
33601
33602     /**
33603      * Enables this node
33604      */
33605     enable : function(){
33606         this.disabled = false;
33607         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33608             this.ui.onDisableChange(this, false);
33609         }
33610         this.fireEvent("disabledchange", this, false);
33611     },
33612
33613     // private
33614     renderChildren : function(suppressEvent){
33615         if(suppressEvent !== false){
33616             this.fireEvent("beforechildrenrendered", this);
33617         }
33618         var cs = this.childNodes;
33619         for(var i = 0, len = cs.length; i < len; i++){
33620             cs[i].render(true);
33621         }
33622         this.childrenRendered = true;
33623     },
33624
33625     // private
33626     sort : function(fn, scope){
33627         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33628         if(this.childrenRendered){
33629             var cs = this.childNodes;
33630             for(var i = 0, len = cs.length; i < len; i++){
33631                 cs[i].render(true);
33632             }
33633         }
33634     },
33635
33636     // private
33637     render : function(bulkRender){
33638         this.ui.render(bulkRender);
33639         if(!this.rendered){
33640             this.rendered = true;
33641             if(this.expanded){
33642                 this.expanded = false;
33643                 this.expand(false, false);
33644             }
33645         }
33646     },
33647
33648     // private
33649     renderIndent : function(deep, refresh){
33650         if(refresh){
33651             this.ui.childIndent = null;
33652         }
33653         this.ui.renderIndent();
33654         if(deep === true && this.childrenRendered){
33655             var cs = this.childNodes;
33656             for(var i = 0, len = cs.length; i < len; i++){
33657                 cs[i].renderIndent(true, refresh);
33658             }
33659         }
33660     }
33661 });/*
33662  * Based on:
33663  * Ext JS Library 1.1.1
33664  * Copyright(c) 2006-2007, Ext JS, LLC.
33665  *
33666  * Originally Released Under LGPL - original licence link has changed is not relivant.
33667  *
33668  * Fork - LGPL
33669  * <script type="text/javascript">
33670  */
33671  
33672 /**
33673  * @class Roo.tree.AsyncTreeNode
33674  * @extends Roo.tree.TreeNode
33675  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33676  * @constructor
33677  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33678  */
33679  Roo.tree.AsyncTreeNode = function(config){
33680     this.loaded = false;
33681     this.loading = false;
33682     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33683     /**
33684     * @event beforeload
33685     * Fires before this node is loaded, return false to cancel
33686     * @param {Node} this This node
33687     */
33688     this.addEvents({'beforeload':true, 'load': true});
33689     /**
33690     * @event load
33691     * Fires when this node is loaded
33692     * @param {Node} this This node
33693     */
33694     /**
33695      * The loader used by this node (defaults to using the tree's defined loader)
33696      * @type TreeLoader
33697      * @property loader
33698      */
33699 };
33700 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33701     expand : function(deep, anim, callback){
33702         if(this.loading){ // if an async load is already running, waiting til it's done
33703             var timer;
33704             var f = function(){
33705                 if(!this.loading){ // done loading
33706                     clearInterval(timer);
33707                     this.expand(deep, anim, callback);
33708                 }
33709             }.createDelegate(this);
33710             timer = setInterval(f, 200);
33711             return;
33712         }
33713         if(!this.loaded){
33714             if(this.fireEvent("beforeload", this) === false){
33715                 return;
33716             }
33717             this.loading = true;
33718             this.ui.beforeLoad(this);
33719             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33720             if(loader){
33721                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33722                 return;
33723             }
33724         }
33725         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33726     },
33727     
33728     /**
33729      * Returns true if this node is currently loading
33730      * @return {Boolean}
33731      */
33732     isLoading : function(){
33733         return this.loading;  
33734     },
33735     
33736     loadComplete : function(deep, anim, callback){
33737         this.loading = false;
33738         this.loaded = true;
33739         this.ui.afterLoad(this);
33740         this.fireEvent("load", this);
33741         this.expand(deep, anim, callback);
33742     },
33743     
33744     /**
33745      * Returns true if this node has been loaded
33746      * @return {Boolean}
33747      */
33748     isLoaded : function(){
33749         return this.loaded;
33750     },
33751     
33752     hasChildNodes : function(){
33753         if(!this.isLeaf() && !this.loaded){
33754             return true;
33755         }else{
33756             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33757         }
33758     },
33759
33760     /**
33761      * Trigger a reload for this node
33762      * @param {Function} callback
33763      */
33764     reload : function(callback){
33765         this.collapse(false, false);
33766         while(this.firstChild){
33767             this.removeChild(this.firstChild);
33768         }
33769         this.childrenRendered = false;
33770         this.loaded = false;
33771         if(this.isHiddenRoot()){
33772             this.expanded = false;
33773         }
33774         this.expand(false, false, callback);
33775     }
33776 });/*
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 /**
33788  * @class Roo.tree.TreeNodeUI
33789  * @constructor
33790  * @param {Object} node The node to render
33791  * The TreeNode UI implementation is separate from the
33792  * tree implementation. Unless you are customizing the tree UI,
33793  * you should never have to use this directly.
33794  */
33795 Roo.tree.TreeNodeUI = function(node){
33796     this.node = node;
33797     this.rendered = false;
33798     this.animating = false;
33799     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33800 };
33801
33802 Roo.tree.TreeNodeUI.prototype = {
33803     removeChild : function(node){
33804         if(this.rendered){
33805             this.ctNode.removeChild(node.ui.getEl());
33806         }
33807     },
33808
33809     beforeLoad : function(){
33810          this.addClass("x-tree-node-loading");
33811     },
33812
33813     afterLoad : function(){
33814          this.removeClass("x-tree-node-loading");
33815     },
33816
33817     onTextChange : function(node, text, oldText){
33818         if(this.rendered){
33819             this.textNode.innerHTML = text;
33820         }
33821     },
33822
33823     onDisableChange : function(node, state){
33824         this.disabled = state;
33825         if(state){
33826             this.addClass("x-tree-node-disabled");
33827         }else{
33828             this.removeClass("x-tree-node-disabled");
33829         }
33830     },
33831
33832     onSelectedChange : function(state){
33833         if(state){
33834             this.focus();
33835             this.addClass("x-tree-selected");
33836         }else{
33837             //this.blur();
33838             this.removeClass("x-tree-selected");
33839         }
33840     },
33841
33842     onMove : function(tree, node, oldParent, newParent, index, refNode){
33843         this.childIndent = null;
33844         if(this.rendered){
33845             var targetNode = newParent.ui.getContainer();
33846             if(!targetNode){//target not rendered
33847                 this.holder = document.createElement("div");
33848                 this.holder.appendChild(this.wrap);
33849                 return;
33850             }
33851             var insertBefore = refNode ? refNode.ui.getEl() : null;
33852             if(insertBefore){
33853                 targetNode.insertBefore(this.wrap, insertBefore);
33854             }else{
33855                 targetNode.appendChild(this.wrap);
33856             }
33857             this.node.renderIndent(true);
33858         }
33859     },
33860
33861     addClass : function(cls){
33862         if(this.elNode){
33863             Roo.fly(this.elNode).addClass(cls);
33864         }
33865     },
33866
33867     removeClass : function(cls){
33868         if(this.elNode){
33869             Roo.fly(this.elNode).removeClass(cls);
33870         }
33871     },
33872
33873     remove : function(){
33874         if(this.rendered){
33875             this.holder = document.createElement("div");
33876             this.holder.appendChild(this.wrap);
33877         }
33878     },
33879
33880     fireEvent : function(){
33881         return this.node.fireEvent.apply(this.node, arguments);
33882     },
33883
33884     initEvents : function(){
33885         this.node.on("move", this.onMove, this);
33886         var E = Roo.EventManager;
33887         var a = this.anchor;
33888
33889         var el = Roo.fly(a, '_treeui');
33890
33891         if(Roo.isOpera){ // opera render bug ignores the CSS
33892             el.setStyle("text-decoration", "none");
33893         }
33894
33895         el.on("click", this.onClick, this);
33896         el.on("dblclick", this.onDblClick, this);
33897
33898         if(this.checkbox){
33899             Roo.EventManager.on(this.checkbox,
33900                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33901         }
33902
33903         el.on("contextmenu", this.onContextMenu, this);
33904
33905         var icon = Roo.fly(this.iconNode);
33906         icon.on("click", this.onClick, this);
33907         icon.on("dblclick", this.onDblClick, this);
33908         icon.on("contextmenu", this.onContextMenu, this);
33909         E.on(this.ecNode, "click", this.ecClick, this, true);
33910
33911         if(this.node.disabled){
33912             this.addClass("x-tree-node-disabled");
33913         }
33914         if(this.node.hidden){
33915             this.addClass("x-tree-node-disabled");
33916         }
33917         var ot = this.node.getOwnerTree();
33918         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33919         if(dd && (!this.node.isRoot || ot.rootVisible)){
33920             Roo.dd.Registry.register(this.elNode, {
33921                 node: this.node,
33922                 handles: this.getDDHandles(),
33923                 isHandle: false
33924             });
33925         }
33926     },
33927
33928     getDDHandles : function(){
33929         return [this.iconNode, this.textNode];
33930     },
33931
33932     hide : function(){
33933         if(this.rendered){
33934             this.wrap.style.display = "none";
33935         }
33936     },
33937
33938     show : function(){
33939         if(this.rendered){
33940             this.wrap.style.display = "";
33941         }
33942     },
33943
33944     onContextMenu : function(e){
33945         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33946             e.preventDefault();
33947             this.focus();
33948             this.fireEvent("contextmenu", this.node, e);
33949         }
33950     },
33951
33952     onClick : function(e){
33953         if(this.dropping){
33954             e.stopEvent();
33955             return;
33956         }
33957         if(this.fireEvent("beforeclick", this.node, e) !== false){
33958             if(!this.disabled && this.node.attributes.href){
33959                 this.fireEvent("click", this.node, e);
33960                 return;
33961             }
33962             e.preventDefault();
33963             if(this.disabled){
33964                 return;
33965             }
33966
33967             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33968                 this.node.toggle();
33969             }
33970
33971             this.fireEvent("click", this.node, e);
33972         }else{
33973             e.stopEvent();
33974         }
33975     },
33976
33977     onDblClick : function(e){
33978         e.preventDefault();
33979         if(this.disabled){
33980             return;
33981         }
33982         if(this.checkbox){
33983             this.toggleCheck();
33984         }
33985         if(!this.animating && this.node.hasChildNodes()){
33986             this.node.toggle();
33987         }
33988         this.fireEvent("dblclick", this.node, e);
33989     },
33990
33991     onCheckChange : function(){
33992         var checked = this.checkbox.checked;
33993         this.node.attributes.checked = checked;
33994         this.fireEvent('checkchange', this.node, checked);
33995     },
33996
33997     ecClick : function(e){
33998         if(!this.animating && this.node.hasChildNodes()){
33999             this.node.toggle();
34000         }
34001     },
34002
34003     startDrop : function(){
34004         this.dropping = true;
34005     },
34006
34007     // delayed drop so the click event doesn't get fired on a drop
34008     endDrop : function(){
34009        setTimeout(function(){
34010            this.dropping = false;
34011        }.createDelegate(this), 50);
34012     },
34013
34014     expand : function(){
34015         this.updateExpandIcon();
34016         this.ctNode.style.display = "";
34017     },
34018
34019     focus : function(){
34020         if(!this.node.preventHScroll){
34021             try{this.anchor.focus();
34022             }catch(e){}
34023         }else if(!Roo.isIE){
34024             try{
34025                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34026                 var l = noscroll.scrollLeft;
34027                 this.anchor.focus();
34028                 noscroll.scrollLeft = l;
34029             }catch(e){}
34030         }
34031     },
34032
34033     toggleCheck : function(value){
34034         var cb = this.checkbox;
34035         if(cb){
34036             cb.checked = (value === undefined ? !cb.checked : value);
34037         }
34038     },
34039
34040     blur : function(){
34041         try{
34042             this.anchor.blur();
34043         }catch(e){}
34044     },
34045
34046     animExpand : function(callback){
34047         var ct = Roo.get(this.ctNode);
34048         ct.stopFx();
34049         if(!this.node.hasChildNodes()){
34050             this.updateExpandIcon();
34051             this.ctNode.style.display = "";
34052             Roo.callback(callback);
34053             return;
34054         }
34055         this.animating = true;
34056         this.updateExpandIcon();
34057
34058         ct.slideIn('t', {
34059            callback : function(){
34060                this.animating = false;
34061                Roo.callback(callback);
34062             },
34063             scope: this,
34064             duration: this.node.ownerTree.duration || .25
34065         });
34066     },
34067
34068     highlight : function(){
34069         var tree = this.node.getOwnerTree();
34070         Roo.fly(this.wrap).highlight(
34071             tree.hlColor || "C3DAF9",
34072             {endColor: tree.hlBaseColor}
34073         );
34074     },
34075
34076     collapse : function(){
34077         this.updateExpandIcon();
34078         this.ctNode.style.display = "none";
34079     },
34080
34081     animCollapse : function(callback){
34082         var ct = Roo.get(this.ctNode);
34083         ct.enableDisplayMode('block');
34084         ct.stopFx();
34085
34086         this.animating = true;
34087         this.updateExpandIcon();
34088
34089         ct.slideOut('t', {
34090             callback : function(){
34091                this.animating = false;
34092                Roo.callback(callback);
34093             },
34094             scope: this,
34095             duration: this.node.ownerTree.duration || .25
34096         });
34097     },
34098
34099     getContainer : function(){
34100         return this.ctNode;
34101     },
34102
34103     getEl : function(){
34104         return this.wrap;
34105     },
34106
34107     appendDDGhost : function(ghostNode){
34108         ghostNode.appendChild(this.elNode.cloneNode(true));
34109     },
34110
34111     getDDRepairXY : function(){
34112         return Roo.lib.Dom.getXY(this.iconNode);
34113     },
34114
34115     onRender : function(){
34116         this.render();
34117     },
34118
34119     render : function(bulkRender){
34120         var n = this.node, a = n.attributes;
34121         var targetNode = n.parentNode ?
34122               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34123
34124         if(!this.rendered){
34125             this.rendered = true;
34126
34127             this.renderElements(n, a, targetNode, bulkRender);
34128
34129             if(a.qtip){
34130                if(this.textNode.setAttributeNS){
34131                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34132                    if(a.qtipTitle){
34133                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34134                    }
34135                }else{
34136                    this.textNode.setAttribute("ext:qtip", a.qtip);
34137                    if(a.qtipTitle){
34138                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34139                    }
34140                }
34141             }else if(a.qtipCfg){
34142                 a.qtipCfg.target = Roo.id(this.textNode);
34143                 Roo.QuickTips.register(a.qtipCfg);
34144             }
34145             this.initEvents();
34146             if(!this.node.expanded){
34147                 this.updateExpandIcon();
34148             }
34149         }else{
34150             if(bulkRender === true) {
34151                 targetNode.appendChild(this.wrap);
34152             }
34153         }
34154     },
34155
34156     renderElements : function(n, a, targetNode, bulkRender)
34157     {
34158         // add some indent caching, this helps performance when rendering a large tree
34159         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34160         var t = n.getOwnerTree();
34161         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34162         if (typeof(n.attributes.html) != 'undefined') {
34163             txt = n.attributes.html;
34164         }
34165         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34166         var cb = typeof a.checked == 'boolean';
34167         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34168         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34169             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34170             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34171             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34172             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34173             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34174              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34175                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34176             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34177             "</li>"];
34178
34179         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34180             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34181                                 n.nextSibling.ui.getEl(), buf.join(""));
34182         }else{
34183             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34184         }
34185
34186         this.elNode = this.wrap.childNodes[0];
34187         this.ctNode = this.wrap.childNodes[1];
34188         var cs = this.elNode.childNodes;
34189         this.indentNode = cs[0];
34190         this.ecNode = cs[1];
34191         this.iconNode = cs[2];
34192         var index = 3;
34193         if(cb){
34194             this.checkbox = cs[3];
34195             index++;
34196         }
34197         this.anchor = cs[index];
34198         this.textNode = cs[index].firstChild;
34199     },
34200
34201     getAnchor : function(){
34202         return this.anchor;
34203     },
34204
34205     getTextEl : function(){
34206         return this.textNode;
34207     },
34208
34209     getIconEl : function(){
34210         return this.iconNode;
34211     },
34212
34213     isChecked : function(){
34214         return this.checkbox ? this.checkbox.checked : false;
34215     },
34216
34217     updateExpandIcon : function(){
34218         if(this.rendered){
34219             var n = this.node, c1, c2;
34220             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34221             var hasChild = n.hasChildNodes();
34222             if(hasChild){
34223                 if(n.expanded){
34224                     cls += "-minus";
34225                     c1 = "x-tree-node-collapsed";
34226                     c2 = "x-tree-node-expanded";
34227                 }else{
34228                     cls += "-plus";
34229                     c1 = "x-tree-node-expanded";
34230                     c2 = "x-tree-node-collapsed";
34231                 }
34232                 if(this.wasLeaf){
34233                     this.removeClass("x-tree-node-leaf");
34234                     this.wasLeaf = false;
34235                 }
34236                 if(this.c1 != c1 || this.c2 != c2){
34237                     Roo.fly(this.elNode).replaceClass(c1, c2);
34238                     this.c1 = c1; this.c2 = c2;
34239                 }
34240             }else{
34241                 // this changes non-leafs into leafs if they have no children.
34242                 // it's not very rational behaviour..
34243                 
34244                 if(!this.wasLeaf && this.node.leaf){
34245                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34246                     delete this.c1;
34247                     delete this.c2;
34248                     this.wasLeaf = true;
34249                 }
34250             }
34251             var ecc = "x-tree-ec-icon "+cls;
34252             if(this.ecc != ecc){
34253                 this.ecNode.className = ecc;
34254                 this.ecc = ecc;
34255             }
34256         }
34257     },
34258
34259     getChildIndent : function(){
34260         if(!this.childIndent){
34261             var buf = [];
34262             var p = this.node;
34263             while(p){
34264                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34265                     if(!p.isLast()) {
34266                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34267                     } else {
34268                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34269                     }
34270                 }
34271                 p = p.parentNode;
34272             }
34273             this.childIndent = buf.join("");
34274         }
34275         return this.childIndent;
34276     },
34277
34278     renderIndent : function(){
34279         if(this.rendered){
34280             var indent = "";
34281             var p = this.node.parentNode;
34282             if(p){
34283                 indent = p.ui.getChildIndent();
34284             }
34285             if(this.indentMarkup != indent){ // don't rerender if not required
34286                 this.indentNode.innerHTML = indent;
34287                 this.indentMarkup = indent;
34288             }
34289             this.updateExpandIcon();
34290         }
34291     }
34292 };
34293
34294 Roo.tree.RootTreeNodeUI = function(){
34295     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34296 };
34297 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34298     render : function(){
34299         if(!this.rendered){
34300             var targetNode = this.node.ownerTree.innerCt.dom;
34301             this.node.expanded = true;
34302             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34303             this.wrap = this.ctNode = targetNode.firstChild;
34304         }
34305     },
34306     collapse : function(){
34307     },
34308     expand : function(){
34309     }
34310 });/*
34311  * Based on:
34312  * Ext JS Library 1.1.1
34313  * Copyright(c) 2006-2007, Ext JS, LLC.
34314  *
34315  * Originally Released Under LGPL - original licence link has changed is not relivant.
34316  *
34317  * Fork - LGPL
34318  * <script type="text/javascript">
34319  */
34320 /**
34321  * @class Roo.tree.TreeLoader
34322  * @extends Roo.util.Observable
34323  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34324  * nodes from a specified URL. The response must be a javascript Array definition
34325  * who's elements are node definition objects. eg:
34326  * <pre><code>
34327 {  success : true,
34328    data :      [
34329    
34330     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34331     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34332     ]
34333 }
34334
34335
34336 </code></pre>
34337  * <br><br>
34338  * The old style respose with just an array is still supported, but not recommended.
34339  * <br><br>
34340  *
34341  * A server request is sent, and child nodes are loaded only when a node is expanded.
34342  * The loading node's id is passed to the server under the parameter name "node" to
34343  * enable the server to produce the correct child nodes.
34344  * <br><br>
34345  * To pass extra parameters, an event handler may be attached to the "beforeload"
34346  * event, and the parameters specified in the TreeLoader's baseParams property:
34347  * <pre><code>
34348     myTreeLoader.on("beforeload", function(treeLoader, node) {
34349         this.baseParams.category = node.attributes.category;
34350     }, this);
34351 </code></pre><
34352  * This would pass an HTTP parameter called "category" to the server containing
34353  * the value of the Node's "category" attribute.
34354  * @constructor
34355  * Creates a new Treeloader.
34356  * @param {Object} config A config object containing config properties.
34357  */
34358 Roo.tree.TreeLoader = function(config){
34359     this.baseParams = {};
34360     this.requestMethod = "POST";
34361     Roo.apply(this, config);
34362
34363     this.addEvents({
34364     
34365         /**
34366          * @event beforeload
34367          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34368          * @param {Object} This TreeLoader object.
34369          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34370          * @param {Object} callback The callback function specified in the {@link #load} call.
34371          */
34372         beforeload : true,
34373         /**
34374          * @event load
34375          * Fires when the node has been successfuly loaded.
34376          * @param {Object} This TreeLoader object.
34377          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34378          * @param {Object} response The response object containing the data from the server.
34379          */
34380         load : true,
34381         /**
34382          * @event loadexception
34383          * Fires if the network request failed.
34384          * @param {Object} This TreeLoader object.
34385          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34386          * @param {Object} response The response object containing the data from the server.
34387          */
34388         loadexception : true,
34389         /**
34390          * @event create
34391          * Fires before a node is created, enabling you to return custom Node types 
34392          * @param {Object} This TreeLoader object.
34393          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34394          */
34395         create : true
34396     });
34397
34398     Roo.tree.TreeLoader.superclass.constructor.call(this);
34399 };
34400
34401 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34402     /**
34403     * @cfg {String} dataUrl The URL from which to request a Json string which
34404     * specifies an array of node definition object representing the child nodes
34405     * to be loaded.
34406     */
34407     /**
34408     * @cfg {String} requestMethod either GET or POST
34409     * defaults to POST (due to BC)
34410     * to be loaded.
34411     */
34412     /**
34413     * @cfg {Object} baseParams (optional) An object containing properties which
34414     * specify HTTP parameters to be passed to each request for child nodes.
34415     */
34416     /**
34417     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34418     * created by this loader. If the attributes sent by the server have an attribute in this object,
34419     * they take priority.
34420     */
34421     /**
34422     * @cfg {Object} uiProviders (optional) An object containing properties which
34423     * 
34424     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34425     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34426     * <i>uiProvider</i> attribute of a returned child node is a string rather
34427     * than a reference to a TreeNodeUI implementation, this that string value
34428     * is used as a property name in the uiProviders object. You can define the provider named
34429     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34430     */
34431     uiProviders : {},
34432
34433     /**
34434     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34435     * child nodes before loading.
34436     */
34437     clearOnLoad : true,
34438
34439     /**
34440     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34441     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34442     * Grid query { data : [ .....] }
34443     */
34444     
34445     root : false,
34446      /**
34447     * @cfg {String} queryParam (optional) 
34448     * Name of the query as it will be passed on the querystring (defaults to 'node')
34449     * eg. the request will be ?node=[id]
34450     */
34451     
34452     
34453     queryParam: false,
34454     
34455     /**
34456      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34457      * This is called automatically when a node is expanded, but may be used to reload
34458      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34459      * @param {Roo.tree.TreeNode} node
34460      * @param {Function} callback
34461      */
34462     load : function(node, callback){
34463         if(this.clearOnLoad){
34464             while(node.firstChild){
34465                 node.removeChild(node.firstChild);
34466             }
34467         }
34468         if(node.attributes.children){ // preloaded json children
34469             var cs = node.attributes.children;
34470             for(var i = 0, len = cs.length; i < len; i++){
34471                 node.appendChild(this.createNode(cs[i]));
34472             }
34473             if(typeof callback == "function"){
34474                 callback();
34475             }
34476         }else if(this.dataUrl){
34477             this.requestData(node, callback);
34478         }
34479     },
34480
34481     getParams: function(node){
34482         var buf = [], bp = this.baseParams;
34483         for(var key in bp){
34484             if(typeof bp[key] != "function"){
34485                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34486             }
34487         }
34488         var n = this.queryParam === false ? 'node' : this.queryParam;
34489         buf.push(n + "=", encodeURIComponent(node.id));
34490         return buf.join("");
34491     },
34492
34493     requestData : function(node, callback){
34494         if(this.fireEvent("beforeload", this, node, callback) !== false){
34495             this.transId = Roo.Ajax.request({
34496                 method:this.requestMethod,
34497                 url: this.dataUrl||this.url,
34498                 success: this.handleResponse,
34499                 failure: this.handleFailure,
34500                 scope: this,
34501                 argument: {callback: callback, node: node},
34502                 params: this.getParams(node)
34503             });
34504         }else{
34505             // if the load is cancelled, make sure we notify
34506             // the node that we are done
34507             if(typeof callback == "function"){
34508                 callback();
34509             }
34510         }
34511     },
34512
34513     isLoading : function(){
34514         return this.transId ? true : false;
34515     },
34516
34517     abort : function(){
34518         if(this.isLoading()){
34519             Roo.Ajax.abort(this.transId);
34520         }
34521     },
34522
34523     // private
34524     createNode : function(attr)
34525     {
34526         // apply baseAttrs, nice idea Corey!
34527         if(this.baseAttrs){
34528             Roo.applyIf(attr, this.baseAttrs);
34529         }
34530         if(this.applyLoader !== false){
34531             attr.loader = this;
34532         }
34533         // uiProvider = depreciated..
34534         
34535         if(typeof(attr.uiProvider) == 'string'){
34536            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34537                 /**  eval:var:attr */ eval(attr.uiProvider);
34538         }
34539         if(typeof(this.uiProviders['default']) != 'undefined') {
34540             attr.uiProvider = this.uiProviders['default'];
34541         }
34542         
34543         this.fireEvent('create', this, attr);
34544         
34545         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34546         return(attr.leaf ?
34547                         new Roo.tree.TreeNode(attr) :
34548                         new Roo.tree.AsyncTreeNode(attr));
34549     },
34550
34551     processResponse : function(response, node, callback)
34552     {
34553         var json = response.responseText;
34554         try {
34555             
34556             var o = Roo.decode(json);
34557             
34558             if (this.root === false && typeof(o.success) != undefined) {
34559                 this.root = 'data'; // the default behaviour for list like data..
34560                 }
34561                 
34562             if (this.root !== false &&  !o.success) {
34563                 // it's a failure condition.
34564                 var a = response.argument;
34565                 this.fireEvent("loadexception", this, a.node, response);
34566                 Roo.log("Load failed - should have a handler really");
34567                 return;
34568             }
34569             
34570             
34571             
34572             if (this.root !== false) {
34573                  o = o[this.root];
34574             }
34575             
34576             for(var i = 0, len = o.length; i < len; i++){
34577                 var n = this.createNode(o[i]);
34578                 if(n){
34579                     node.appendChild(n);
34580                 }
34581             }
34582             if(typeof callback == "function"){
34583                 callback(this, node);
34584             }
34585         }catch(e){
34586             this.handleFailure(response);
34587         }
34588     },
34589
34590     handleResponse : function(response){
34591         this.transId = false;
34592         var a = response.argument;
34593         this.processResponse(response, a.node, a.callback);
34594         this.fireEvent("load", this, a.node, response);
34595     },
34596
34597     handleFailure : function(response)
34598     {
34599         // should handle failure better..
34600         this.transId = false;
34601         var a = response.argument;
34602         this.fireEvent("loadexception", this, a.node, response);
34603         if(typeof a.callback == "function"){
34604             a.callback(this, a.node);
34605         }
34606     }
34607 });/*
34608  * Based on:
34609  * Ext JS Library 1.1.1
34610  * Copyright(c) 2006-2007, Ext JS, LLC.
34611  *
34612  * Originally Released Under LGPL - original licence link has changed is not relivant.
34613  *
34614  * Fork - LGPL
34615  * <script type="text/javascript">
34616  */
34617
34618 /**
34619 * @class Roo.tree.TreeFilter
34620 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34621 * @param {TreePanel} tree
34622 * @param {Object} config (optional)
34623  */
34624 Roo.tree.TreeFilter = function(tree, config){
34625     this.tree = tree;
34626     this.filtered = {};
34627     Roo.apply(this, config);
34628 };
34629
34630 Roo.tree.TreeFilter.prototype = {
34631     clearBlank:false,
34632     reverse:false,
34633     autoClear:false,
34634     remove:false,
34635
34636      /**
34637      * Filter the data by a specific attribute.
34638      * @param {String/RegExp} value Either string that the attribute value
34639      * should start with or a RegExp to test against the attribute
34640      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34641      * @param {TreeNode} startNode (optional) The node to start the filter at.
34642      */
34643     filter : function(value, attr, startNode){
34644         attr = attr || "text";
34645         var f;
34646         if(typeof value == "string"){
34647             var vlen = value.length;
34648             // auto clear empty filter
34649             if(vlen == 0 && this.clearBlank){
34650                 this.clear();
34651                 return;
34652             }
34653             value = value.toLowerCase();
34654             f = function(n){
34655                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34656             };
34657         }else if(value.exec){ // regex?
34658             f = function(n){
34659                 return value.test(n.attributes[attr]);
34660             };
34661         }else{
34662             throw 'Illegal filter type, must be string or regex';
34663         }
34664         this.filterBy(f, null, startNode);
34665         },
34666
34667     /**
34668      * Filter by a function. The passed function will be called with each
34669      * node in the tree (or from the startNode). If the function returns true, the node is kept
34670      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34671      * @param {Function} fn The filter function
34672      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34673      */
34674     filterBy : function(fn, scope, startNode){
34675         startNode = startNode || this.tree.root;
34676         if(this.autoClear){
34677             this.clear();
34678         }
34679         var af = this.filtered, rv = this.reverse;
34680         var f = function(n){
34681             if(n == startNode){
34682                 return true;
34683             }
34684             if(af[n.id]){
34685                 return false;
34686             }
34687             var m = fn.call(scope || n, n);
34688             if(!m || rv){
34689                 af[n.id] = n;
34690                 n.ui.hide();
34691                 return false;
34692             }
34693             return true;
34694         };
34695         startNode.cascade(f);
34696         if(this.remove){
34697            for(var id in af){
34698                if(typeof id != "function"){
34699                    var n = af[id];
34700                    if(n && n.parentNode){
34701                        n.parentNode.removeChild(n);
34702                    }
34703                }
34704            }
34705         }
34706     },
34707
34708     /**
34709      * Clears the current filter. Note: with the "remove" option
34710      * set a filter cannot be cleared.
34711      */
34712     clear : function(){
34713         var t = this.tree;
34714         var af = this.filtered;
34715         for(var id in af){
34716             if(typeof id != "function"){
34717                 var n = af[id];
34718                 if(n){
34719                     n.ui.show();
34720                 }
34721             }
34722         }
34723         this.filtered = {};
34724     }
34725 };
34726 /*
34727  * Based on:
34728  * Ext JS Library 1.1.1
34729  * Copyright(c) 2006-2007, Ext JS, LLC.
34730  *
34731  * Originally Released Under LGPL - original licence link has changed is not relivant.
34732  *
34733  * Fork - LGPL
34734  * <script type="text/javascript">
34735  */
34736  
34737
34738 /**
34739  * @class Roo.tree.TreeSorter
34740  * Provides sorting of nodes in a TreePanel
34741  * 
34742  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34743  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34744  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34745  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34746  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34747  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34748  * @constructor
34749  * @param {TreePanel} tree
34750  * @param {Object} config
34751  */
34752 Roo.tree.TreeSorter = function(tree, config){
34753     Roo.apply(this, config);
34754     tree.on("beforechildrenrendered", this.doSort, this);
34755     tree.on("append", this.updateSort, this);
34756     tree.on("insert", this.updateSort, this);
34757     
34758     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34759     var p = this.property || "text";
34760     var sortType = this.sortType;
34761     var fs = this.folderSort;
34762     var cs = this.caseSensitive === true;
34763     var leafAttr = this.leafAttr || 'leaf';
34764
34765     this.sortFn = function(n1, n2){
34766         if(fs){
34767             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34768                 return 1;
34769             }
34770             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34771                 return -1;
34772             }
34773         }
34774         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34775         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34776         if(v1 < v2){
34777                         return dsc ? +1 : -1;
34778                 }else if(v1 > v2){
34779                         return dsc ? -1 : +1;
34780         }else{
34781                 return 0;
34782         }
34783     };
34784 };
34785
34786 Roo.tree.TreeSorter.prototype = {
34787     doSort : function(node){
34788         node.sort(this.sortFn);
34789     },
34790     
34791     compareNodes : function(n1, n2){
34792         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34793     },
34794     
34795     updateSort : function(tree, node){
34796         if(node.childrenRendered){
34797             this.doSort.defer(1, this, [node]);
34798         }
34799     }
34800 };/*
34801  * Based on:
34802  * Ext JS Library 1.1.1
34803  * Copyright(c) 2006-2007, Ext JS, LLC.
34804  *
34805  * Originally Released Under LGPL - original licence link has changed is not relivant.
34806  *
34807  * Fork - LGPL
34808  * <script type="text/javascript">
34809  */
34810
34811 if(Roo.dd.DropZone){
34812     
34813 Roo.tree.TreeDropZone = function(tree, config){
34814     this.allowParentInsert = false;
34815     this.allowContainerDrop = false;
34816     this.appendOnly = false;
34817     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34818     this.tree = tree;
34819     this.lastInsertClass = "x-tree-no-status";
34820     this.dragOverData = {};
34821 };
34822
34823 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34824     ddGroup : "TreeDD",
34825     scroll:  true,
34826     
34827     expandDelay : 1000,
34828     
34829     expandNode : function(node){
34830         if(node.hasChildNodes() && !node.isExpanded()){
34831             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34832         }
34833     },
34834     
34835     queueExpand : function(node){
34836         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34837     },
34838     
34839     cancelExpand : function(){
34840         if(this.expandProcId){
34841             clearTimeout(this.expandProcId);
34842             this.expandProcId = false;
34843         }
34844     },
34845     
34846     isValidDropPoint : function(n, pt, dd, e, data){
34847         if(!n || !data){ return false; }
34848         var targetNode = n.node;
34849         var dropNode = data.node;
34850         // default drop rules
34851         if(!(targetNode && targetNode.isTarget && pt)){
34852             return false;
34853         }
34854         if(pt == "append" && targetNode.allowChildren === false){
34855             return false;
34856         }
34857         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34858             return false;
34859         }
34860         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34861             return false;
34862         }
34863         // reuse the object
34864         var overEvent = this.dragOverData;
34865         overEvent.tree = this.tree;
34866         overEvent.target = targetNode;
34867         overEvent.data = data;
34868         overEvent.point = pt;
34869         overEvent.source = dd;
34870         overEvent.rawEvent = e;
34871         overEvent.dropNode = dropNode;
34872         overEvent.cancel = false;  
34873         var result = this.tree.fireEvent("nodedragover", overEvent);
34874         return overEvent.cancel === false && result !== false;
34875     },
34876     
34877     getDropPoint : function(e, n, dd)
34878     {
34879         var tn = n.node;
34880         if(tn.isRoot){
34881             return tn.allowChildren !== false ? "append" : false; // always append for root
34882         }
34883         var dragEl = n.ddel;
34884         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34885         var y = Roo.lib.Event.getPageY(e);
34886         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34887         
34888         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34889         var noAppend = tn.allowChildren === false;
34890         if(this.appendOnly || tn.parentNode.allowChildren === false){
34891             return noAppend ? false : "append";
34892         }
34893         var noBelow = false;
34894         if(!this.allowParentInsert){
34895             noBelow = tn.hasChildNodes() && tn.isExpanded();
34896         }
34897         var q = (b - t) / (noAppend ? 2 : 3);
34898         if(y >= t && y < (t + q)){
34899             return "above";
34900         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34901             return "below";
34902         }else{
34903             return "append";
34904         }
34905     },
34906     
34907     onNodeEnter : function(n, dd, e, data)
34908     {
34909         this.cancelExpand();
34910     },
34911     
34912     onNodeOver : function(n, dd, e, data)
34913     {
34914        
34915         var pt = this.getDropPoint(e, n, dd);
34916         var node = n.node;
34917         
34918         // auto node expand check
34919         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34920             this.queueExpand(node);
34921         }else if(pt != "append"){
34922             this.cancelExpand();
34923         }
34924         
34925         // set the insert point style on the target node
34926         var returnCls = this.dropNotAllowed;
34927         if(this.isValidDropPoint(n, pt, dd, e, data)){
34928            if(pt){
34929                var el = n.ddel;
34930                var cls;
34931                if(pt == "above"){
34932                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34933                    cls = "x-tree-drag-insert-above";
34934                }else if(pt == "below"){
34935                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34936                    cls = "x-tree-drag-insert-below";
34937                }else{
34938                    returnCls = "x-tree-drop-ok-append";
34939                    cls = "x-tree-drag-append";
34940                }
34941                if(this.lastInsertClass != cls){
34942                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34943                    this.lastInsertClass = cls;
34944                }
34945            }
34946        }
34947        return returnCls;
34948     },
34949     
34950     onNodeOut : function(n, dd, e, data){
34951         
34952         this.cancelExpand();
34953         this.removeDropIndicators(n);
34954     },
34955     
34956     onNodeDrop : function(n, dd, e, data){
34957         var point = this.getDropPoint(e, n, dd);
34958         var targetNode = n.node;
34959         targetNode.ui.startDrop();
34960         if(!this.isValidDropPoint(n, point, dd, e, data)){
34961             targetNode.ui.endDrop();
34962             return false;
34963         }
34964         // first try to find the drop node
34965         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34966         var dropEvent = {
34967             tree : this.tree,
34968             target: targetNode,
34969             data: data,
34970             point: point,
34971             source: dd,
34972             rawEvent: e,
34973             dropNode: dropNode,
34974             cancel: !dropNode   
34975         };
34976         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34977         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34978             targetNode.ui.endDrop();
34979             return false;
34980         }
34981         // allow target changing
34982         targetNode = dropEvent.target;
34983         if(point == "append" && !targetNode.isExpanded()){
34984             targetNode.expand(false, null, function(){
34985                 this.completeDrop(dropEvent);
34986             }.createDelegate(this));
34987         }else{
34988             this.completeDrop(dropEvent);
34989         }
34990         return true;
34991     },
34992     
34993     completeDrop : function(de){
34994         var ns = de.dropNode, p = de.point, t = de.target;
34995         if(!(ns instanceof Array)){
34996             ns = [ns];
34997         }
34998         var n;
34999         for(var i = 0, len = ns.length; i < len; i++){
35000             n = ns[i];
35001             if(p == "above"){
35002                 t.parentNode.insertBefore(n, t);
35003             }else if(p == "below"){
35004                 t.parentNode.insertBefore(n, t.nextSibling);
35005             }else{
35006                 t.appendChild(n);
35007             }
35008         }
35009         n.ui.focus();
35010         if(this.tree.hlDrop){
35011             n.ui.highlight();
35012         }
35013         t.ui.endDrop();
35014         this.tree.fireEvent("nodedrop", de);
35015     },
35016     
35017     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35018         if(this.tree.hlDrop){
35019             dropNode.ui.focus();
35020             dropNode.ui.highlight();
35021         }
35022         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35023     },
35024     
35025     getTree : function(){
35026         return this.tree;
35027     },
35028     
35029     removeDropIndicators : function(n){
35030         if(n && n.ddel){
35031             var el = n.ddel;
35032             Roo.fly(el).removeClass([
35033                     "x-tree-drag-insert-above",
35034                     "x-tree-drag-insert-below",
35035                     "x-tree-drag-append"]);
35036             this.lastInsertClass = "_noclass";
35037         }
35038     },
35039     
35040     beforeDragDrop : function(target, e, id){
35041         this.cancelExpand();
35042         return true;
35043     },
35044     
35045     afterRepair : function(data){
35046         if(data && Roo.enableFx){
35047             data.node.ui.highlight();
35048         }
35049         this.hideProxy();
35050     } 
35051     
35052 });
35053
35054 }
35055 /*
35056  * Based on:
35057  * Ext JS Library 1.1.1
35058  * Copyright(c) 2006-2007, Ext JS, LLC.
35059  *
35060  * Originally Released Under LGPL - original licence link has changed is not relivant.
35061  *
35062  * Fork - LGPL
35063  * <script type="text/javascript">
35064  */
35065  
35066
35067 if(Roo.dd.DragZone){
35068 Roo.tree.TreeDragZone = function(tree, config){
35069     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35070     this.tree = tree;
35071 };
35072
35073 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35074     ddGroup : "TreeDD",
35075    
35076     onBeforeDrag : function(data, e){
35077         var n = data.node;
35078         return n && n.draggable && !n.disabled;
35079     },
35080      
35081     
35082     onInitDrag : function(e){
35083         var data = this.dragData;
35084         this.tree.getSelectionModel().select(data.node);
35085         this.proxy.update("");
35086         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35087         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35088     },
35089     
35090     getRepairXY : function(e, data){
35091         return data.node.ui.getDDRepairXY();
35092     },
35093     
35094     onEndDrag : function(data, e){
35095         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35096         
35097         
35098     },
35099     
35100     onValidDrop : function(dd, e, id){
35101         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35102         this.hideProxy();
35103     },
35104     
35105     beforeInvalidDrop : function(e, id){
35106         // this scrolls the original position back into view
35107         var sm = this.tree.getSelectionModel();
35108         sm.clearSelections();
35109         sm.select(this.dragData.node);
35110     }
35111 });
35112 }/*
35113  * Based on:
35114  * Ext JS Library 1.1.1
35115  * Copyright(c) 2006-2007, Ext JS, LLC.
35116  *
35117  * Originally Released Under LGPL - original licence link has changed is not relivant.
35118  *
35119  * Fork - LGPL
35120  * <script type="text/javascript">
35121  */
35122 /**
35123  * @class Roo.tree.TreeEditor
35124  * @extends Roo.Editor
35125  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35126  * as the editor field.
35127  * @constructor
35128  * @param {Object} config (used to be the tree panel.)
35129  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35130  * 
35131  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35132  * @cfg {Roo.form.TextField|Object} field The field configuration
35133  *
35134  * 
35135  */
35136 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35137     var tree = config;
35138     var field;
35139     if (oldconfig) { // old style..
35140         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35141     } else {
35142         // new style..
35143         tree = config.tree;
35144         config.field = config.field  || {};
35145         config.field.xtype = 'TextField';
35146         field = Roo.factory(config.field, Roo.form);
35147     }
35148     config = config || {};
35149     
35150     
35151     this.addEvents({
35152         /**
35153          * @event beforenodeedit
35154          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35155          * false from the handler of this event.
35156          * @param {Editor} this
35157          * @param {Roo.tree.Node} node 
35158          */
35159         "beforenodeedit" : true
35160     });
35161     
35162     //Roo.log(config);
35163     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35164
35165     this.tree = tree;
35166
35167     tree.on('beforeclick', this.beforeNodeClick, this);
35168     tree.getTreeEl().on('mousedown', this.hide, this);
35169     this.on('complete', this.updateNode, this);
35170     this.on('beforestartedit', this.fitToTree, this);
35171     this.on('startedit', this.bindScroll, this, {delay:10});
35172     this.on('specialkey', this.onSpecialKey, this);
35173 };
35174
35175 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35176     /**
35177      * @cfg {String} alignment
35178      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35179      */
35180     alignment: "l-l",
35181     // inherit
35182     autoSize: false,
35183     /**
35184      * @cfg {Boolean} hideEl
35185      * True to hide the bound element while the editor is displayed (defaults to false)
35186      */
35187     hideEl : false,
35188     /**
35189      * @cfg {String} cls
35190      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35191      */
35192     cls: "x-small-editor x-tree-editor",
35193     /**
35194      * @cfg {Boolean} shim
35195      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35196      */
35197     shim:false,
35198     // inherit
35199     shadow:"frame",
35200     /**
35201      * @cfg {Number} maxWidth
35202      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35203      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35204      * scroll and client offsets into account prior to each edit.
35205      */
35206     maxWidth: 250,
35207
35208     editDelay : 350,
35209
35210     // private
35211     fitToTree : function(ed, el){
35212         var td = this.tree.getTreeEl().dom, nd = el.dom;
35213         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35214             td.scrollLeft = nd.offsetLeft;
35215         }
35216         var w = Math.min(
35217                 this.maxWidth,
35218                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35219         this.setSize(w, '');
35220         
35221         return this.fireEvent('beforenodeedit', this, this.editNode);
35222         
35223     },
35224
35225     // private
35226     triggerEdit : function(node){
35227         this.completeEdit();
35228         this.editNode = node;
35229         this.startEdit(node.ui.textNode, node.text);
35230     },
35231
35232     // private
35233     bindScroll : function(){
35234         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35235     },
35236
35237     // private
35238     beforeNodeClick : function(node, e){
35239         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35240         this.lastClick = new Date();
35241         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35242             e.stopEvent();
35243             this.triggerEdit(node);
35244             return false;
35245         }
35246         return true;
35247     },
35248
35249     // private
35250     updateNode : function(ed, value){
35251         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35252         this.editNode.setText(value);
35253     },
35254
35255     // private
35256     onHide : function(){
35257         Roo.tree.TreeEditor.superclass.onHide.call(this);
35258         if(this.editNode){
35259             this.editNode.ui.focus();
35260         }
35261     },
35262
35263     // private
35264     onSpecialKey : function(field, e){
35265         var k = e.getKey();
35266         if(k == e.ESC){
35267             e.stopEvent();
35268             this.cancelEdit();
35269         }else if(k == e.ENTER && !e.hasModifier()){
35270             e.stopEvent();
35271             this.completeEdit();
35272         }
35273     }
35274 });//<Script type="text/javascript">
35275 /*
35276  * Based on:
35277  * Ext JS Library 1.1.1
35278  * Copyright(c) 2006-2007, Ext JS, LLC.
35279  *
35280  * Originally Released Under LGPL - original licence link has changed is not relivant.
35281  *
35282  * Fork - LGPL
35283  * <script type="text/javascript">
35284  */
35285  
35286 /**
35287  * Not documented??? - probably should be...
35288  */
35289
35290 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35291     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35292     
35293     renderElements : function(n, a, targetNode, bulkRender){
35294         //consel.log("renderElements?");
35295         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35296
35297         var t = n.getOwnerTree();
35298         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35299         
35300         var cols = t.columns;
35301         var bw = t.borderWidth;
35302         var c = cols[0];
35303         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35304          var cb = typeof a.checked == "boolean";
35305         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35306         var colcls = 'x-t-' + tid + '-c0';
35307         var buf = [
35308             '<li class="x-tree-node">',
35309             
35310                 
35311                 '<div class="x-tree-node-el ', a.cls,'">',
35312                     // extran...
35313                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35314                 
35315                 
35316                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35317                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35318                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35319                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35320                            (a.iconCls ? ' '+a.iconCls : ''),
35321                            '" unselectable="on" />',
35322                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35323                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35324                              
35325                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35326                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35327                             '<span unselectable="on" qtip="' + tx + '">',
35328                              tx,
35329                              '</span></a>' ,
35330                     '</div>',
35331                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35332                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35333                  ];
35334         for(var i = 1, len = cols.length; i < len; i++){
35335             c = cols[i];
35336             colcls = 'x-t-' + tid + '-c' +i;
35337             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35338             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35339                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35340                       "</div>");
35341          }
35342          
35343          buf.push(
35344             '</a>',
35345             '<div class="x-clear"></div></div>',
35346             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35347             "</li>");
35348         
35349         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35350             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35351                                 n.nextSibling.ui.getEl(), buf.join(""));
35352         }else{
35353             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35354         }
35355         var el = this.wrap.firstChild;
35356         this.elRow = el;
35357         this.elNode = el.firstChild;
35358         this.ranchor = el.childNodes[1];
35359         this.ctNode = this.wrap.childNodes[1];
35360         var cs = el.firstChild.childNodes;
35361         this.indentNode = cs[0];
35362         this.ecNode = cs[1];
35363         this.iconNode = cs[2];
35364         var index = 3;
35365         if(cb){
35366             this.checkbox = cs[3];
35367             index++;
35368         }
35369         this.anchor = cs[index];
35370         
35371         this.textNode = cs[index].firstChild;
35372         
35373         //el.on("click", this.onClick, this);
35374         //el.on("dblclick", this.onDblClick, this);
35375         
35376         
35377        // console.log(this);
35378     },
35379     initEvents : function(){
35380         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35381         
35382             
35383         var a = this.ranchor;
35384
35385         var el = Roo.get(a);
35386
35387         if(Roo.isOpera){ // opera render bug ignores the CSS
35388             el.setStyle("text-decoration", "none");
35389         }
35390
35391         el.on("click", this.onClick, this);
35392         el.on("dblclick", this.onDblClick, this);
35393         el.on("contextmenu", this.onContextMenu, this);
35394         
35395     },
35396     
35397     /*onSelectedChange : function(state){
35398         if(state){
35399             this.focus();
35400             this.addClass("x-tree-selected");
35401         }else{
35402             //this.blur();
35403             this.removeClass("x-tree-selected");
35404         }
35405     },*/
35406     addClass : function(cls){
35407         if(this.elRow){
35408             Roo.fly(this.elRow).addClass(cls);
35409         }
35410         
35411     },
35412     
35413     
35414     removeClass : function(cls){
35415         if(this.elRow){
35416             Roo.fly(this.elRow).removeClass(cls);
35417         }
35418     }
35419
35420     
35421     
35422 });//<Script type="text/javascript">
35423
35424 /*
35425  * Based on:
35426  * Ext JS Library 1.1.1
35427  * Copyright(c) 2006-2007, Ext JS, LLC.
35428  *
35429  * Originally Released Under LGPL - original licence link has changed is not relivant.
35430  *
35431  * Fork - LGPL
35432  * <script type="text/javascript">
35433  */
35434  
35435
35436 /**
35437  * @class Roo.tree.ColumnTree
35438  * @extends Roo.data.TreePanel
35439  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35440  * @cfg {int} borderWidth  compined right/left border allowance
35441  * @constructor
35442  * @param {String/HTMLElement/Element} el The container element
35443  * @param {Object} config
35444  */
35445 Roo.tree.ColumnTree =  function(el, config)
35446 {
35447    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35448    this.addEvents({
35449         /**
35450         * @event resize
35451         * Fire this event on a container when it resizes
35452         * @param {int} w Width
35453         * @param {int} h Height
35454         */
35455        "resize" : true
35456     });
35457     this.on('resize', this.onResize, this);
35458 };
35459
35460 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35461     //lines:false,
35462     
35463     
35464     borderWidth: Roo.isBorderBox ? 0 : 2, 
35465     headEls : false,
35466     
35467     render : function(){
35468         // add the header.....
35469        
35470         Roo.tree.ColumnTree.superclass.render.apply(this);
35471         
35472         this.el.addClass('x-column-tree');
35473         
35474         this.headers = this.el.createChild(
35475             {cls:'x-tree-headers'},this.innerCt.dom);
35476    
35477         var cols = this.columns, c;
35478         var totalWidth = 0;
35479         this.headEls = [];
35480         var  len = cols.length;
35481         for(var i = 0; i < len; i++){
35482              c = cols[i];
35483              totalWidth += c.width;
35484             this.headEls.push(this.headers.createChild({
35485                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35486                  cn: {
35487                      cls:'x-tree-hd-text',
35488                      html: c.header
35489                  },
35490                  style:'width:'+(c.width-this.borderWidth)+'px;'
35491              }));
35492         }
35493         this.headers.createChild({cls:'x-clear'});
35494         // prevent floats from wrapping when clipped
35495         this.headers.setWidth(totalWidth);
35496         //this.innerCt.setWidth(totalWidth);
35497         this.innerCt.setStyle({ overflow: 'auto' });
35498         this.onResize(this.width, this.height);
35499              
35500         
35501     },
35502     onResize : function(w,h)
35503     {
35504         this.height = h;
35505         this.width = w;
35506         // resize cols..
35507         this.innerCt.setWidth(this.width);
35508         this.innerCt.setHeight(this.height-20);
35509         
35510         // headers...
35511         var cols = this.columns, c;
35512         var totalWidth = 0;
35513         var expEl = false;
35514         var len = cols.length;
35515         for(var i = 0; i < len; i++){
35516             c = cols[i];
35517             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35518                 // it's the expander..
35519                 expEl  = this.headEls[i];
35520                 continue;
35521             }
35522             totalWidth += c.width;
35523             
35524         }
35525         if (expEl) {
35526             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35527         }
35528         this.headers.setWidth(w-20);
35529
35530         
35531         
35532         
35533     }
35534 });
35535 /*
35536  * Based on:
35537  * Ext JS Library 1.1.1
35538  * Copyright(c) 2006-2007, Ext JS, LLC.
35539  *
35540  * Originally Released Under LGPL - original licence link has changed is not relivant.
35541  *
35542  * Fork - LGPL
35543  * <script type="text/javascript">
35544  */
35545  
35546 /**
35547  * @class Roo.menu.Menu
35548  * @extends Roo.util.Observable
35549  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35550  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35551  * @constructor
35552  * Creates a new Menu
35553  * @param {Object} config Configuration options
35554  */
35555 Roo.menu.Menu = function(config){
35556     Roo.apply(this, config);
35557     this.id = this.id || Roo.id();
35558     this.addEvents({
35559         /**
35560          * @event beforeshow
35561          * Fires before this menu is displayed
35562          * @param {Roo.menu.Menu} this
35563          */
35564         beforeshow : true,
35565         /**
35566          * @event beforehide
35567          * Fires before this menu is hidden
35568          * @param {Roo.menu.Menu} this
35569          */
35570         beforehide : true,
35571         /**
35572          * @event show
35573          * Fires after this menu is displayed
35574          * @param {Roo.menu.Menu} this
35575          */
35576         show : true,
35577         /**
35578          * @event hide
35579          * Fires after this menu is hidden
35580          * @param {Roo.menu.Menu} this
35581          */
35582         hide : true,
35583         /**
35584          * @event click
35585          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35586          * @param {Roo.menu.Menu} this
35587          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35588          * @param {Roo.EventObject} e
35589          */
35590         click : true,
35591         /**
35592          * @event mouseover
35593          * Fires when the mouse is hovering over this menu
35594          * @param {Roo.menu.Menu} this
35595          * @param {Roo.EventObject} e
35596          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35597          */
35598         mouseover : true,
35599         /**
35600          * @event mouseout
35601          * Fires when the mouse exits this menu
35602          * @param {Roo.menu.Menu} this
35603          * @param {Roo.EventObject} e
35604          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35605          */
35606         mouseout : true,
35607         /**
35608          * @event itemclick
35609          * Fires when a menu item contained in this menu is clicked
35610          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35611          * @param {Roo.EventObject} e
35612          */
35613         itemclick: true
35614     });
35615     if (this.registerMenu) {
35616         Roo.menu.MenuMgr.register(this);
35617     }
35618     
35619     var mis = this.items;
35620     this.items = new Roo.util.MixedCollection();
35621     if(mis){
35622         this.add.apply(this, mis);
35623     }
35624 };
35625
35626 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35627     /**
35628      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35629      */
35630     minWidth : 120,
35631     /**
35632      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35633      * for bottom-right shadow (defaults to "sides")
35634      */
35635     shadow : "sides",
35636     /**
35637      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35638      * this menu (defaults to "tl-tr?")
35639      */
35640     subMenuAlign : "tl-tr?",
35641     /**
35642      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35643      * relative to its element of origin (defaults to "tl-bl?")
35644      */
35645     defaultAlign : "tl-bl?",
35646     /**
35647      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35648      */
35649     allowOtherMenus : false,
35650     /**
35651      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35652      */
35653     registerMenu : true,
35654
35655     hidden:true,
35656
35657     // private
35658     render : function(){
35659         if(this.el){
35660             return;
35661         }
35662         var el = this.el = new Roo.Layer({
35663             cls: "x-menu",
35664             shadow:this.shadow,
35665             constrain: false,
35666             parentEl: this.parentEl || document.body,
35667             zindex:15000
35668         });
35669
35670         this.keyNav = new Roo.menu.MenuNav(this);
35671
35672         if(this.plain){
35673             el.addClass("x-menu-plain");
35674         }
35675         if(this.cls){
35676             el.addClass(this.cls);
35677         }
35678         // generic focus element
35679         this.focusEl = el.createChild({
35680             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35681         });
35682         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35683         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35684         
35685         ul.on("mouseover", this.onMouseOver, this);
35686         ul.on("mouseout", this.onMouseOut, this);
35687         this.items.each(function(item){
35688             if (item.hidden) {
35689                 return;
35690             }
35691             
35692             var li = document.createElement("li");
35693             li.className = "x-menu-list-item";
35694             ul.dom.appendChild(li);
35695             item.render(li, this);
35696         }, this);
35697         this.ul = ul;
35698         this.autoWidth();
35699     },
35700
35701     // private
35702     autoWidth : function(){
35703         var el = this.el, ul = this.ul;
35704         if(!el){
35705             return;
35706         }
35707         var w = this.width;
35708         if(w){
35709             el.setWidth(w);
35710         }else if(Roo.isIE){
35711             el.setWidth(this.minWidth);
35712             var t = el.dom.offsetWidth; // force recalc
35713             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35714         }
35715     },
35716
35717     // private
35718     delayAutoWidth : function(){
35719         if(this.rendered){
35720             if(!this.awTask){
35721                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35722             }
35723             this.awTask.delay(20);
35724         }
35725     },
35726
35727     // private
35728     findTargetItem : function(e){
35729         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35730         if(t && t.menuItemId){
35731             return this.items.get(t.menuItemId);
35732         }
35733     },
35734
35735     // private
35736     onClick : function(e){
35737         Roo.log("menu.onClick");
35738         var t = this.findTargetItem(e);
35739         if(!t){
35740             return;
35741         }
35742         Roo.log(e);
35743         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35744             if(t == this.activeItem && t.shouldDeactivate(e)){
35745                 this.activeItem.deactivate();
35746                 delete this.activeItem;
35747                 return;
35748             }
35749             if(t.canActivate){
35750                 this.setActiveItem(t, true);
35751             }
35752             return;
35753             
35754             
35755         }
35756         
35757         t.onClick(e);
35758         this.fireEvent("click", this, t, e);
35759     },
35760
35761     // private
35762     setActiveItem : function(item, autoExpand){
35763         if(item != this.activeItem){
35764             if(this.activeItem){
35765                 this.activeItem.deactivate();
35766             }
35767             this.activeItem = item;
35768             item.activate(autoExpand);
35769         }else if(autoExpand){
35770             item.expandMenu();
35771         }
35772     },
35773
35774     // private
35775     tryActivate : function(start, step){
35776         var items = this.items;
35777         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35778             var item = items.get(i);
35779             if(!item.disabled && item.canActivate){
35780                 this.setActiveItem(item, false);
35781                 return item;
35782             }
35783         }
35784         return false;
35785     },
35786
35787     // private
35788     onMouseOver : function(e){
35789         var t;
35790         if(t = this.findTargetItem(e)){
35791             if(t.canActivate && !t.disabled){
35792                 this.setActiveItem(t, true);
35793             }
35794         }
35795         this.fireEvent("mouseover", this, e, t);
35796     },
35797
35798     // private
35799     onMouseOut : function(e){
35800         var t;
35801         if(t = this.findTargetItem(e)){
35802             if(t == this.activeItem && t.shouldDeactivate(e)){
35803                 this.activeItem.deactivate();
35804                 delete this.activeItem;
35805             }
35806         }
35807         this.fireEvent("mouseout", this, e, t);
35808     },
35809
35810     /**
35811      * Read-only.  Returns true if the menu is currently displayed, else false.
35812      * @type Boolean
35813      */
35814     isVisible : function(){
35815         return this.el && !this.hidden;
35816     },
35817
35818     /**
35819      * Displays this menu relative to another element
35820      * @param {String/HTMLElement/Roo.Element} element The element to align to
35821      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35822      * the element (defaults to this.defaultAlign)
35823      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35824      */
35825     show : function(el, pos, parentMenu){
35826         this.parentMenu = parentMenu;
35827         if(!this.el){
35828             this.render();
35829         }
35830         this.fireEvent("beforeshow", this);
35831         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35832     },
35833
35834     /**
35835      * Displays this menu at a specific xy position
35836      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35837      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35838      */
35839     showAt : function(xy, parentMenu, /* private: */_e){
35840         this.parentMenu = parentMenu;
35841         if(!this.el){
35842             this.render();
35843         }
35844         if(_e !== false){
35845             this.fireEvent("beforeshow", this);
35846             xy = this.el.adjustForConstraints(xy);
35847         }
35848         this.el.setXY(xy);
35849         this.el.show();
35850         this.hidden = false;
35851         this.focus();
35852         this.fireEvent("show", this);
35853     },
35854
35855     focus : function(){
35856         if(!this.hidden){
35857             this.doFocus.defer(50, this);
35858         }
35859     },
35860
35861     doFocus : function(){
35862         if(!this.hidden){
35863             this.focusEl.focus();
35864         }
35865     },
35866
35867     /**
35868      * Hides this menu and optionally all parent menus
35869      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35870      */
35871     hide : function(deep){
35872         if(this.el && this.isVisible()){
35873             this.fireEvent("beforehide", this);
35874             if(this.activeItem){
35875                 this.activeItem.deactivate();
35876                 this.activeItem = null;
35877             }
35878             this.el.hide();
35879             this.hidden = true;
35880             this.fireEvent("hide", this);
35881         }
35882         if(deep === true && this.parentMenu){
35883             this.parentMenu.hide(true);
35884         }
35885     },
35886
35887     /**
35888      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35889      * Any of the following are valid:
35890      * <ul>
35891      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35892      * <li>An HTMLElement object which will be converted to a menu item</li>
35893      * <li>A menu item config object that will be created as a new menu item</li>
35894      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35895      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35896      * </ul>
35897      * Usage:
35898      * <pre><code>
35899 // Create the menu
35900 var menu = new Roo.menu.Menu();
35901
35902 // Create a menu item to add by reference
35903 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35904
35905 // Add a bunch of items at once using different methods.
35906 // Only the last item added will be returned.
35907 var item = menu.add(
35908     menuItem,                // add existing item by ref
35909     'Dynamic Item',          // new TextItem
35910     '-',                     // new separator
35911     { text: 'Config Item' }  // new item by config
35912 );
35913 </code></pre>
35914      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35915      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35916      */
35917     add : function(){
35918         var a = arguments, l = a.length, item;
35919         for(var i = 0; i < l; i++){
35920             var el = a[i];
35921             if ((typeof(el) == "object") && el.xtype && el.xns) {
35922                 el = Roo.factory(el, Roo.menu);
35923             }
35924             
35925             if(el.render){ // some kind of Item
35926                 item = this.addItem(el);
35927             }else if(typeof el == "string"){ // string
35928                 if(el == "separator" || el == "-"){
35929                     item = this.addSeparator();
35930                 }else{
35931                     item = this.addText(el);
35932                 }
35933             }else if(el.tagName || el.el){ // element
35934                 item = this.addElement(el);
35935             }else if(typeof el == "object"){ // must be menu item config?
35936                 item = this.addMenuItem(el);
35937             }
35938         }
35939         return item;
35940     },
35941
35942     /**
35943      * Returns this menu's underlying {@link Roo.Element} object
35944      * @return {Roo.Element} The element
35945      */
35946     getEl : function(){
35947         if(!this.el){
35948             this.render();
35949         }
35950         return this.el;
35951     },
35952
35953     /**
35954      * Adds a separator bar to the menu
35955      * @return {Roo.menu.Item} The menu item that was added
35956      */
35957     addSeparator : function(){
35958         return this.addItem(new Roo.menu.Separator());
35959     },
35960
35961     /**
35962      * Adds an {@link Roo.Element} object to the menu
35963      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35964      * @return {Roo.menu.Item} The menu item that was added
35965      */
35966     addElement : function(el){
35967         return this.addItem(new Roo.menu.BaseItem(el));
35968     },
35969
35970     /**
35971      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35972      * @param {Roo.menu.Item} item The menu item to add
35973      * @return {Roo.menu.Item} The menu item that was added
35974      */
35975     addItem : function(item){
35976         this.items.add(item);
35977         if(this.ul){
35978             var li = document.createElement("li");
35979             li.className = "x-menu-list-item";
35980             this.ul.dom.appendChild(li);
35981             item.render(li, this);
35982             this.delayAutoWidth();
35983         }
35984         return item;
35985     },
35986
35987     /**
35988      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35989      * @param {Object} config A MenuItem config object
35990      * @return {Roo.menu.Item} The menu item that was added
35991      */
35992     addMenuItem : function(config){
35993         if(!(config instanceof Roo.menu.Item)){
35994             if(typeof config.checked == "boolean"){ // must be check menu item config?
35995                 config = new Roo.menu.CheckItem(config);
35996             }else{
35997                 config = new Roo.menu.Item(config);
35998             }
35999         }
36000         return this.addItem(config);
36001     },
36002
36003     /**
36004      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36005      * @param {String} text The text to display in the menu item
36006      * @return {Roo.menu.Item} The menu item that was added
36007      */
36008     addText : function(text){
36009         return this.addItem(new Roo.menu.TextItem({ text : text }));
36010     },
36011
36012     /**
36013      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36014      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36015      * @param {Roo.menu.Item} item The menu item to add
36016      * @return {Roo.menu.Item} The menu item that was added
36017      */
36018     insert : function(index, item){
36019         this.items.insert(index, item);
36020         if(this.ul){
36021             var li = document.createElement("li");
36022             li.className = "x-menu-list-item";
36023             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36024             item.render(li, this);
36025             this.delayAutoWidth();
36026         }
36027         return item;
36028     },
36029
36030     /**
36031      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36032      * @param {Roo.menu.Item} item The menu item to remove
36033      */
36034     remove : function(item){
36035         this.items.removeKey(item.id);
36036         item.destroy();
36037     },
36038
36039     /**
36040      * Removes and destroys all items in the menu
36041      */
36042     removeAll : function(){
36043         var f;
36044         while(f = this.items.first()){
36045             this.remove(f);
36046         }
36047     }
36048 });
36049
36050 // MenuNav is a private utility class used internally by the Menu
36051 Roo.menu.MenuNav = function(menu){
36052     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36053     this.scope = this.menu = menu;
36054 };
36055
36056 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36057     doRelay : function(e, h){
36058         var k = e.getKey();
36059         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36060             this.menu.tryActivate(0, 1);
36061             return false;
36062         }
36063         return h.call(this.scope || this, e, this.menu);
36064     },
36065
36066     up : function(e, m){
36067         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36068             m.tryActivate(m.items.length-1, -1);
36069         }
36070     },
36071
36072     down : function(e, m){
36073         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36074             m.tryActivate(0, 1);
36075         }
36076     },
36077
36078     right : function(e, m){
36079         if(m.activeItem){
36080             m.activeItem.expandMenu(true);
36081         }
36082     },
36083
36084     left : function(e, m){
36085         m.hide();
36086         if(m.parentMenu && m.parentMenu.activeItem){
36087             m.parentMenu.activeItem.activate();
36088         }
36089     },
36090
36091     enter : function(e, m){
36092         if(m.activeItem){
36093             e.stopPropagation();
36094             m.activeItem.onClick(e);
36095             m.fireEvent("click", this, m.activeItem);
36096             return true;
36097         }
36098     }
36099 });/*
36100  * Based on:
36101  * Ext JS Library 1.1.1
36102  * Copyright(c) 2006-2007, Ext JS, LLC.
36103  *
36104  * Originally Released Under LGPL - original licence link has changed is not relivant.
36105  *
36106  * Fork - LGPL
36107  * <script type="text/javascript">
36108  */
36109  
36110 /**
36111  * @class Roo.menu.MenuMgr
36112  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36113  * @singleton
36114  */
36115 Roo.menu.MenuMgr = function(){
36116    var menus, active, groups = {}, attached = false, lastShow = new Date();
36117
36118    // private - called when first menu is created
36119    function init(){
36120        menus = {};
36121        active = new Roo.util.MixedCollection();
36122        Roo.get(document).addKeyListener(27, function(){
36123            if(active.length > 0){
36124                hideAll();
36125            }
36126        });
36127    }
36128
36129    // private
36130    function hideAll(){
36131        if(active && active.length > 0){
36132            var c = active.clone();
36133            c.each(function(m){
36134                m.hide();
36135            });
36136        }
36137    }
36138
36139    // private
36140    function onHide(m){
36141        active.remove(m);
36142        if(active.length < 1){
36143            Roo.get(document).un("mousedown", onMouseDown);
36144            attached = false;
36145        }
36146    }
36147
36148    // private
36149    function onShow(m){
36150        var last = active.last();
36151        lastShow = new Date();
36152        active.add(m);
36153        if(!attached){
36154            Roo.get(document).on("mousedown", onMouseDown);
36155            attached = true;
36156        }
36157        if(m.parentMenu){
36158           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36159           m.parentMenu.activeChild = m;
36160        }else if(last && last.isVisible()){
36161           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36162        }
36163    }
36164
36165    // private
36166    function onBeforeHide(m){
36167        if(m.activeChild){
36168            m.activeChild.hide();
36169        }
36170        if(m.autoHideTimer){
36171            clearTimeout(m.autoHideTimer);
36172            delete m.autoHideTimer;
36173        }
36174    }
36175
36176    // private
36177    function onBeforeShow(m){
36178        var pm = m.parentMenu;
36179        if(!pm && !m.allowOtherMenus){
36180            hideAll();
36181        }else if(pm && pm.activeChild && active != m){
36182            pm.activeChild.hide();
36183        }
36184    }
36185
36186    // private
36187    function onMouseDown(e){
36188        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36189            hideAll();
36190        }
36191    }
36192
36193    // private
36194    function onBeforeCheck(mi, state){
36195        if(state){
36196            var g = groups[mi.group];
36197            for(var i = 0, l = g.length; i < l; i++){
36198                if(g[i] != mi){
36199                    g[i].setChecked(false);
36200                }
36201            }
36202        }
36203    }
36204
36205    return {
36206
36207        /**
36208         * Hides all menus that are currently visible
36209         */
36210        hideAll : function(){
36211             hideAll();  
36212        },
36213
36214        // private
36215        register : function(menu){
36216            if(!menus){
36217                init();
36218            }
36219            menus[menu.id] = menu;
36220            menu.on("beforehide", onBeforeHide);
36221            menu.on("hide", onHide);
36222            menu.on("beforeshow", onBeforeShow);
36223            menu.on("show", onShow);
36224            var g = menu.group;
36225            if(g && menu.events["checkchange"]){
36226                if(!groups[g]){
36227                    groups[g] = [];
36228                }
36229                groups[g].push(menu);
36230                menu.on("checkchange", onCheck);
36231            }
36232        },
36233
36234         /**
36235          * Returns a {@link Roo.menu.Menu} object
36236          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36237          * be used to generate and return a new Menu instance.
36238          */
36239        get : function(menu){
36240            if(typeof menu == "string"){ // menu id
36241                return menus[menu];
36242            }else if(menu.events){  // menu instance
36243                return menu;
36244            }else if(typeof menu.length == 'number'){ // array of menu items?
36245                return new Roo.menu.Menu({items:menu});
36246            }else{ // otherwise, must be a config
36247                return new Roo.menu.Menu(menu);
36248            }
36249        },
36250
36251        // private
36252        unregister : function(menu){
36253            delete menus[menu.id];
36254            menu.un("beforehide", onBeforeHide);
36255            menu.un("hide", onHide);
36256            menu.un("beforeshow", onBeforeShow);
36257            menu.un("show", onShow);
36258            var g = menu.group;
36259            if(g && menu.events["checkchange"]){
36260                groups[g].remove(menu);
36261                menu.un("checkchange", onCheck);
36262            }
36263        },
36264
36265        // private
36266        registerCheckable : function(menuItem){
36267            var g = menuItem.group;
36268            if(g){
36269                if(!groups[g]){
36270                    groups[g] = [];
36271                }
36272                groups[g].push(menuItem);
36273                menuItem.on("beforecheckchange", onBeforeCheck);
36274            }
36275        },
36276
36277        // private
36278        unregisterCheckable : function(menuItem){
36279            var g = menuItem.group;
36280            if(g){
36281                groups[g].remove(menuItem);
36282                menuItem.un("beforecheckchange", onBeforeCheck);
36283            }
36284        }
36285    };
36286 }();/*
36287  * Based on:
36288  * Ext JS Library 1.1.1
36289  * Copyright(c) 2006-2007, Ext JS, LLC.
36290  *
36291  * Originally Released Under LGPL - original licence link has changed is not relivant.
36292  *
36293  * Fork - LGPL
36294  * <script type="text/javascript">
36295  */
36296  
36297
36298 /**
36299  * @class Roo.menu.BaseItem
36300  * @extends Roo.Component
36301  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36302  * management and base configuration options shared by all menu components.
36303  * @constructor
36304  * Creates a new BaseItem
36305  * @param {Object} config Configuration options
36306  */
36307 Roo.menu.BaseItem = function(config){
36308     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36309
36310     this.addEvents({
36311         /**
36312          * @event click
36313          * Fires when this item is clicked
36314          * @param {Roo.menu.BaseItem} this
36315          * @param {Roo.EventObject} e
36316          */
36317         click: true,
36318         /**
36319          * @event activate
36320          * Fires when this item is activated
36321          * @param {Roo.menu.BaseItem} this
36322          */
36323         activate : true,
36324         /**
36325          * @event deactivate
36326          * Fires when this item is deactivated
36327          * @param {Roo.menu.BaseItem} this
36328          */
36329         deactivate : true
36330     });
36331
36332     if(this.handler){
36333         this.on("click", this.handler, this.scope, true);
36334     }
36335 };
36336
36337 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36338     /**
36339      * @cfg {Function} handler
36340      * A function that will handle the click event of this menu item (defaults to undefined)
36341      */
36342     /**
36343      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36344      */
36345     canActivate : false,
36346     
36347      /**
36348      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36349      */
36350     hidden: false,
36351     
36352     /**
36353      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36354      */
36355     activeClass : "x-menu-item-active",
36356     /**
36357      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36358      */
36359     hideOnClick : true,
36360     /**
36361      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36362      */
36363     hideDelay : 100,
36364
36365     // private
36366     ctype: "Roo.menu.BaseItem",
36367
36368     // private
36369     actionMode : "container",
36370
36371     // private
36372     render : function(container, parentMenu){
36373         this.parentMenu = parentMenu;
36374         Roo.menu.BaseItem.superclass.render.call(this, container);
36375         this.container.menuItemId = this.id;
36376     },
36377
36378     // private
36379     onRender : function(container, position){
36380         this.el = Roo.get(this.el);
36381         container.dom.appendChild(this.el.dom);
36382     },
36383
36384     // private
36385     onClick : function(e){
36386         if(!this.disabled && this.fireEvent("click", this, e) !== false
36387                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36388             this.handleClick(e);
36389         }else{
36390             e.stopEvent();
36391         }
36392     },
36393
36394     // private
36395     activate : function(){
36396         if(this.disabled){
36397             return false;
36398         }
36399         var li = this.container;
36400         li.addClass(this.activeClass);
36401         this.region = li.getRegion().adjust(2, 2, -2, -2);
36402         this.fireEvent("activate", this);
36403         return true;
36404     },
36405
36406     // private
36407     deactivate : function(){
36408         this.container.removeClass(this.activeClass);
36409         this.fireEvent("deactivate", this);
36410     },
36411
36412     // private
36413     shouldDeactivate : function(e){
36414         return !this.region || !this.region.contains(e.getPoint());
36415     },
36416
36417     // private
36418     handleClick : function(e){
36419         if(this.hideOnClick){
36420             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36421         }
36422     },
36423
36424     // private
36425     expandMenu : function(autoActivate){
36426         // do nothing
36427     },
36428
36429     // private
36430     hideMenu : function(){
36431         // do nothing
36432     }
36433 });/*
36434  * Based on:
36435  * Ext JS Library 1.1.1
36436  * Copyright(c) 2006-2007, Ext JS, LLC.
36437  *
36438  * Originally Released Under LGPL - original licence link has changed is not relivant.
36439  *
36440  * Fork - LGPL
36441  * <script type="text/javascript">
36442  */
36443  
36444 /**
36445  * @class Roo.menu.Adapter
36446  * @extends Roo.menu.BaseItem
36447  * 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.
36448  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36449  * @constructor
36450  * Creates a new Adapter
36451  * @param {Object} config Configuration options
36452  */
36453 Roo.menu.Adapter = function(component, config){
36454     Roo.menu.Adapter.superclass.constructor.call(this, config);
36455     this.component = component;
36456 };
36457 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36458     // private
36459     canActivate : true,
36460
36461     // private
36462     onRender : function(container, position){
36463         this.component.render(container);
36464         this.el = this.component.getEl();
36465     },
36466
36467     // private
36468     activate : function(){
36469         if(this.disabled){
36470             return false;
36471         }
36472         this.component.focus();
36473         this.fireEvent("activate", this);
36474         return true;
36475     },
36476
36477     // private
36478     deactivate : function(){
36479         this.fireEvent("deactivate", this);
36480     },
36481
36482     // private
36483     disable : function(){
36484         this.component.disable();
36485         Roo.menu.Adapter.superclass.disable.call(this);
36486     },
36487
36488     // private
36489     enable : function(){
36490         this.component.enable();
36491         Roo.menu.Adapter.superclass.enable.call(this);
36492     }
36493 });/*
36494  * Based on:
36495  * Ext JS Library 1.1.1
36496  * Copyright(c) 2006-2007, Ext JS, LLC.
36497  *
36498  * Originally Released Under LGPL - original licence link has changed is not relivant.
36499  *
36500  * Fork - LGPL
36501  * <script type="text/javascript">
36502  */
36503
36504 /**
36505  * @class Roo.menu.TextItem
36506  * @extends Roo.menu.BaseItem
36507  * Adds a static text string to a menu, usually used as either a heading or group separator.
36508  * Note: old style constructor with text is still supported.
36509  * 
36510  * @constructor
36511  * Creates a new TextItem
36512  * @param {Object} cfg Configuration
36513  */
36514 Roo.menu.TextItem = function(cfg){
36515     if (typeof(cfg) == 'string') {
36516         this.text = cfg;
36517     } else {
36518         Roo.apply(this,cfg);
36519     }
36520     
36521     Roo.menu.TextItem.superclass.constructor.call(this);
36522 };
36523
36524 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36525     /**
36526      * @cfg {Boolean} text Text to show on item.
36527      */
36528     text : '',
36529     
36530     /**
36531      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36532      */
36533     hideOnClick : false,
36534     /**
36535      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36536      */
36537     itemCls : "x-menu-text",
36538
36539     // private
36540     onRender : function(){
36541         var s = document.createElement("span");
36542         s.className = this.itemCls;
36543         s.innerHTML = this.text;
36544         this.el = s;
36545         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36546     }
36547 });/*
36548  * Based on:
36549  * Ext JS Library 1.1.1
36550  * Copyright(c) 2006-2007, Ext JS, LLC.
36551  *
36552  * Originally Released Under LGPL - original licence link has changed is not relivant.
36553  *
36554  * Fork - LGPL
36555  * <script type="text/javascript">
36556  */
36557
36558 /**
36559  * @class Roo.menu.Separator
36560  * @extends Roo.menu.BaseItem
36561  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36562  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36563  * @constructor
36564  * @param {Object} config Configuration options
36565  */
36566 Roo.menu.Separator = function(config){
36567     Roo.menu.Separator.superclass.constructor.call(this, config);
36568 };
36569
36570 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36571     /**
36572      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36573      */
36574     itemCls : "x-menu-sep",
36575     /**
36576      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36577      */
36578     hideOnClick : false,
36579
36580     // private
36581     onRender : function(li){
36582         var s = document.createElement("span");
36583         s.className = this.itemCls;
36584         s.innerHTML = "&#160;";
36585         this.el = s;
36586         li.addClass("x-menu-sep-li");
36587         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36588     }
36589 });/*
36590  * Based on:
36591  * Ext JS Library 1.1.1
36592  * Copyright(c) 2006-2007, Ext JS, LLC.
36593  *
36594  * Originally Released Under LGPL - original licence link has changed is not relivant.
36595  *
36596  * Fork - LGPL
36597  * <script type="text/javascript">
36598  */
36599 /**
36600  * @class Roo.menu.Item
36601  * @extends Roo.menu.BaseItem
36602  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36603  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36604  * activation and click handling.
36605  * @constructor
36606  * Creates a new Item
36607  * @param {Object} config Configuration options
36608  */
36609 Roo.menu.Item = function(config){
36610     Roo.menu.Item.superclass.constructor.call(this, config);
36611     if(this.menu){
36612         this.menu = Roo.menu.MenuMgr.get(this.menu);
36613     }
36614 };
36615 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36616     
36617     /**
36618      * @cfg {String} text
36619      * The text to show on the menu item.
36620      */
36621     text: '',
36622      /**
36623      * @cfg {String} HTML to render in menu
36624      * The text to show on the menu item (HTML version).
36625      */
36626     html: '',
36627     /**
36628      * @cfg {String} icon
36629      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36630      */
36631     icon: undefined,
36632     /**
36633      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36634      */
36635     itemCls : "x-menu-item",
36636     /**
36637      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36638      */
36639     canActivate : true,
36640     /**
36641      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36642      */
36643     showDelay: 200,
36644     // doc'd in BaseItem
36645     hideDelay: 200,
36646
36647     // private
36648     ctype: "Roo.menu.Item",
36649     
36650     // private
36651     onRender : function(container, position){
36652         var el = document.createElement("a");
36653         el.hideFocus = true;
36654         el.unselectable = "on";
36655         el.href = this.href || "#";
36656         if(this.hrefTarget){
36657             el.target = this.hrefTarget;
36658         }
36659         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36660         
36661         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36662         
36663         el.innerHTML = String.format(
36664                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36665                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36666         this.el = el;
36667         Roo.menu.Item.superclass.onRender.call(this, container, position);
36668     },
36669
36670     /**
36671      * Sets the text to display in this menu item
36672      * @param {String} text The text to display
36673      * @param {Boolean} isHTML true to indicate text is pure html.
36674      */
36675     setText : function(text, isHTML){
36676         if (isHTML) {
36677             this.html = text;
36678         } else {
36679             this.text = text;
36680             this.html = '';
36681         }
36682         if(this.rendered){
36683             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36684      
36685             this.el.update(String.format(
36686                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36687                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36688             this.parentMenu.autoWidth();
36689         }
36690     },
36691
36692     // private
36693     handleClick : function(e){
36694         if(!this.href){ // if no link defined, stop the event automatically
36695             e.stopEvent();
36696         }
36697         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36698     },
36699
36700     // private
36701     activate : function(autoExpand){
36702         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36703             this.focus();
36704             if(autoExpand){
36705                 this.expandMenu();
36706             }
36707         }
36708         return true;
36709     },
36710
36711     // private
36712     shouldDeactivate : function(e){
36713         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36714             if(this.menu && this.menu.isVisible()){
36715                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36716             }
36717             return true;
36718         }
36719         return false;
36720     },
36721
36722     // private
36723     deactivate : function(){
36724         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36725         this.hideMenu();
36726     },
36727
36728     // private
36729     expandMenu : function(autoActivate){
36730         if(!this.disabled && this.menu){
36731             clearTimeout(this.hideTimer);
36732             delete this.hideTimer;
36733             if(!this.menu.isVisible() && !this.showTimer){
36734                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36735             }else if (this.menu.isVisible() && autoActivate){
36736                 this.menu.tryActivate(0, 1);
36737             }
36738         }
36739     },
36740
36741     // private
36742     deferExpand : function(autoActivate){
36743         delete this.showTimer;
36744         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36745         if(autoActivate){
36746             this.menu.tryActivate(0, 1);
36747         }
36748     },
36749
36750     // private
36751     hideMenu : function(){
36752         clearTimeout(this.showTimer);
36753         delete this.showTimer;
36754         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36755             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36756         }
36757     },
36758
36759     // private
36760     deferHide : function(){
36761         delete this.hideTimer;
36762         this.menu.hide();
36763     }
36764 });/*
36765  * Based on:
36766  * Ext JS Library 1.1.1
36767  * Copyright(c) 2006-2007, Ext JS, LLC.
36768  *
36769  * Originally Released Under LGPL - original licence link has changed is not relivant.
36770  *
36771  * Fork - LGPL
36772  * <script type="text/javascript">
36773  */
36774  
36775 /**
36776  * @class Roo.menu.CheckItem
36777  * @extends Roo.menu.Item
36778  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36779  * @constructor
36780  * Creates a new CheckItem
36781  * @param {Object} config Configuration options
36782  */
36783 Roo.menu.CheckItem = function(config){
36784     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36785     this.addEvents({
36786         /**
36787          * @event beforecheckchange
36788          * Fires before the checked value is set, providing an opportunity to cancel if needed
36789          * @param {Roo.menu.CheckItem} this
36790          * @param {Boolean} checked The new checked value that will be set
36791          */
36792         "beforecheckchange" : true,
36793         /**
36794          * @event checkchange
36795          * Fires after the checked value has been set
36796          * @param {Roo.menu.CheckItem} this
36797          * @param {Boolean} checked The checked value that was set
36798          */
36799         "checkchange" : true
36800     });
36801     if(this.checkHandler){
36802         this.on('checkchange', this.checkHandler, this.scope);
36803     }
36804 };
36805 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36806     /**
36807      * @cfg {String} group
36808      * All check items with the same group name will automatically be grouped into a single-select
36809      * radio button group (defaults to '')
36810      */
36811     /**
36812      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36813      */
36814     itemCls : "x-menu-item x-menu-check-item",
36815     /**
36816      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36817      */
36818     groupClass : "x-menu-group-item",
36819
36820     /**
36821      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36822      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36823      * initialized with checked = true will be rendered as checked.
36824      */
36825     checked: false,
36826
36827     // private
36828     ctype: "Roo.menu.CheckItem",
36829
36830     // private
36831     onRender : function(c){
36832         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36833         if(this.group){
36834             this.el.addClass(this.groupClass);
36835         }
36836         Roo.menu.MenuMgr.registerCheckable(this);
36837         if(this.checked){
36838             this.checked = false;
36839             this.setChecked(true, true);
36840         }
36841     },
36842
36843     // private
36844     destroy : function(){
36845         if(this.rendered){
36846             Roo.menu.MenuMgr.unregisterCheckable(this);
36847         }
36848         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36849     },
36850
36851     /**
36852      * Set the checked state of this item
36853      * @param {Boolean} checked The new checked value
36854      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36855      */
36856     setChecked : function(state, suppressEvent){
36857         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36858             if(this.container){
36859                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36860             }
36861             this.checked = state;
36862             if(suppressEvent !== true){
36863                 this.fireEvent("checkchange", this, state);
36864             }
36865         }
36866     },
36867
36868     // private
36869     handleClick : function(e){
36870        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36871            this.setChecked(!this.checked);
36872        }
36873        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36874     }
36875 });/*
36876  * Based on:
36877  * Ext JS Library 1.1.1
36878  * Copyright(c) 2006-2007, Ext JS, LLC.
36879  *
36880  * Originally Released Under LGPL - original licence link has changed is not relivant.
36881  *
36882  * Fork - LGPL
36883  * <script type="text/javascript">
36884  */
36885  
36886 /**
36887  * @class Roo.menu.DateItem
36888  * @extends Roo.menu.Adapter
36889  * A menu item that wraps the {@link Roo.DatPicker} component.
36890  * @constructor
36891  * Creates a new DateItem
36892  * @param {Object} config Configuration options
36893  */
36894 Roo.menu.DateItem = function(config){
36895     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36896     /** The Roo.DatePicker object @type Roo.DatePicker */
36897     this.picker = this.component;
36898     this.addEvents({select: true});
36899     
36900     this.picker.on("render", function(picker){
36901         picker.getEl().swallowEvent("click");
36902         picker.container.addClass("x-menu-date-item");
36903     });
36904
36905     this.picker.on("select", this.onSelect, this);
36906 };
36907
36908 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36909     // private
36910     onSelect : function(picker, date){
36911         this.fireEvent("select", this, date, picker);
36912         Roo.menu.DateItem.superclass.handleClick.call(this);
36913     }
36914 });/*
36915  * Based on:
36916  * Ext JS Library 1.1.1
36917  * Copyright(c) 2006-2007, Ext JS, LLC.
36918  *
36919  * Originally Released Under LGPL - original licence link has changed is not relivant.
36920  *
36921  * Fork - LGPL
36922  * <script type="text/javascript">
36923  */
36924  
36925 /**
36926  * @class Roo.menu.ColorItem
36927  * @extends Roo.menu.Adapter
36928  * A menu item that wraps the {@link Roo.ColorPalette} component.
36929  * @constructor
36930  * Creates a new ColorItem
36931  * @param {Object} config Configuration options
36932  */
36933 Roo.menu.ColorItem = function(config){
36934     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36935     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36936     this.palette = this.component;
36937     this.relayEvents(this.palette, ["select"]);
36938     if(this.selectHandler){
36939         this.on('select', this.selectHandler, this.scope);
36940     }
36941 };
36942 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36943  * Based on:
36944  * Ext JS Library 1.1.1
36945  * Copyright(c) 2006-2007, Ext JS, LLC.
36946  *
36947  * Originally Released Under LGPL - original licence link has changed is not relivant.
36948  *
36949  * Fork - LGPL
36950  * <script type="text/javascript">
36951  */
36952  
36953
36954 /**
36955  * @class Roo.menu.DateMenu
36956  * @extends Roo.menu.Menu
36957  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36958  * @constructor
36959  * Creates a new DateMenu
36960  * @param {Object} config Configuration options
36961  */
36962 Roo.menu.DateMenu = function(config){
36963     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36964     this.plain = true;
36965     var di = new Roo.menu.DateItem(config);
36966     this.add(di);
36967     /**
36968      * The {@link Roo.DatePicker} instance for this DateMenu
36969      * @type DatePicker
36970      */
36971     this.picker = di.picker;
36972     /**
36973      * @event select
36974      * @param {DatePicker} picker
36975      * @param {Date} date
36976      */
36977     this.relayEvents(di, ["select"]);
36978     this.on('beforeshow', function(){
36979         if(this.picker){
36980             this.picker.hideMonthPicker(false);
36981         }
36982     }, this);
36983 };
36984 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36985     cls:'x-date-menu'
36986 });/*
36987  * Based on:
36988  * Ext JS Library 1.1.1
36989  * Copyright(c) 2006-2007, Ext JS, LLC.
36990  *
36991  * Originally Released Under LGPL - original licence link has changed is not relivant.
36992  *
36993  * Fork - LGPL
36994  * <script type="text/javascript">
36995  */
36996  
36997
36998 /**
36999  * @class Roo.menu.ColorMenu
37000  * @extends Roo.menu.Menu
37001  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37002  * @constructor
37003  * Creates a new ColorMenu
37004  * @param {Object} config Configuration options
37005  */
37006 Roo.menu.ColorMenu = function(config){
37007     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37008     this.plain = true;
37009     var ci = new Roo.menu.ColorItem(config);
37010     this.add(ci);
37011     /**
37012      * The {@link Roo.ColorPalette} instance for this ColorMenu
37013      * @type ColorPalette
37014      */
37015     this.palette = ci.palette;
37016     /**
37017      * @event select
37018      * @param {ColorPalette} palette
37019      * @param {String} color
37020      */
37021     this.relayEvents(ci, ["select"]);
37022 };
37023 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37024  * Based on:
37025  * Ext JS Library 1.1.1
37026  * Copyright(c) 2006-2007, Ext JS, LLC.
37027  *
37028  * Originally Released Under LGPL - original licence link has changed is not relivant.
37029  *
37030  * Fork - LGPL
37031  * <script type="text/javascript">
37032  */
37033  
37034 /**
37035  * @class Roo.form.Field
37036  * @extends Roo.BoxComponent
37037  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37038  * @constructor
37039  * Creates a new Field
37040  * @param {Object} config Configuration options
37041  */
37042 Roo.form.Field = function(config){
37043     Roo.form.Field.superclass.constructor.call(this, config);
37044 };
37045
37046 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37047     /**
37048      * @cfg {String} fieldLabel Label to use when rendering a form.
37049      */
37050        /**
37051      * @cfg {String} qtip Mouse over tip
37052      */
37053      
37054     /**
37055      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37056      */
37057     invalidClass : "x-form-invalid",
37058     /**
37059      * @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")
37060      */
37061     invalidText : "The value in this field is invalid",
37062     /**
37063      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37064      */
37065     focusClass : "x-form-focus",
37066     /**
37067      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37068       automatic validation (defaults to "keyup").
37069      */
37070     validationEvent : "keyup",
37071     /**
37072      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37073      */
37074     validateOnBlur : true,
37075     /**
37076      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37077      */
37078     validationDelay : 250,
37079     /**
37080      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37081      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37082      */
37083     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37084     /**
37085      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37086      */
37087     fieldClass : "x-form-field",
37088     /**
37089      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37090      *<pre>
37091 Value         Description
37092 -----------   ----------------------------------------------------------------------
37093 qtip          Display a quick tip when the user hovers over the field
37094 title         Display a default browser title attribute popup
37095 under         Add a block div beneath the field containing the error text
37096 side          Add an error icon to the right of the field with a popup on hover
37097 [element id]  Add the error text directly to the innerHTML of the specified element
37098 </pre>
37099      */
37100     msgTarget : 'qtip',
37101     /**
37102      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37103      */
37104     msgFx : 'normal',
37105
37106     /**
37107      * @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.
37108      */
37109     readOnly : false,
37110
37111     /**
37112      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37113      */
37114     disabled : false,
37115
37116     /**
37117      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37118      */
37119     inputType : undefined,
37120     
37121     /**
37122      * @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).
37123          */
37124         tabIndex : undefined,
37125         
37126     // private
37127     isFormField : true,
37128
37129     // private
37130     hasFocus : false,
37131     /**
37132      * @property {Roo.Element} fieldEl
37133      * Element Containing the rendered Field (with label etc.)
37134      */
37135     /**
37136      * @cfg {Mixed} value A value to initialize this field with.
37137      */
37138     value : undefined,
37139
37140     /**
37141      * @cfg {String} name The field's HTML name attribute.
37142      */
37143     /**
37144      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37145      */
37146
37147         // private ??
37148         initComponent : function(){
37149         Roo.form.Field.superclass.initComponent.call(this);
37150         this.addEvents({
37151             /**
37152              * @event focus
37153              * Fires when this field receives input focus.
37154              * @param {Roo.form.Field} this
37155              */
37156             focus : true,
37157             /**
37158              * @event blur
37159              * Fires when this field loses input focus.
37160              * @param {Roo.form.Field} this
37161              */
37162             blur : true,
37163             /**
37164              * @event specialkey
37165              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37166              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37167              * @param {Roo.form.Field} this
37168              * @param {Roo.EventObject} e The event object
37169              */
37170             specialkey : true,
37171             /**
37172              * @event change
37173              * Fires just before the field blurs if the field value has changed.
37174              * @param {Roo.form.Field} this
37175              * @param {Mixed} newValue The new value
37176              * @param {Mixed} oldValue The original value
37177              */
37178             change : true,
37179             /**
37180              * @event invalid
37181              * Fires after the field has been marked as invalid.
37182              * @param {Roo.form.Field} this
37183              * @param {String} msg The validation message
37184              */
37185             invalid : true,
37186             /**
37187              * @event valid
37188              * Fires after the field has been validated with no errors.
37189              * @param {Roo.form.Field} this
37190              */
37191             valid : true,
37192              /**
37193              * @event keyup
37194              * Fires after the key up
37195              * @param {Roo.form.Field} this
37196              * @param {Roo.EventObject}  e The event Object
37197              */
37198             keyup : true
37199         });
37200     },
37201
37202     /**
37203      * Returns the name attribute of the field if available
37204      * @return {String} name The field name
37205      */
37206     getName: function(){
37207          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37208     },
37209
37210     // private
37211     onRender : function(ct, position){
37212         Roo.form.Field.superclass.onRender.call(this, ct, position);
37213         if(!this.el){
37214             var cfg = this.getAutoCreate();
37215             if(!cfg.name){
37216                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37217             }
37218             if (!cfg.name.length) {
37219                 delete cfg.name;
37220             }
37221             if(this.inputType){
37222                 cfg.type = this.inputType;
37223             }
37224             this.el = ct.createChild(cfg, position);
37225         }
37226         var type = this.el.dom.type;
37227         if(type){
37228             if(type == 'password'){
37229                 type = 'text';
37230             }
37231             this.el.addClass('x-form-'+type);
37232         }
37233         if(this.readOnly){
37234             this.el.dom.readOnly = true;
37235         }
37236         if(this.tabIndex !== undefined){
37237             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37238         }
37239
37240         this.el.addClass([this.fieldClass, this.cls]);
37241         this.initValue();
37242     },
37243
37244     /**
37245      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37246      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37247      * @return {Roo.form.Field} this
37248      */
37249     applyTo : function(target){
37250         this.allowDomMove = false;
37251         this.el = Roo.get(target);
37252         this.render(this.el.dom.parentNode);
37253         return this;
37254     },
37255
37256     // private
37257     initValue : function(){
37258         if(this.value !== undefined){
37259             this.setValue(this.value);
37260         }else if(this.el.dom.value.length > 0){
37261             this.setValue(this.el.dom.value);
37262         }
37263     },
37264
37265     /**
37266      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37267      */
37268     isDirty : function() {
37269         if(this.disabled) {
37270             return false;
37271         }
37272         return String(this.getValue()) !== String(this.originalValue);
37273     },
37274
37275     // private
37276     afterRender : function(){
37277         Roo.form.Field.superclass.afterRender.call(this);
37278         this.initEvents();
37279     },
37280
37281     // private
37282     fireKey : function(e){
37283         //Roo.log('field ' + e.getKey());
37284         if(e.isNavKeyPress()){
37285             this.fireEvent("specialkey", this, e);
37286         }
37287     },
37288
37289     /**
37290      * Resets the current field value to the originally loaded value and clears any validation messages
37291      */
37292     reset : function(){
37293         this.setValue(this.resetValue);
37294         this.clearInvalid();
37295     },
37296
37297     // private
37298     initEvents : function(){
37299         // safari killled keypress - so keydown is now used..
37300         this.el.on("keydown" , this.fireKey,  this);
37301         this.el.on("focus", this.onFocus,  this);
37302         this.el.on("blur", this.onBlur,  this);
37303         this.el.relayEvent('keyup', this);
37304
37305         // reference to original value for reset
37306         this.originalValue = this.getValue();
37307         this.resetValue =  this.getValue();
37308     },
37309
37310     // private
37311     onFocus : function(){
37312         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37313             this.el.addClass(this.focusClass);
37314         }
37315         if(!this.hasFocus){
37316             this.hasFocus = true;
37317             this.startValue = this.getValue();
37318             this.fireEvent("focus", this);
37319         }
37320     },
37321
37322     beforeBlur : Roo.emptyFn,
37323
37324     // private
37325     onBlur : function(){
37326         this.beforeBlur();
37327         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37328             this.el.removeClass(this.focusClass);
37329         }
37330         this.hasFocus = false;
37331         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37332             this.validate();
37333         }
37334         var v = this.getValue();
37335         if(String(v) !== String(this.startValue)){
37336             this.fireEvent('change', this, v, this.startValue);
37337         }
37338         this.fireEvent("blur", this);
37339     },
37340
37341     /**
37342      * Returns whether or not the field value is currently valid
37343      * @param {Boolean} preventMark True to disable marking the field invalid
37344      * @return {Boolean} True if the value is valid, else false
37345      */
37346     isValid : function(preventMark){
37347         if(this.disabled){
37348             return true;
37349         }
37350         var restore = this.preventMark;
37351         this.preventMark = preventMark === true;
37352         var v = this.validateValue(this.processValue(this.getRawValue()));
37353         this.preventMark = restore;
37354         return v;
37355     },
37356
37357     /**
37358      * Validates the field value
37359      * @return {Boolean} True if the value is valid, else false
37360      */
37361     validate : function(){
37362         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37363             this.clearInvalid();
37364             return true;
37365         }
37366         return false;
37367     },
37368
37369     processValue : function(value){
37370         return value;
37371     },
37372
37373     // private
37374     // Subclasses should provide the validation implementation by overriding this
37375     validateValue : function(value){
37376         return true;
37377     },
37378
37379     /**
37380      * Mark this field as invalid
37381      * @param {String} msg The validation message
37382      */
37383     markInvalid : function(msg){
37384         if(!this.rendered || this.preventMark){ // not rendered
37385             return;
37386         }
37387         
37388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37389         
37390         obj.el.addClass(this.invalidClass);
37391         msg = msg || this.invalidText;
37392         switch(this.msgTarget){
37393             case 'qtip':
37394                 obj.el.dom.qtip = msg;
37395                 obj.el.dom.qclass = 'x-form-invalid-tip';
37396                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37397                     Roo.QuickTips.enable();
37398                 }
37399                 break;
37400             case 'title':
37401                 this.el.dom.title = msg;
37402                 break;
37403             case 'under':
37404                 if(!this.errorEl){
37405                     var elp = this.el.findParent('.x-form-element', 5, true);
37406                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37407                     this.errorEl.setWidth(elp.getWidth(true)-20);
37408                 }
37409                 this.errorEl.update(msg);
37410                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37411                 break;
37412             case 'side':
37413                 if(!this.errorIcon){
37414                     var elp = this.el.findParent('.x-form-element', 5, true);
37415                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37416                 }
37417                 this.alignErrorIcon();
37418                 this.errorIcon.dom.qtip = msg;
37419                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37420                 this.errorIcon.show();
37421                 this.on('resize', this.alignErrorIcon, this);
37422                 break;
37423             default:
37424                 var t = Roo.getDom(this.msgTarget);
37425                 t.innerHTML = msg;
37426                 t.style.display = this.msgDisplay;
37427                 break;
37428         }
37429         this.fireEvent('invalid', this, msg);
37430     },
37431
37432     // private
37433     alignErrorIcon : function(){
37434         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37435     },
37436
37437     /**
37438      * Clear any invalid styles/messages for this field
37439      */
37440     clearInvalid : function(){
37441         if(!this.rendered || this.preventMark){ // not rendered
37442             return;
37443         }
37444         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37445         
37446         obj.el.removeClass(this.invalidClass);
37447         switch(this.msgTarget){
37448             case 'qtip':
37449                 obj.el.dom.qtip = '';
37450                 break;
37451             case 'title':
37452                 this.el.dom.title = '';
37453                 break;
37454             case 'under':
37455                 if(this.errorEl){
37456                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37457                 }
37458                 break;
37459             case 'side':
37460                 if(this.errorIcon){
37461                     this.errorIcon.dom.qtip = '';
37462                     this.errorIcon.hide();
37463                     this.un('resize', this.alignErrorIcon, this);
37464                 }
37465                 break;
37466             default:
37467                 var t = Roo.getDom(this.msgTarget);
37468                 t.innerHTML = '';
37469                 t.style.display = 'none';
37470                 break;
37471         }
37472         this.fireEvent('valid', this);
37473     },
37474
37475     /**
37476      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37477      * @return {Mixed} value The field value
37478      */
37479     getRawValue : function(){
37480         var v = this.el.getValue();
37481         
37482         return v;
37483     },
37484
37485     /**
37486      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37487      * @return {Mixed} value The field value
37488      */
37489     getValue : function(){
37490         var v = this.el.getValue();
37491          
37492         return v;
37493     },
37494
37495     /**
37496      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37497      * @param {Mixed} value The value to set
37498      */
37499     setRawValue : function(v){
37500         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37501     },
37502
37503     /**
37504      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37505      * @param {Mixed} value The value to set
37506      */
37507     setValue : function(v){
37508         this.value = v;
37509         if(this.rendered){
37510             this.el.dom.value = (v === null || v === undefined ? '' : v);
37511              this.validate();
37512         }
37513     },
37514
37515     adjustSize : function(w, h){
37516         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37517         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37518         return s;
37519     },
37520
37521     adjustWidth : function(tag, w){
37522         tag = tag.toLowerCase();
37523         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37524             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37525                 if(tag == 'input'){
37526                     return w + 2;
37527                 }
37528                 if(tag == 'textarea'){
37529                     return w-2;
37530                 }
37531             }else if(Roo.isOpera){
37532                 if(tag == 'input'){
37533                     return w + 2;
37534                 }
37535                 if(tag == 'textarea'){
37536                     return w-2;
37537                 }
37538             }
37539         }
37540         return w;
37541     }
37542 });
37543
37544
37545 // anything other than normal should be considered experimental
37546 Roo.form.Field.msgFx = {
37547     normal : {
37548         show: function(msgEl, f){
37549             msgEl.setDisplayed('block');
37550         },
37551
37552         hide : function(msgEl, f){
37553             msgEl.setDisplayed(false).update('');
37554         }
37555     },
37556
37557     slide : {
37558         show: function(msgEl, f){
37559             msgEl.slideIn('t', {stopFx:true});
37560         },
37561
37562         hide : function(msgEl, f){
37563             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37564         }
37565     },
37566
37567     slideRight : {
37568         show: function(msgEl, f){
37569             msgEl.fixDisplay();
37570             msgEl.alignTo(f.el, 'tl-tr');
37571             msgEl.slideIn('l', {stopFx:true});
37572         },
37573
37574         hide : function(msgEl, f){
37575             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37576         }
37577     }
37578 };/*
37579  * Based on:
37580  * Ext JS Library 1.1.1
37581  * Copyright(c) 2006-2007, Ext JS, LLC.
37582  *
37583  * Originally Released Under LGPL - original licence link has changed is not relivant.
37584  *
37585  * Fork - LGPL
37586  * <script type="text/javascript">
37587  */
37588  
37589
37590 /**
37591  * @class Roo.form.TextField
37592  * @extends Roo.form.Field
37593  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37594  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37595  * @constructor
37596  * Creates a new TextField
37597  * @param {Object} config Configuration options
37598  */
37599 Roo.form.TextField = function(config){
37600     Roo.form.TextField.superclass.constructor.call(this, config);
37601     this.addEvents({
37602         /**
37603          * @event autosize
37604          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37605          * according to the default logic, but this event provides a hook for the developer to apply additional
37606          * logic at runtime to resize the field if needed.
37607              * @param {Roo.form.Field} this This text field
37608              * @param {Number} width The new field width
37609              */
37610         autosize : true
37611     });
37612 };
37613
37614 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37615     /**
37616      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37617      */
37618     grow : false,
37619     /**
37620      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37621      */
37622     growMin : 30,
37623     /**
37624      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37625      */
37626     growMax : 800,
37627     /**
37628      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37629      */
37630     vtype : null,
37631     /**
37632      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37633      */
37634     maskRe : null,
37635     /**
37636      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37637      */
37638     disableKeyFilter : false,
37639     /**
37640      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37641      */
37642     allowBlank : true,
37643     /**
37644      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37645      */
37646     minLength : 0,
37647     /**
37648      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37649      */
37650     maxLength : Number.MAX_VALUE,
37651     /**
37652      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37653      */
37654     minLengthText : "The minimum length for this field is {0}",
37655     /**
37656      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37657      */
37658     maxLengthText : "The maximum length for this field is {0}",
37659     /**
37660      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37661      */
37662     selectOnFocus : false,
37663     /**
37664      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37665      */
37666     blankText : "This field is required",
37667     /**
37668      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37669      * If available, this function will be called only after the basic validators all return true, and will be passed the
37670      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37671      */
37672     validator : null,
37673     /**
37674      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37675      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37676      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37677      */
37678     regex : null,
37679     /**
37680      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37681      */
37682     regexText : "",
37683     /**
37684      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37685      */
37686     emptyText : null,
37687    
37688
37689     // private
37690     initEvents : function()
37691     {
37692         if (this.emptyText) {
37693             this.el.attr('placeholder', this.emptyText);
37694         }
37695         
37696         Roo.form.TextField.superclass.initEvents.call(this);
37697         if(this.validationEvent == 'keyup'){
37698             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37699             this.el.on('keyup', this.filterValidation, this);
37700         }
37701         else if(this.validationEvent !== false){
37702             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37703         }
37704         
37705         if(this.selectOnFocus){
37706             this.on("focus", this.preFocus, this);
37707             
37708         }
37709         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37710             this.el.on("keypress", this.filterKeys, this);
37711         }
37712         if(this.grow){
37713             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37714             this.el.on("click", this.autoSize,  this);
37715         }
37716         if(this.el.is('input[type=password]') && Roo.isSafari){
37717             this.el.on('keydown', this.SafariOnKeyDown, this);
37718         }
37719     },
37720
37721     processValue : function(value){
37722         if(this.stripCharsRe){
37723             var newValue = value.replace(this.stripCharsRe, '');
37724             if(newValue !== value){
37725                 this.setRawValue(newValue);
37726                 return newValue;
37727             }
37728         }
37729         return value;
37730     },
37731
37732     filterValidation : function(e){
37733         if(!e.isNavKeyPress()){
37734             this.validationTask.delay(this.validationDelay);
37735         }
37736     },
37737
37738     // private
37739     onKeyUp : function(e){
37740         if(!e.isNavKeyPress()){
37741             this.autoSize();
37742         }
37743     },
37744
37745     /**
37746      * Resets the current field value to the originally-loaded value and clears any validation messages.
37747      *  
37748      */
37749     reset : function(){
37750         Roo.form.TextField.superclass.reset.call(this);
37751        
37752     },
37753
37754     
37755     // private
37756     preFocus : function(){
37757         
37758         if(this.selectOnFocus){
37759             this.el.dom.select();
37760         }
37761     },
37762
37763     
37764     // private
37765     filterKeys : function(e){
37766         var k = e.getKey();
37767         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37768             return;
37769         }
37770         var c = e.getCharCode(), cc = String.fromCharCode(c);
37771         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37772             return;
37773         }
37774         if(!this.maskRe.test(cc)){
37775             e.stopEvent();
37776         }
37777     },
37778
37779     setValue : function(v){
37780         
37781         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37782         
37783         this.autoSize();
37784     },
37785
37786     /**
37787      * Validates a value according to the field's validation rules and marks the field as invalid
37788      * if the validation fails
37789      * @param {Mixed} value The value to validate
37790      * @return {Boolean} True if the value is valid, else false
37791      */
37792     validateValue : function(value){
37793         if(value.length < 1)  { // if it's blank
37794              if(this.allowBlank){
37795                 this.clearInvalid();
37796                 return true;
37797              }else{
37798                 this.markInvalid(this.blankText);
37799                 return false;
37800              }
37801         }
37802         if(value.length < this.minLength){
37803             this.markInvalid(String.format(this.minLengthText, this.minLength));
37804             return false;
37805         }
37806         if(value.length > this.maxLength){
37807             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37808             return false;
37809         }
37810         if(this.vtype){
37811             var vt = Roo.form.VTypes;
37812             if(!vt[this.vtype](value, this)){
37813                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37814                 return false;
37815             }
37816         }
37817         if(typeof this.validator == "function"){
37818             var msg = this.validator(value);
37819             if(msg !== true){
37820                 this.markInvalid(msg);
37821                 return false;
37822             }
37823         }
37824         if(this.regex && !this.regex.test(value)){
37825             this.markInvalid(this.regexText);
37826             return false;
37827         }
37828         return true;
37829     },
37830
37831     /**
37832      * Selects text in this field
37833      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37834      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37835      */
37836     selectText : function(start, end){
37837         var v = this.getRawValue();
37838         if(v.length > 0){
37839             start = start === undefined ? 0 : start;
37840             end = end === undefined ? v.length : end;
37841             var d = this.el.dom;
37842             if(d.setSelectionRange){
37843                 d.setSelectionRange(start, end);
37844             }else if(d.createTextRange){
37845                 var range = d.createTextRange();
37846                 range.moveStart("character", start);
37847                 range.moveEnd("character", v.length-end);
37848                 range.select();
37849             }
37850         }
37851     },
37852
37853     /**
37854      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37855      * This only takes effect if grow = true, and fires the autosize event.
37856      */
37857     autoSize : function(){
37858         if(!this.grow || !this.rendered){
37859             return;
37860         }
37861         if(!this.metrics){
37862             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37863         }
37864         var el = this.el;
37865         var v = el.dom.value;
37866         var d = document.createElement('div');
37867         d.appendChild(document.createTextNode(v));
37868         v = d.innerHTML;
37869         d = null;
37870         v += "&#160;";
37871         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37872         this.el.setWidth(w);
37873         this.fireEvent("autosize", this, w);
37874     },
37875     
37876     // private
37877     SafariOnKeyDown : function(event)
37878     {
37879         // this is a workaround for a password hang bug on chrome/ webkit.
37880         
37881         var isSelectAll = false;
37882         
37883         if(this.el.dom.selectionEnd > 0){
37884             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37885         }
37886         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37887             event.preventDefault();
37888             this.setValue('');
37889             return;
37890         }
37891         
37892         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37893             
37894             event.preventDefault();
37895             // this is very hacky as keydown always get's upper case.
37896             
37897             var cc = String.fromCharCode(event.getCharCode());
37898             
37899             
37900             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37901             
37902         }
37903         
37904         
37905     }
37906 });/*
37907  * Based on:
37908  * Ext JS Library 1.1.1
37909  * Copyright(c) 2006-2007, Ext JS, LLC.
37910  *
37911  * Originally Released Under LGPL - original licence link has changed is not relivant.
37912  *
37913  * Fork - LGPL
37914  * <script type="text/javascript">
37915  */
37916  
37917 /**
37918  * @class Roo.form.Hidden
37919  * @extends Roo.form.TextField
37920  * Simple Hidden element used on forms 
37921  * 
37922  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37923  * 
37924  * @constructor
37925  * Creates a new Hidden form element.
37926  * @param {Object} config Configuration options
37927  */
37928
37929
37930
37931 // easy hidden field...
37932 Roo.form.Hidden = function(config){
37933     Roo.form.Hidden.superclass.constructor.call(this, config);
37934 };
37935   
37936 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37937     fieldLabel:      '',
37938     inputType:      'hidden',
37939     width:          50,
37940     allowBlank:     true,
37941     labelSeparator: '',
37942     hidden:         true,
37943     itemCls :       'x-form-item-display-none'
37944
37945
37946 });
37947
37948
37949 /*
37950  * Based on:
37951  * Ext JS Library 1.1.1
37952  * Copyright(c) 2006-2007, Ext JS, LLC.
37953  *
37954  * Originally Released Under LGPL - original licence link has changed is not relivant.
37955  *
37956  * Fork - LGPL
37957  * <script type="text/javascript">
37958  */
37959  
37960 /**
37961  * @class Roo.form.TriggerField
37962  * @extends Roo.form.TextField
37963  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37964  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37965  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37966  * for which you can provide a custom implementation.  For example:
37967  * <pre><code>
37968 var trigger = new Roo.form.TriggerField();
37969 trigger.onTriggerClick = myTriggerFn;
37970 trigger.applyTo('my-field');
37971 </code></pre>
37972  *
37973  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37974  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37975  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37976  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37977  * @constructor
37978  * Create a new TriggerField.
37979  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37980  * to the base TextField)
37981  */
37982 Roo.form.TriggerField = function(config){
37983     this.mimicing = false;
37984     Roo.form.TriggerField.superclass.constructor.call(this, config);
37985 };
37986
37987 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37988     /**
37989      * @cfg {String} triggerClass A CSS class to apply to the trigger
37990      */
37991     /**
37992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37993      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37994      */
37995     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
37996     /**
37997      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37998      */
37999     hideTrigger:false,
38000
38001     /** @cfg {Boolean} grow @hide */
38002     /** @cfg {Number} growMin @hide */
38003     /** @cfg {Number} growMax @hide */
38004
38005     /**
38006      * @hide 
38007      * @method
38008      */
38009     autoSize: Roo.emptyFn,
38010     // private
38011     monitorTab : true,
38012     // private
38013     deferHeight : true,
38014
38015     
38016     actionMode : 'wrap',
38017     // private
38018     onResize : function(w, h){
38019         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38020         if(typeof w == 'number'){
38021             var x = w - this.trigger.getWidth();
38022             this.el.setWidth(this.adjustWidth('input', x));
38023             this.trigger.setStyle('left', x+'px');
38024         }
38025     },
38026
38027     // private
38028     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38029
38030     // private
38031     getResizeEl : function(){
38032         return this.wrap;
38033     },
38034
38035     // private
38036     getPositionEl : function(){
38037         return this.wrap;
38038     },
38039
38040     // private
38041     alignErrorIcon : function(){
38042         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38043     },
38044
38045     // private
38046     onRender : function(ct, position){
38047         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38048         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38049         this.trigger = this.wrap.createChild(this.triggerConfig ||
38050                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38051         if(this.hideTrigger){
38052             this.trigger.setDisplayed(false);
38053         }
38054         this.initTrigger();
38055         if(!this.width){
38056             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38057         }
38058     },
38059
38060     // private
38061     initTrigger : function(){
38062         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38063         this.trigger.addClassOnOver('x-form-trigger-over');
38064         this.trigger.addClassOnClick('x-form-trigger-click');
38065     },
38066
38067     // private
38068     onDestroy : function(){
38069         if(this.trigger){
38070             this.trigger.removeAllListeners();
38071             this.trigger.remove();
38072         }
38073         if(this.wrap){
38074             this.wrap.remove();
38075         }
38076         Roo.form.TriggerField.superclass.onDestroy.call(this);
38077     },
38078
38079     // private
38080     onFocus : function(){
38081         Roo.form.TriggerField.superclass.onFocus.call(this);
38082         if(!this.mimicing){
38083             this.wrap.addClass('x-trigger-wrap-focus');
38084             this.mimicing = true;
38085             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38086             if(this.monitorTab){
38087                 this.el.on("keydown", this.checkTab, this);
38088             }
38089         }
38090     },
38091
38092     // private
38093     checkTab : function(e){
38094         if(e.getKey() == e.TAB){
38095             this.triggerBlur();
38096         }
38097     },
38098
38099     // private
38100     onBlur : function(){
38101         // do nothing
38102     },
38103
38104     // private
38105     mimicBlur : function(e, t){
38106         if(!this.wrap.contains(t) && this.validateBlur()){
38107             this.triggerBlur();
38108         }
38109     },
38110
38111     // private
38112     triggerBlur : function(){
38113         this.mimicing = false;
38114         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38115         if(this.monitorTab){
38116             this.el.un("keydown", this.checkTab, this);
38117         }
38118         this.wrap.removeClass('x-trigger-wrap-focus');
38119         Roo.form.TriggerField.superclass.onBlur.call(this);
38120     },
38121
38122     // private
38123     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38124     validateBlur : function(e, t){
38125         return true;
38126     },
38127
38128     // private
38129     onDisable : function(){
38130         Roo.form.TriggerField.superclass.onDisable.call(this);
38131         if(this.wrap){
38132             this.wrap.addClass('x-item-disabled');
38133         }
38134     },
38135
38136     // private
38137     onEnable : function(){
38138         Roo.form.TriggerField.superclass.onEnable.call(this);
38139         if(this.wrap){
38140             this.wrap.removeClass('x-item-disabled');
38141         }
38142     },
38143
38144     // private
38145     onShow : function(){
38146         var ae = this.getActionEl();
38147         
38148         if(ae){
38149             ae.dom.style.display = '';
38150             ae.dom.style.visibility = 'visible';
38151         }
38152     },
38153
38154     // private
38155     
38156     onHide : function(){
38157         var ae = this.getActionEl();
38158         ae.dom.style.display = 'none';
38159     },
38160
38161     /**
38162      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38163      * by an implementing function.
38164      * @method
38165      * @param {EventObject} e
38166      */
38167     onTriggerClick : Roo.emptyFn
38168 });
38169
38170 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38171 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38172 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38173 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38174     initComponent : function(){
38175         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38176
38177         this.triggerConfig = {
38178             tag:'span', cls:'x-form-twin-triggers', cn:[
38179             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38180             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38181         ]};
38182     },
38183
38184     getTrigger : function(index){
38185         return this.triggers[index];
38186     },
38187
38188     initTrigger : function(){
38189         var ts = this.trigger.select('.x-form-trigger', true);
38190         this.wrap.setStyle('overflow', 'hidden');
38191         var triggerField = this;
38192         ts.each(function(t, all, index){
38193             t.hide = function(){
38194                 var w = triggerField.wrap.getWidth();
38195                 this.dom.style.display = 'none';
38196                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38197             };
38198             t.show = function(){
38199                 var w = triggerField.wrap.getWidth();
38200                 this.dom.style.display = '';
38201                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38202             };
38203             var triggerIndex = 'Trigger'+(index+1);
38204
38205             if(this['hide'+triggerIndex]){
38206                 t.dom.style.display = 'none';
38207             }
38208             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38209             t.addClassOnOver('x-form-trigger-over');
38210             t.addClassOnClick('x-form-trigger-click');
38211         }, this);
38212         this.triggers = ts.elements;
38213     },
38214
38215     onTrigger1Click : Roo.emptyFn,
38216     onTrigger2Click : Roo.emptyFn
38217 });/*
38218  * Based on:
38219  * Ext JS Library 1.1.1
38220  * Copyright(c) 2006-2007, Ext JS, LLC.
38221  *
38222  * Originally Released Under LGPL - original licence link has changed is not relivant.
38223  *
38224  * Fork - LGPL
38225  * <script type="text/javascript">
38226  */
38227  
38228 /**
38229  * @class Roo.form.TextArea
38230  * @extends Roo.form.TextField
38231  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38232  * support for auto-sizing.
38233  * @constructor
38234  * Creates a new TextArea
38235  * @param {Object} config Configuration options
38236  */
38237 Roo.form.TextArea = function(config){
38238     Roo.form.TextArea.superclass.constructor.call(this, config);
38239     // these are provided exchanges for backwards compat
38240     // minHeight/maxHeight were replaced by growMin/growMax to be
38241     // compatible with TextField growing config values
38242     if(this.minHeight !== undefined){
38243         this.growMin = this.minHeight;
38244     }
38245     if(this.maxHeight !== undefined){
38246         this.growMax = this.maxHeight;
38247     }
38248 };
38249
38250 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38251     /**
38252      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38253      */
38254     growMin : 60,
38255     /**
38256      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38257      */
38258     growMax: 1000,
38259     /**
38260      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38261      * in the field (equivalent to setting overflow: hidden, defaults to false)
38262      */
38263     preventScrollbars: false,
38264     /**
38265      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38266      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38267      */
38268
38269     // private
38270     onRender : function(ct, position){
38271         if(!this.el){
38272             this.defaultAutoCreate = {
38273                 tag: "textarea",
38274                 style:"width:300px;height:60px;",
38275                 autocomplete: "new-password"
38276             };
38277         }
38278         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38279         if(this.grow){
38280             this.textSizeEl = Roo.DomHelper.append(document.body, {
38281                 tag: "pre", cls: "x-form-grow-sizer"
38282             });
38283             if(this.preventScrollbars){
38284                 this.el.setStyle("overflow", "hidden");
38285             }
38286             this.el.setHeight(this.growMin);
38287         }
38288     },
38289
38290     onDestroy : function(){
38291         if(this.textSizeEl){
38292             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38293         }
38294         Roo.form.TextArea.superclass.onDestroy.call(this);
38295     },
38296
38297     // private
38298     onKeyUp : function(e){
38299         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38300             this.autoSize();
38301         }
38302     },
38303
38304     /**
38305      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38306      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38307      */
38308     autoSize : function(){
38309         if(!this.grow || !this.textSizeEl){
38310             return;
38311         }
38312         var el = this.el;
38313         var v = el.dom.value;
38314         var ts = this.textSizeEl;
38315
38316         ts.innerHTML = '';
38317         ts.appendChild(document.createTextNode(v));
38318         v = ts.innerHTML;
38319
38320         Roo.fly(ts).setWidth(this.el.getWidth());
38321         if(v.length < 1){
38322             v = "&#160;&#160;";
38323         }else{
38324             if(Roo.isIE){
38325                 v = v.replace(/\n/g, '<p>&#160;</p>');
38326             }
38327             v += "&#160;\n&#160;";
38328         }
38329         ts.innerHTML = v;
38330         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38331         if(h != this.lastHeight){
38332             this.lastHeight = h;
38333             this.el.setHeight(h);
38334             this.fireEvent("autosize", this, h);
38335         }
38336     }
38337 });/*
38338  * Based on:
38339  * Ext JS Library 1.1.1
38340  * Copyright(c) 2006-2007, Ext JS, LLC.
38341  *
38342  * Originally Released Under LGPL - original licence link has changed is not relivant.
38343  *
38344  * Fork - LGPL
38345  * <script type="text/javascript">
38346  */
38347  
38348
38349 /**
38350  * @class Roo.form.NumberField
38351  * @extends Roo.form.TextField
38352  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38353  * @constructor
38354  * Creates a new NumberField
38355  * @param {Object} config Configuration options
38356  */
38357 Roo.form.NumberField = function(config){
38358     Roo.form.NumberField.superclass.constructor.call(this, config);
38359 };
38360
38361 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38362     /**
38363      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38364      */
38365     fieldClass: "x-form-field x-form-num-field",
38366     /**
38367      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38368      */
38369     allowDecimals : true,
38370     /**
38371      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38372      */
38373     decimalSeparator : ".",
38374     /**
38375      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38376      */
38377     decimalPrecision : 2,
38378     /**
38379      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38380      */
38381     allowNegative : true,
38382     /**
38383      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38384      */
38385     minValue : Number.NEGATIVE_INFINITY,
38386     /**
38387      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38388      */
38389     maxValue : Number.MAX_VALUE,
38390     /**
38391      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38392      */
38393     minText : "The minimum value for this field is {0}",
38394     /**
38395      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38396      */
38397     maxText : "The maximum value for this field is {0}",
38398     /**
38399      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38400      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38401      */
38402     nanText : "{0} is not a valid number",
38403
38404     // private
38405     initEvents : function(){
38406         Roo.form.NumberField.superclass.initEvents.call(this);
38407         var allowed = "0123456789";
38408         if(this.allowDecimals){
38409             allowed += this.decimalSeparator;
38410         }
38411         if(this.allowNegative){
38412             allowed += "-";
38413         }
38414         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38415         var keyPress = function(e){
38416             var k = e.getKey();
38417             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38418                 return;
38419             }
38420             var c = e.getCharCode();
38421             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38422                 e.stopEvent();
38423             }
38424         };
38425         this.el.on("keypress", keyPress, this);
38426     },
38427
38428     // private
38429     validateValue : function(value){
38430         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38431             return false;
38432         }
38433         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38434              return true;
38435         }
38436         var num = this.parseValue(value);
38437         if(isNaN(num)){
38438             this.markInvalid(String.format(this.nanText, value));
38439             return false;
38440         }
38441         if(num < this.minValue){
38442             this.markInvalid(String.format(this.minText, this.minValue));
38443             return false;
38444         }
38445         if(num > this.maxValue){
38446             this.markInvalid(String.format(this.maxText, this.maxValue));
38447             return false;
38448         }
38449         return true;
38450     },
38451
38452     getValue : function(){
38453         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38454     },
38455
38456     // private
38457     parseValue : function(value){
38458         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38459         return isNaN(value) ? '' : value;
38460     },
38461
38462     // private
38463     fixPrecision : function(value){
38464         var nan = isNaN(value);
38465         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38466             return nan ? '' : value;
38467         }
38468         return parseFloat(value).toFixed(this.decimalPrecision);
38469     },
38470
38471     setValue : function(v){
38472         v = this.fixPrecision(v);
38473         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38474     },
38475
38476     // private
38477     decimalPrecisionFcn : function(v){
38478         return Math.floor(v);
38479     },
38480
38481     beforeBlur : function(){
38482         var v = this.parseValue(this.getRawValue());
38483         if(v){
38484             this.setValue(v);
38485         }
38486     }
38487 });/*
38488  * Based on:
38489  * Ext JS Library 1.1.1
38490  * Copyright(c) 2006-2007, Ext JS, LLC.
38491  *
38492  * Originally Released Under LGPL - original licence link has changed is not relivant.
38493  *
38494  * Fork - LGPL
38495  * <script type="text/javascript">
38496  */
38497  
38498 /**
38499  * @class Roo.form.DateField
38500  * @extends Roo.form.TriggerField
38501  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38502 * @constructor
38503 * Create a new DateField
38504 * @param {Object} config
38505  */
38506 Roo.form.DateField = function(config){
38507     Roo.form.DateField.superclass.constructor.call(this, config);
38508     
38509       this.addEvents({
38510          
38511         /**
38512          * @event select
38513          * Fires when a date is selected
38514              * @param {Roo.form.DateField} combo This combo box
38515              * @param {Date} date The date selected
38516              */
38517         'select' : true
38518          
38519     });
38520     
38521     
38522     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38523     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38524     this.ddMatch = null;
38525     if(this.disabledDates){
38526         var dd = this.disabledDates;
38527         var re = "(?:";
38528         for(var i = 0; i < dd.length; i++){
38529             re += dd[i];
38530             if(i != dd.length-1) re += "|";
38531         }
38532         this.ddMatch = new RegExp(re + ")");
38533     }
38534 };
38535
38536 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38537     /**
38538      * @cfg {String} format
38539      * The default date format string which can be overriden for localization support.  The format must be
38540      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38541      */
38542     format : "m/d/y",
38543     /**
38544      * @cfg {String} altFormats
38545      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38546      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38547      */
38548     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38549     /**
38550      * @cfg {Array} disabledDays
38551      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38552      */
38553     disabledDays : null,
38554     /**
38555      * @cfg {String} disabledDaysText
38556      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38557      */
38558     disabledDaysText : "Disabled",
38559     /**
38560      * @cfg {Array} disabledDates
38561      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38562      * expression so they are very powerful. Some examples:
38563      * <ul>
38564      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38565      * <li>["03/08", "09/16"] would disable those days for every year</li>
38566      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38567      * <li>["03/../2006"] would disable every day in March 2006</li>
38568      * <li>["^03"] would disable every day in every March</li>
38569      * </ul>
38570      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38571      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38572      */
38573     disabledDates : null,
38574     /**
38575      * @cfg {String} disabledDatesText
38576      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38577      */
38578     disabledDatesText : "Disabled",
38579     /**
38580      * @cfg {Date/String} minValue
38581      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38582      * valid format (defaults to null).
38583      */
38584     minValue : null,
38585     /**
38586      * @cfg {Date/String} maxValue
38587      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38588      * valid format (defaults to null).
38589      */
38590     maxValue : null,
38591     /**
38592      * @cfg {String} minText
38593      * The error text to display when the date in the cell is before minValue (defaults to
38594      * 'The date in this field must be after {minValue}').
38595      */
38596     minText : "The date in this field must be equal to or after {0}",
38597     /**
38598      * @cfg {String} maxText
38599      * The error text to display when the date in the cell is after maxValue (defaults to
38600      * 'The date in this field must be before {maxValue}').
38601      */
38602     maxText : "The date in this field must be equal to or before {0}",
38603     /**
38604      * @cfg {String} invalidText
38605      * The error text to display when the date in the field is invalid (defaults to
38606      * '{value} is not a valid date - it must be in the format {format}').
38607      */
38608     invalidText : "{0} is not a valid date - it must be in the format {1}",
38609     /**
38610      * @cfg {String} triggerClass
38611      * An additional CSS class used to style the trigger button.  The trigger will always get the
38612      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38613      * which displays a calendar icon).
38614      */
38615     triggerClass : 'x-form-date-trigger',
38616     
38617
38618     /**
38619      * @cfg {Boolean} useIso
38620      * if enabled, then the date field will use a hidden field to store the 
38621      * real value as iso formated date. default (false)
38622      */ 
38623     useIso : false,
38624     /**
38625      * @cfg {String/Object} autoCreate
38626      * A DomHelper element spec, or true for a default element spec (defaults to
38627      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38628      */ 
38629     // private
38630     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38631     
38632     // private
38633     hiddenField: false,
38634     
38635     onRender : function(ct, position)
38636     {
38637         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38638         if (this.useIso) {
38639             //this.el.dom.removeAttribute('name'); 
38640             Roo.log("Changing name?");
38641             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38642             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38643                     'before', true);
38644             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38645             // prevent input submission
38646             this.hiddenName = this.name;
38647         }
38648             
38649             
38650     },
38651     
38652     // private
38653     validateValue : function(value)
38654     {
38655         value = this.formatDate(value);
38656         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38657             Roo.log('super failed');
38658             return false;
38659         }
38660         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38661              return true;
38662         }
38663         var svalue = value;
38664         value = this.parseDate(value);
38665         if(!value){
38666             Roo.log('parse date failed' + svalue);
38667             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38668             return false;
38669         }
38670         var time = value.getTime();
38671         if(this.minValue && time < this.minValue.getTime()){
38672             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38673             return false;
38674         }
38675         if(this.maxValue && time > this.maxValue.getTime()){
38676             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38677             return false;
38678         }
38679         if(this.disabledDays){
38680             var day = value.getDay();
38681             for(var i = 0; i < this.disabledDays.length; i++) {
38682                 if(day === this.disabledDays[i]){
38683                     this.markInvalid(this.disabledDaysText);
38684                     return false;
38685                 }
38686             }
38687         }
38688         var fvalue = this.formatDate(value);
38689         if(this.ddMatch && this.ddMatch.test(fvalue)){
38690             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38691             return false;
38692         }
38693         return true;
38694     },
38695
38696     // private
38697     // Provides logic to override the default TriggerField.validateBlur which just returns true
38698     validateBlur : function(){
38699         return !this.menu || !this.menu.isVisible();
38700     },
38701     
38702     getName: function()
38703     {
38704         // returns hidden if it's set..
38705         if (!this.rendered) {return ''};
38706         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38707         
38708     },
38709
38710     /**
38711      * Returns the current date value of the date field.
38712      * @return {Date} The date value
38713      */
38714     getValue : function(){
38715         
38716         return  this.hiddenField ?
38717                 this.hiddenField.value :
38718                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38719     },
38720
38721     /**
38722      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38723      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38724      * (the default format used is "m/d/y").
38725      * <br />Usage:
38726      * <pre><code>
38727 //All of these calls set the same date value (May 4, 2006)
38728
38729 //Pass a date object:
38730 var dt = new Date('5/4/06');
38731 dateField.setValue(dt);
38732
38733 //Pass a date string (default format):
38734 dateField.setValue('5/4/06');
38735
38736 //Pass a date string (custom format):
38737 dateField.format = 'Y-m-d';
38738 dateField.setValue('2006-5-4');
38739 </code></pre>
38740      * @param {String/Date} date The date or valid date string
38741      */
38742     setValue : function(date){
38743         if (this.hiddenField) {
38744             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38745         }
38746         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38747         // make sure the value field is always stored as a date..
38748         this.value = this.parseDate(date);
38749         
38750         
38751     },
38752
38753     // private
38754     parseDate : function(value){
38755         if(!value || value instanceof Date){
38756             return value;
38757         }
38758         var v = Date.parseDate(value, this.format);
38759          if (!v && this.useIso) {
38760             v = Date.parseDate(value, 'Y-m-d');
38761         }
38762         if(!v && this.altFormats){
38763             if(!this.altFormatsArray){
38764                 this.altFormatsArray = this.altFormats.split("|");
38765             }
38766             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38767                 v = Date.parseDate(value, this.altFormatsArray[i]);
38768             }
38769         }
38770         return v;
38771     },
38772
38773     // private
38774     formatDate : function(date, fmt){
38775         return (!date || !(date instanceof Date)) ?
38776                date : date.dateFormat(fmt || this.format);
38777     },
38778
38779     // private
38780     menuListeners : {
38781         select: function(m, d){
38782             
38783             this.setValue(d);
38784             this.fireEvent('select', this, d);
38785         },
38786         show : function(){ // retain focus styling
38787             this.onFocus();
38788         },
38789         hide : function(){
38790             this.focus.defer(10, this);
38791             var ml = this.menuListeners;
38792             this.menu.un("select", ml.select,  this);
38793             this.menu.un("show", ml.show,  this);
38794             this.menu.un("hide", ml.hide,  this);
38795         }
38796     },
38797
38798     // private
38799     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38800     onTriggerClick : function(){
38801         if(this.disabled){
38802             return;
38803         }
38804         if(this.menu == null){
38805             this.menu = new Roo.menu.DateMenu();
38806         }
38807         Roo.apply(this.menu.picker,  {
38808             showClear: this.allowBlank,
38809             minDate : this.minValue,
38810             maxDate : this.maxValue,
38811             disabledDatesRE : this.ddMatch,
38812             disabledDatesText : this.disabledDatesText,
38813             disabledDays : this.disabledDays,
38814             disabledDaysText : this.disabledDaysText,
38815             format : this.useIso ? 'Y-m-d' : this.format,
38816             minText : String.format(this.minText, this.formatDate(this.minValue)),
38817             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38818         });
38819         this.menu.on(Roo.apply({}, this.menuListeners, {
38820             scope:this
38821         }));
38822         this.menu.picker.setValue(this.getValue() || new Date());
38823         this.menu.show(this.el, "tl-bl?");
38824     },
38825
38826     beforeBlur : function(){
38827         var v = this.parseDate(this.getRawValue());
38828         if(v){
38829             this.setValue(v);
38830         }
38831     },
38832
38833     /*@
38834      * overide
38835      * 
38836      */
38837     isDirty : function() {
38838         if(this.disabled) {
38839             return false;
38840         }
38841         
38842         if(typeof(this.startValue) === 'undefined'){
38843             return false;
38844         }
38845         
38846         return String(this.getValue()) !== String(this.startValue);
38847         
38848     }
38849 });/*
38850  * Based on:
38851  * Ext JS Library 1.1.1
38852  * Copyright(c) 2006-2007, Ext JS, LLC.
38853  *
38854  * Originally Released Under LGPL - original licence link has changed is not relivant.
38855  *
38856  * Fork - LGPL
38857  * <script type="text/javascript">
38858  */
38859  
38860 /**
38861  * @class Roo.form.MonthField
38862  * @extends Roo.form.TriggerField
38863  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38864 * @constructor
38865 * Create a new MonthField
38866 * @param {Object} config
38867  */
38868 Roo.form.MonthField = function(config){
38869     
38870     Roo.form.MonthField.superclass.constructor.call(this, config);
38871     
38872       this.addEvents({
38873          
38874         /**
38875          * @event select
38876          * Fires when a date is selected
38877              * @param {Roo.form.MonthFieeld} combo This combo box
38878              * @param {Date} date The date selected
38879              */
38880         'select' : true
38881          
38882     });
38883     
38884     
38885     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38886     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38887     this.ddMatch = null;
38888     if(this.disabledDates){
38889         var dd = this.disabledDates;
38890         var re = "(?:";
38891         for(var i = 0; i < dd.length; i++){
38892             re += dd[i];
38893             if(i != dd.length-1) re += "|";
38894         }
38895         this.ddMatch = new RegExp(re + ")");
38896     }
38897 };
38898
38899 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38900     /**
38901      * @cfg {String} format
38902      * The default date format string which can be overriden for localization support.  The format must be
38903      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38904      */
38905     format : "M Y",
38906     /**
38907      * @cfg {String} altFormats
38908      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38909      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38910      */
38911     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38912     /**
38913      * @cfg {Array} disabledDays
38914      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38915      */
38916     disabledDays : [0,1,2,3,4,5,6],
38917     /**
38918      * @cfg {String} disabledDaysText
38919      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38920      */
38921     disabledDaysText : "Disabled",
38922     /**
38923      * @cfg {Array} disabledDates
38924      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38925      * expression so they are very powerful. Some examples:
38926      * <ul>
38927      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38928      * <li>["03/08", "09/16"] would disable those days for every year</li>
38929      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38930      * <li>["03/../2006"] would disable every day in March 2006</li>
38931      * <li>["^03"] would disable every day in every March</li>
38932      * </ul>
38933      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38934      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38935      */
38936     disabledDates : null,
38937     /**
38938      * @cfg {String} disabledDatesText
38939      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38940      */
38941     disabledDatesText : "Disabled",
38942     /**
38943      * @cfg {Date/String} minValue
38944      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38945      * valid format (defaults to null).
38946      */
38947     minValue : null,
38948     /**
38949      * @cfg {Date/String} maxValue
38950      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38951      * valid format (defaults to null).
38952      */
38953     maxValue : null,
38954     /**
38955      * @cfg {String} minText
38956      * The error text to display when the date in the cell is before minValue (defaults to
38957      * 'The date in this field must be after {minValue}').
38958      */
38959     minText : "The date in this field must be equal to or after {0}",
38960     /**
38961      * @cfg {String} maxTextf
38962      * The error text to display when the date in the cell is after maxValue (defaults to
38963      * 'The date in this field must be before {maxValue}').
38964      */
38965     maxText : "The date in this field must be equal to or before {0}",
38966     /**
38967      * @cfg {String} invalidText
38968      * The error text to display when the date in the field is invalid (defaults to
38969      * '{value} is not a valid date - it must be in the format {format}').
38970      */
38971     invalidText : "{0} is not a valid date - it must be in the format {1}",
38972     /**
38973      * @cfg {String} triggerClass
38974      * An additional CSS class used to style the trigger button.  The trigger will always get the
38975      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38976      * which displays a calendar icon).
38977      */
38978     triggerClass : 'x-form-date-trigger',
38979     
38980
38981     /**
38982      * @cfg {Boolean} useIso
38983      * if enabled, then the date field will use a hidden field to store the 
38984      * real value as iso formated date. default (true)
38985      */ 
38986     useIso : true,
38987     /**
38988      * @cfg {String/Object} autoCreate
38989      * A DomHelper element spec, or true for a default element spec (defaults to
38990      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38991      */ 
38992     // private
38993     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38994     
38995     // private
38996     hiddenField: false,
38997     
38998     hideMonthPicker : false,
38999     
39000     onRender : function(ct, position)
39001     {
39002         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39003         if (this.useIso) {
39004             this.el.dom.removeAttribute('name'); 
39005             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39006                     'before', true);
39007             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39008             // prevent input submission
39009             this.hiddenName = this.name;
39010         }
39011             
39012             
39013     },
39014     
39015     // private
39016     validateValue : function(value)
39017     {
39018         value = this.formatDate(value);
39019         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39020             return false;
39021         }
39022         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39023              return true;
39024         }
39025         var svalue = value;
39026         value = this.parseDate(value);
39027         if(!value){
39028             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39029             return false;
39030         }
39031         var time = value.getTime();
39032         if(this.minValue && time < this.minValue.getTime()){
39033             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39034             return false;
39035         }
39036         if(this.maxValue && time > this.maxValue.getTime()){
39037             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39038             return false;
39039         }
39040         /*if(this.disabledDays){
39041             var day = value.getDay();
39042             for(var i = 0; i < this.disabledDays.length; i++) {
39043                 if(day === this.disabledDays[i]){
39044                     this.markInvalid(this.disabledDaysText);
39045                     return false;
39046                 }
39047             }
39048         }
39049         */
39050         var fvalue = this.formatDate(value);
39051         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39052             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39053             return false;
39054         }
39055         */
39056         return true;
39057     },
39058
39059     // private
39060     // Provides logic to override the default TriggerField.validateBlur which just returns true
39061     validateBlur : function(){
39062         return !this.menu || !this.menu.isVisible();
39063     },
39064
39065     /**
39066      * Returns the current date value of the date field.
39067      * @return {Date} The date value
39068      */
39069     getValue : function(){
39070         
39071         
39072         
39073         return  this.hiddenField ?
39074                 this.hiddenField.value :
39075                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39076     },
39077
39078     /**
39079      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39080      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39081      * (the default format used is "m/d/y").
39082      * <br />Usage:
39083      * <pre><code>
39084 //All of these calls set the same date value (May 4, 2006)
39085
39086 //Pass a date object:
39087 var dt = new Date('5/4/06');
39088 monthField.setValue(dt);
39089
39090 //Pass a date string (default format):
39091 monthField.setValue('5/4/06');
39092
39093 //Pass a date string (custom format):
39094 monthField.format = 'Y-m-d';
39095 monthField.setValue('2006-5-4');
39096 </code></pre>
39097      * @param {String/Date} date The date or valid date string
39098      */
39099     setValue : function(date){
39100         Roo.log('month setValue' + date);
39101         // can only be first of month..
39102         
39103         var val = this.parseDate(date);
39104         
39105         if (this.hiddenField) {
39106             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39107         }
39108         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39109         this.value = this.parseDate(date);
39110     },
39111
39112     // private
39113     parseDate : function(value){
39114         if(!value || value instanceof Date){
39115             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39116             return value;
39117         }
39118         var v = Date.parseDate(value, this.format);
39119         if (!v && this.useIso) {
39120             v = Date.parseDate(value, 'Y-m-d');
39121         }
39122         if (v) {
39123             // 
39124             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39125         }
39126         
39127         
39128         if(!v && this.altFormats){
39129             if(!this.altFormatsArray){
39130                 this.altFormatsArray = this.altFormats.split("|");
39131             }
39132             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39133                 v = Date.parseDate(value, this.altFormatsArray[i]);
39134             }
39135         }
39136         return v;
39137     },
39138
39139     // private
39140     formatDate : function(date, fmt){
39141         return (!date || !(date instanceof Date)) ?
39142                date : date.dateFormat(fmt || this.format);
39143     },
39144
39145     // private
39146     menuListeners : {
39147         select: function(m, d){
39148             this.setValue(d);
39149             this.fireEvent('select', this, d);
39150         },
39151         show : function(){ // retain focus styling
39152             this.onFocus();
39153         },
39154         hide : function(){
39155             this.focus.defer(10, this);
39156             var ml = this.menuListeners;
39157             this.menu.un("select", ml.select,  this);
39158             this.menu.un("show", ml.show,  this);
39159             this.menu.un("hide", ml.hide,  this);
39160         }
39161     },
39162     // private
39163     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39164     onTriggerClick : function(){
39165         if(this.disabled){
39166             return;
39167         }
39168         if(this.menu == null){
39169             this.menu = new Roo.menu.DateMenu();
39170            
39171         }
39172         
39173         Roo.apply(this.menu.picker,  {
39174             
39175             showClear: this.allowBlank,
39176             minDate : this.minValue,
39177             maxDate : this.maxValue,
39178             disabledDatesRE : this.ddMatch,
39179             disabledDatesText : this.disabledDatesText,
39180             
39181             format : this.useIso ? 'Y-m-d' : this.format,
39182             minText : String.format(this.minText, this.formatDate(this.minValue)),
39183             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39184             
39185         });
39186          this.menu.on(Roo.apply({}, this.menuListeners, {
39187             scope:this
39188         }));
39189        
39190         
39191         var m = this.menu;
39192         var p = m.picker;
39193         
39194         // hide month picker get's called when we called by 'before hide';
39195         
39196         var ignorehide = true;
39197         p.hideMonthPicker  = function(disableAnim){
39198             if (ignorehide) {
39199                 return;
39200             }
39201              if(this.monthPicker){
39202                 Roo.log("hideMonthPicker called");
39203                 if(disableAnim === true){
39204                     this.monthPicker.hide();
39205                 }else{
39206                     this.monthPicker.slideOut('t', {duration:.2});
39207                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39208                     p.fireEvent("select", this, this.value);
39209                     m.hide();
39210                 }
39211             }
39212         }
39213         
39214         Roo.log('picker set value');
39215         Roo.log(this.getValue());
39216         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39217         m.show(this.el, 'tl-bl?');
39218         ignorehide  = false;
39219         // this will trigger hideMonthPicker..
39220         
39221         
39222         // hidden the day picker
39223         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39224         
39225         
39226         
39227       
39228         
39229         p.showMonthPicker.defer(100, p);
39230     
39231         
39232        
39233     },
39234
39235     beforeBlur : function(){
39236         var v = this.parseDate(this.getRawValue());
39237         if(v){
39238             this.setValue(v);
39239         }
39240     }
39241
39242     /** @cfg {Boolean} grow @hide */
39243     /** @cfg {Number} growMin @hide */
39244     /** @cfg {Number} growMax @hide */
39245     /**
39246      * @hide
39247      * @method autoSize
39248      */
39249 });/*
39250  * Based on:
39251  * Ext JS Library 1.1.1
39252  * Copyright(c) 2006-2007, Ext JS, LLC.
39253  *
39254  * Originally Released Under LGPL - original licence link has changed is not relivant.
39255  *
39256  * Fork - LGPL
39257  * <script type="text/javascript">
39258  */
39259  
39260
39261 /**
39262  * @class Roo.form.ComboBox
39263  * @extends Roo.form.TriggerField
39264  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39265  * @constructor
39266  * Create a new ComboBox.
39267  * @param {Object} config Configuration options
39268  */
39269 Roo.form.ComboBox = function(config){
39270     Roo.form.ComboBox.superclass.constructor.call(this, config);
39271     this.addEvents({
39272         /**
39273          * @event expand
39274          * Fires when the dropdown list is expanded
39275              * @param {Roo.form.ComboBox} combo This combo box
39276              */
39277         'expand' : true,
39278         /**
39279          * @event collapse
39280          * Fires when the dropdown list is collapsed
39281              * @param {Roo.form.ComboBox} combo This combo box
39282              */
39283         'collapse' : true,
39284         /**
39285          * @event beforeselect
39286          * Fires before a list item is selected. Return false to cancel the selection.
39287              * @param {Roo.form.ComboBox} combo This combo box
39288              * @param {Roo.data.Record} record The data record returned from the underlying store
39289              * @param {Number} index The index of the selected item in the dropdown list
39290              */
39291         'beforeselect' : true,
39292         /**
39293          * @event select
39294          * Fires when a list item is selected
39295              * @param {Roo.form.ComboBox} combo This combo box
39296              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39297              * @param {Number} index The index of the selected item in the dropdown list
39298              */
39299         'select' : true,
39300         /**
39301          * @event beforequery
39302          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39303          * The event object passed has these properties:
39304              * @param {Roo.form.ComboBox} combo This combo box
39305              * @param {String} query The query
39306              * @param {Boolean} forceAll true to force "all" query
39307              * @param {Boolean} cancel true to cancel the query
39308              * @param {Object} e The query event object
39309              */
39310         'beforequery': true,
39311          /**
39312          * @event add
39313          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39314              * @param {Roo.form.ComboBox} combo This combo box
39315              */
39316         'add' : true,
39317         /**
39318          * @event edit
39319          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39320              * @param {Roo.form.ComboBox} combo This combo box
39321              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39322              */
39323         'edit' : true
39324         
39325         
39326     });
39327     if(this.transform){
39328         this.allowDomMove = false;
39329         var s = Roo.getDom(this.transform);
39330         if(!this.hiddenName){
39331             this.hiddenName = s.name;
39332         }
39333         if(!this.store){
39334             this.mode = 'local';
39335             var d = [], opts = s.options;
39336             for(var i = 0, len = opts.length;i < len; i++){
39337                 var o = opts[i];
39338                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39339                 if(o.selected) {
39340                     this.value = value;
39341                 }
39342                 d.push([value, o.text]);
39343             }
39344             this.store = new Roo.data.SimpleStore({
39345                 'id': 0,
39346                 fields: ['value', 'text'],
39347                 data : d
39348             });
39349             this.valueField = 'value';
39350             this.displayField = 'text';
39351         }
39352         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39353         if(!this.lazyRender){
39354             this.target = true;
39355             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39356             s.parentNode.removeChild(s); // remove it
39357             this.render(this.el.parentNode);
39358         }else{
39359             s.parentNode.removeChild(s); // remove it
39360         }
39361
39362     }
39363     if (this.store) {
39364         this.store = Roo.factory(this.store, Roo.data);
39365     }
39366     
39367     this.selectedIndex = -1;
39368     if(this.mode == 'local'){
39369         if(config.queryDelay === undefined){
39370             this.queryDelay = 10;
39371         }
39372         if(config.minChars === undefined){
39373             this.minChars = 0;
39374         }
39375     }
39376 };
39377
39378 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39379     /**
39380      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39381      */
39382     /**
39383      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39384      * rendering into an Roo.Editor, defaults to false)
39385      */
39386     /**
39387      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39388      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39389      */
39390     /**
39391      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39392      */
39393     /**
39394      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39395      * the dropdown list (defaults to undefined, with no header element)
39396      */
39397
39398      /**
39399      * @cfg {String/Roo.Template} tpl The template to use to render the output
39400      */
39401      
39402     // private
39403     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39404     /**
39405      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39406      */
39407     listWidth: undefined,
39408     /**
39409      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39410      * mode = 'remote' or 'text' if mode = 'local')
39411      */
39412     displayField: undefined,
39413     /**
39414      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39415      * mode = 'remote' or 'value' if mode = 'local'). 
39416      * Note: use of a valueField requires the user make a selection
39417      * in order for a value to be mapped.
39418      */
39419     valueField: undefined,
39420     
39421     
39422     /**
39423      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39424      * field's data value (defaults to the underlying DOM element's name)
39425      */
39426     hiddenName: undefined,
39427     /**
39428      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39429      */
39430     listClass: '',
39431     /**
39432      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39433      */
39434     selectedClass: 'x-combo-selected',
39435     /**
39436      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39437      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39438      * which displays a downward arrow icon).
39439      */
39440     triggerClass : 'x-form-arrow-trigger',
39441     /**
39442      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39443      */
39444     shadow:'sides',
39445     /**
39446      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39447      * anchor positions (defaults to 'tl-bl')
39448      */
39449     listAlign: 'tl-bl?',
39450     /**
39451      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39452      */
39453     maxHeight: 300,
39454     /**
39455      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39456      * query specified by the allQuery config option (defaults to 'query')
39457      */
39458     triggerAction: 'query',
39459     /**
39460      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39461      * (defaults to 4, does not apply if editable = false)
39462      */
39463     minChars : 4,
39464     /**
39465      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39466      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39467      */
39468     typeAhead: false,
39469     /**
39470      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39471      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39472      */
39473     queryDelay: 500,
39474     /**
39475      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39476      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39477      */
39478     pageSize: 0,
39479     /**
39480      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39481      * when editable = true (defaults to false)
39482      */
39483     selectOnFocus:false,
39484     /**
39485      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39486      */
39487     queryParam: 'query',
39488     /**
39489      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39490      * when mode = 'remote' (defaults to 'Loading...')
39491      */
39492     loadingText: 'Loading...',
39493     /**
39494      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39495      */
39496     resizable: false,
39497     /**
39498      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39499      */
39500     handleHeight : 8,
39501     /**
39502      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39503      * traditional select (defaults to true)
39504      */
39505     editable: true,
39506     /**
39507      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39508      */
39509     allQuery: '',
39510     /**
39511      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39512      */
39513     mode: 'remote',
39514     /**
39515      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39516      * listWidth has a higher value)
39517      */
39518     minListWidth : 70,
39519     /**
39520      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39521      * allow the user to set arbitrary text into the field (defaults to false)
39522      */
39523     forceSelection:false,
39524     /**
39525      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39526      * if typeAhead = true (defaults to 250)
39527      */
39528     typeAheadDelay : 250,
39529     /**
39530      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39531      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39532      */
39533     valueNotFoundText : undefined,
39534     /**
39535      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39536      */
39537     blockFocus : false,
39538     
39539     /**
39540      * @cfg {Boolean} disableClear Disable showing of clear button.
39541      */
39542     disableClear : false,
39543     /**
39544      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39545      */
39546     alwaysQuery : false,
39547     
39548     //private
39549     addicon : false,
39550     editicon: false,
39551     
39552     // element that contains real text value.. (when hidden is used..)
39553      
39554     // private
39555     onRender : function(ct, position){
39556         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39557         if(this.hiddenName){
39558             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39559                     'before', true);
39560             this.hiddenField.value =
39561                 this.hiddenValue !== undefined ? this.hiddenValue :
39562                 this.value !== undefined ? this.value : '';
39563
39564             // prevent input submission
39565             this.el.dom.removeAttribute('name');
39566              
39567              
39568         }
39569         if(Roo.isGecko){
39570             this.el.dom.setAttribute('autocomplete', 'off');
39571         }
39572
39573         var cls = 'x-combo-list';
39574
39575         this.list = new Roo.Layer({
39576             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39577         });
39578
39579         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39580         this.list.setWidth(lw);
39581         this.list.swallowEvent('mousewheel');
39582         this.assetHeight = 0;
39583
39584         if(this.title){
39585             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39586             this.assetHeight += this.header.getHeight();
39587         }
39588
39589         this.innerList = this.list.createChild({cls:cls+'-inner'});
39590         this.innerList.on('mouseover', this.onViewOver, this);
39591         this.innerList.on('mousemove', this.onViewMove, this);
39592         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39593         
39594         if(this.allowBlank && !this.pageSize && !this.disableClear){
39595             this.footer = this.list.createChild({cls:cls+'-ft'});
39596             this.pageTb = new Roo.Toolbar(this.footer);
39597            
39598         }
39599         if(this.pageSize){
39600             this.footer = this.list.createChild({cls:cls+'-ft'});
39601             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39602                     {pageSize: this.pageSize});
39603             
39604         }
39605         
39606         if (this.pageTb && this.allowBlank && !this.disableClear) {
39607             var _this = this;
39608             this.pageTb.add(new Roo.Toolbar.Fill(), {
39609                 cls: 'x-btn-icon x-btn-clear',
39610                 text: '&#160;',
39611                 handler: function()
39612                 {
39613                     _this.collapse();
39614                     _this.clearValue();
39615                     _this.onSelect(false, -1);
39616                 }
39617             });
39618         }
39619         if (this.footer) {
39620             this.assetHeight += this.footer.getHeight();
39621         }
39622         
39623
39624         if(!this.tpl){
39625             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39626         }
39627
39628         this.view = new Roo.View(this.innerList, this.tpl, {
39629             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39630         });
39631
39632         this.view.on('click', this.onViewClick, this);
39633
39634         this.store.on('beforeload', this.onBeforeLoad, this);
39635         this.store.on('load', this.onLoad, this);
39636         this.store.on('loadexception', this.onLoadException, this);
39637
39638         if(this.resizable){
39639             this.resizer = new Roo.Resizable(this.list,  {
39640                pinned:true, handles:'se'
39641             });
39642             this.resizer.on('resize', function(r, w, h){
39643                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39644                 this.listWidth = w;
39645                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39646                 this.restrictHeight();
39647             }, this);
39648             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39649         }
39650         if(!this.editable){
39651             this.editable = true;
39652             this.setEditable(false);
39653         }  
39654         
39655         
39656         if (typeof(this.events.add.listeners) != 'undefined') {
39657             
39658             this.addicon = this.wrap.createChild(
39659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39660        
39661             this.addicon.on('click', function(e) {
39662                 this.fireEvent('add', this);
39663             }, this);
39664         }
39665         if (typeof(this.events.edit.listeners) != 'undefined') {
39666             
39667             this.editicon = this.wrap.createChild(
39668                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39669             if (this.addicon) {
39670                 this.editicon.setStyle('margin-left', '40px');
39671             }
39672             this.editicon.on('click', function(e) {
39673                 
39674                 // we fire even  if inothing is selected..
39675                 this.fireEvent('edit', this, this.lastData );
39676                 
39677             }, this);
39678         }
39679         
39680         
39681         
39682     },
39683
39684     // private
39685     initEvents : function(){
39686         Roo.form.ComboBox.superclass.initEvents.call(this);
39687
39688         this.keyNav = new Roo.KeyNav(this.el, {
39689             "up" : function(e){
39690                 this.inKeyMode = true;
39691                 this.selectPrev();
39692             },
39693
39694             "down" : function(e){
39695                 if(!this.isExpanded()){
39696                     this.onTriggerClick();
39697                 }else{
39698                     this.inKeyMode = true;
39699                     this.selectNext();
39700                 }
39701             },
39702
39703             "enter" : function(e){
39704                 this.onViewClick();
39705                 //return true;
39706             },
39707
39708             "esc" : function(e){
39709                 this.collapse();
39710             },
39711
39712             "tab" : function(e){
39713                 this.onViewClick(false);
39714                 this.fireEvent("specialkey", this, e);
39715                 return true;
39716             },
39717
39718             scope : this,
39719
39720             doRelay : function(foo, bar, hname){
39721                 if(hname == 'down' || this.scope.isExpanded()){
39722                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39723                 }
39724                 return true;
39725             },
39726
39727             forceKeyDown: true
39728         });
39729         this.queryDelay = Math.max(this.queryDelay || 10,
39730                 this.mode == 'local' ? 10 : 250);
39731         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39732         if(this.typeAhead){
39733             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39734         }
39735         if(this.editable !== false){
39736             this.el.on("keyup", this.onKeyUp, this);
39737         }
39738         if(this.forceSelection){
39739             this.on('blur', this.doForce, this);
39740         }
39741     },
39742
39743     onDestroy : function(){
39744         if(this.view){
39745             this.view.setStore(null);
39746             this.view.el.removeAllListeners();
39747             this.view.el.remove();
39748             this.view.purgeListeners();
39749         }
39750         if(this.list){
39751             this.list.destroy();
39752         }
39753         if(this.store){
39754             this.store.un('beforeload', this.onBeforeLoad, this);
39755             this.store.un('load', this.onLoad, this);
39756             this.store.un('loadexception', this.onLoadException, this);
39757         }
39758         Roo.form.ComboBox.superclass.onDestroy.call(this);
39759     },
39760
39761     // private
39762     fireKey : function(e){
39763         if(e.isNavKeyPress() && !this.list.isVisible()){
39764             this.fireEvent("specialkey", this, e);
39765         }
39766     },
39767
39768     // private
39769     onResize: function(w, h){
39770         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39771         
39772         if(typeof w != 'number'){
39773             // we do not handle it!?!?
39774             return;
39775         }
39776         var tw = this.trigger.getWidth();
39777         tw += this.addicon ? this.addicon.getWidth() : 0;
39778         tw += this.editicon ? this.editicon.getWidth() : 0;
39779         var x = w - tw;
39780         this.el.setWidth( this.adjustWidth('input', x));
39781             
39782         this.trigger.setStyle('left', x+'px');
39783         
39784         if(this.list && this.listWidth === undefined){
39785             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39786             this.list.setWidth(lw);
39787             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39788         }
39789         
39790     
39791         
39792     },
39793
39794     /**
39795      * Allow or prevent the user from directly editing the field text.  If false is passed,
39796      * the user will only be able to select from the items defined in the dropdown list.  This method
39797      * is the runtime equivalent of setting the 'editable' config option at config time.
39798      * @param {Boolean} value True to allow the user to directly edit the field text
39799      */
39800     setEditable : function(value){
39801         if(value == this.editable){
39802             return;
39803         }
39804         this.editable = value;
39805         if(!value){
39806             this.el.dom.setAttribute('readOnly', true);
39807             this.el.on('mousedown', this.onTriggerClick,  this);
39808             this.el.addClass('x-combo-noedit');
39809         }else{
39810             this.el.dom.setAttribute('readOnly', false);
39811             this.el.un('mousedown', this.onTriggerClick,  this);
39812             this.el.removeClass('x-combo-noedit');
39813         }
39814     },
39815
39816     // private
39817     onBeforeLoad : function(){
39818         if(!this.hasFocus){
39819             return;
39820         }
39821         this.innerList.update(this.loadingText ?
39822                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39823         this.restrictHeight();
39824         this.selectedIndex = -1;
39825     },
39826
39827     // private
39828     onLoad : function(){
39829         if(!this.hasFocus){
39830             return;
39831         }
39832         if(this.store.getCount() > 0){
39833             this.expand();
39834             this.restrictHeight();
39835             if(this.lastQuery == this.allQuery){
39836                 if(this.editable){
39837                     this.el.dom.select();
39838                 }
39839                 if(!this.selectByValue(this.value, true)){
39840                     this.select(0, true);
39841                 }
39842             }else{
39843                 this.selectNext();
39844                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39845                     this.taTask.delay(this.typeAheadDelay);
39846                 }
39847             }
39848         }else{
39849             this.onEmptyResults();
39850         }
39851         //this.el.focus();
39852     },
39853     // private
39854     onLoadException : function()
39855     {
39856         this.collapse();
39857         Roo.log(this.store.reader.jsonData);
39858         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39859             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39860         }
39861         
39862         
39863     },
39864     // private
39865     onTypeAhead : function(){
39866         if(this.store.getCount() > 0){
39867             var r = this.store.getAt(0);
39868             var newValue = r.data[this.displayField];
39869             var len = newValue.length;
39870             var selStart = this.getRawValue().length;
39871             if(selStart != len){
39872                 this.setRawValue(newValue);
39873                 this.selectText(selStart, newValue.length);
39874             }
39875         }
39876     },
39877
39878     // private
39879     onSelect : function(record, index){
39880         if(this.fireEvent('beforeselect', this, record, index) !== false){
39881             this.setFromData(index > -1 ? record.data : false);
39882             this.collapse();
39883             this.fireEvent('select', this, record, index);
39884         }
39885     },
39886
39887     /**
39888      * Returns the currently selected field value or empty string if no value is set.
39889      * @return {String} value The selected value
39890      */
39891     getValue : function(){
39892         if(this.valueField){
39893             return typeof this.value != 'undefined' ? this.value : '';
39894         }
39895         return Roo.form.ComboBox.superclass.getValue.call(this);
39896     },
39897
39898     /**
39899      * Clears any text/value currently set in the field
39900      */
39901     clearValue : function(){
39902         if(this.hiddenField){
39903             this.hiddenField.value = '';
39904         }
39905         this.value = '';
39906         this.setRawValue('');
39907         this.lastSelectionText = '';
39908         
39909     },
39910
39911     /**
39912      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39913      * will be displayed in the field.  If the value does not match the data value of an existing item,
39914      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39915      * Otherwise the field will be blank (although the value will still be set).
39916      * @param {String} value The value to match
39917      */
39918     setValue : function(v){
39919         var text = v;
39920         if(this.valueField){
39921             var r = this.findRecord(this.valueField, v);
39922             if(r){
39923                 text = r.data[this.displayField];
39924             }else if(this.valueNotFoundText !== undefined){
39925                 text = this.valueNotFoundText;
39926             }
39927         }
39928         this.lastSelectionText = text;
39929         if(this.hiddenField){
39930             this.hiddenField.value = v;
39931         }
39932         Roo.form.ComboBox.superclass.setValue.call(this, text);
39933         this.value = v;
39934     },
39935     /**
39936      * @property {Object} the last set data for the element
39937      */
39938     
39939     lastData : false,
39940     /**
39941      * Sets the value of the field based on a object which is related to the record format for the store.
39942      * @param {Object} value the value to set as. or false on reset?
39943      */
39944     setFromData : function(o){
39945         var dv = ''; // display value
39946         var vv = ''; // value value..
39947         this.lastData = o;
39948         if (this.displayField) {
39949             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39950         } else {
39951             // this is an error condition!!!
39952             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39953         }
39954         
39955         if(this.valueField){
39956             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39957         }
39958         if(this.hiddenField){
39959             this.hiddenField.value = vv;
39960             
39961             this.lastSelectionText = dv;
39962             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39963             this.value = vv;
39964             return;
39965         }
39966         // no hidden field.. - we store the value in 'value', but still display
39967         // display field!!!!
39968         this.lastSelectionText = dv;
39969         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39970         this.value = vv;
39971         
39972         
39973     },
39974     // private
39975     reset : function(){
39976         // overridden so that last data is reset..
39977         this.setValue(this.resetValue);
39978         this.clearInvalid();
39979         this.lastData = false;
39980         if (this.view) {
39981             this.view.clearSelections();
39982         }
39983     },
39984     // private
39985     findRecord : function(prop, value){
39986         var record;
39987         if(this.store.getCount() > 0){
39988             this.store.each(function(r){
39989                 if(r.data[prop] == value){
39990                     record = r;
39991                     return false;
39992                 }
39993                 return true;
39994             });
39995         }
39996         return record;
39997     },
39998     
39999     getName: function()
40000     {
40001         // returns hidden if it's set..
40002         if (!this.rendered) {return ''};
40003         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40004         
40005     },
40006     // private
40007     onViewMove : function(e, t){
40008         this.inKeyMode = false;
40009     },
40010
40011     // private
40012     onViewOver : function(e, t){
40013         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40014             return;
40015         }
40016         var item = this.view.findItemFromChild(t);
40017         if(item){
40018             var index = this.view.indexOf(item);
40019             this.select(index, false);
40020         }
40021     },
40022
40023     // private
40024     onViewClick : function(doFocus)
40025     {
40026         var index = this.view.getSelectedIndexes()[0];
40027         var r = this.store.getAt(index);
40028         if(r){
40029             this.onSelect(r, index);
40030         }
40031         if(doFocus !== false && !this.blockFocus){
40032             this.el.focus();
40033         }
40034     },
40035
40036     // private
40037     restrictHeight : function(){
40038         this.innerList.dom.style.height = '';
40039         var inner = this.innerList.dom;
40040         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40041         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40042         this.list.beginUpdate();
40043         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40044         this.list.alignTo(this.el, this.listAlign);
40045         this.list.endUpdate();
40046     },
40047
40048     // private
40049     onEmptyResults : function(){
40050         this.collapse();
40051     },
40052
40053     /**
40054      * Returns true if the dropdown list is expanded, else false.
40055      */
40056     isExpanded : function(){
40057         return this.list.isVisible();
40058     },
40059
40060     /**
40061      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40062      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40063      * @param {String} value The data value of the item to select
40064      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40065      * selected item if it is not currently in view (defaults to true)
40066      * @return {Boolean} True if the value matched an item in the list, else false
40067      */
40068     selectByValue : function(v, scrollIntoView){
40069         if(v !== undefined && v !== null){
40070             var r = this.findRecord(this.valueField || this.displayField, v);
40071             if(r){
40072                 this.select(this.store.indexOf(r), scrollIntoView);
40073                 return true;
40074             }
40075         }
40076         return false;
40077     },
40078
40079     /**
40080      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40081      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40082      * @param {Number} index The zero-based index of the list item to select
40083      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40084      * selected item if it is not currently in view (defaults to true)
40085      */
40086     select : function(index, scrollIntoView){
40087         this.selectedIndex = index;
40088         this.view.select(index);
40089         if(scrollIntoView !== false){
40090             var el = this.view.getNode(index);
40091             if(el){
40092                 this.innerList.scrollChildIntoView(el, false);
40093             }
40094         }
40095     },
40096
40097     // private
40098     selectNext : function(){
40099         var ct = this.store.getCount();
40100         if(ct > 0){
40101             if(this.selectedIndex == -1){
40102                 this.select(0);
40103             }else if(this.selectedIndex < ct-1){
40104                 this.select(this.selectedIndex+1);
40105             }
40106         }
40107     },
40108
40109     // private
40110     selectPrev : function(){
40111         var ct = this.store.getCount();
40112         if(ct > 0){
40113             if(this.selectedIndex == -1){
40114                 this.select(0);
40115             }else if(this.selectedIndex != 0){
40116                 this.select(this.selectedIndex-1);
40117             }
40118         }
40119     },
40120
40121     // private
40122     onKeyUp : function(e){
40123         if(this.editable !== false && !e.isSpecialKey()){
40124             this.lastKey = e.getKey();
40125             this.dqTask.delay(this.queryDelay);
40126         }
40127     },
40128
40129     // private
40130     validateBlur : function(){
40131         return !this.list || !this.list.isVisible();   
40132     },
40133
40134     // private
40135     initQuery : function(){
40136         this.doQuery(this.getRawValue());
40137     },
40138
40139     // private
40140     doForce : function(){
40141         if(this.el.dom.value.length > 0){
40142             this.el.dom.value =
40143                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40144              
40145         }
40146     },
40147
40148     /**
40149      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40150      * query allowing the query action to be canceled if needed.
40151      * @param {String} query The SQL query to execute
40152      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40153      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40154      * saved in the current store (defaults to false)
40155      */
40156     doQuery : function(q, forceAll){
40157         if(q === undefined || q === null){
40158             q = '';
40159         }
40160         var qe = {
40161             query: q,
40162             forceAll: forceAll,
40163             combo: this,
40164             cancel:false
40165         };
40166         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40167             return false;
40168         }
40169         q = qe.query;
40170         forceAll = qe.forceAll;
40171         if(forceAll === true || (q.length >= this.minChars)){
40172             if(this.lastQuery != q || this.alwaysQuery){
40173                 this.lastQuery = q;
40174                 if(this.mode == 'local'){
40175                     this.selectedIndex = -1;
40176                     if(forceAll){
40177                         this.store.clearFilter();
40178                     }else{
40179                         this.store.filter(this.displayField, q);
40180                     }
40181                     this.onLoad();
40182                 }else{
40183                     this.store.baseParams[this.queryParam] = q;
40184                     this.store.load({
40185                         params: this.getParams(q)
40186                     });
40187                     this.expand();
40188                 }
40189             }else{
40190                 this.selectedIndex = -1;
40191                 this.onLoad();   
40192             }
40193         }
40194     },
40195
40196     // private
40197     getParams : function(q){
40198         var p = {};
40199         //p[this.queryParam] = q;
40200         if(this.pageSize){
40201             p.start = 0;
40202             p.limit = this.pageSize;
40203         }
40204         return p;
40205     },
40206
40207     /**
40208      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40209      */
40210     collapse : function(){
40211         if(!this.isExpanded()){
40212             return;
40213         }
40214         this.list.hide();
40215         Roo.get(document).un('mousedown', this.collapseIf, this);
40216         Roo.get(document).un('mousewheel', this.collapseIf, this);
40217         if (!this.editable) {
40218             Roo.get(document).un('keydown', this.listKeyPress, this);
40219         }
40220         this.fireEvent('collapse', this);
40221     },
40222
40223     // private
40224     collapseIf : function(e){
40225         if(!e.within(this.wrap) && !e.within(this.list)){
40226             this.collapse();
40227         }
40228     },
40229
40230     /**
40231      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40232      */
40233     expand : function(){
40234         if(this.isExpanded() || !this.hasFocus){
40235             return;
40236         }
40237         this.list.alignTo(this.el, this.listAlign);
40238         this.list.show();
40239         Roo.get(document).on('mousedown', this.collapseIf, this);
40240         Roo.get(document).on('mousewheel', this.collapseIf, this);
40241         if (!this.editable) {
40242             Roo.get(document).on('keydown', this.listKeyPress, this);
40243         }
40244         
40245         this.fireEvent('expand', this);
40246     },
40247
40248     // private
40249     // Implements the default empty TriggerField.onTriggerClick function
40250     onTriggerClick : function(){
40251         if(this.disabled){
40252             return;
40253         }
40254         if(this.isExpanded()){
40255             this.collapse();
40256             if (!this.blockFocus) {
40257                 this.el.focus();
40258             }
40259             
40260         }else {
40261             this.hasFocus = true;
40262             if(this.triggerAction == 'all') {
40263                 this.doQuery(this.allQuery, true);
40264             } else {
40265                 this.doQuery(this.getRawValue());
40266             }
40267             if (!this.blockFocus) {
40268                 this.el.focus();
40269             }
40270         }
40271     },
40272     listKeyPress : function(e)
40273     {
40274         //Roo.log('listkeypress');
40275         // scroll to first matching element based on key pres..
40276         if (e.isSpecialKey()) {
40277             return false;
40278         }
40279         var k = String.fromCharCode(e.getKey()).toUpperCase();
40280         //Roo.log(k);
40281         var match  = false;
40282         var csel = this.view.getSelectedNodes();
40283         var cselitem = false;
40284         if (csel.length) {
40285             var ix = this.view.indexOf(csel[0]);
40286             cselitem  = this.store.getAt(ix);
40287             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40288                 cselitem = false;
40289             }
40290             
40291         }
40292         
40293         this.store.each(function(v) { 
40294             if (cselitem) {
40295                 // start at existing selection.
40296                 if (cselitem.id == v.id) {
40297                     cselitem = false;
40298                 }
40299                 return;
40300             }
40301                 
40302             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40303                 match = this.store.indexOf(v);
40304                 return false;
40305             }
40306         }, this);
40307         
40308         if (match === false) {
40309             return true; // no more action?
40310         }
40311         // scroll to?
40312         this.view.select(match);
40313         var sn = Roo.get(this.view.getSelectedNodes()[0])
40314         sn.scrollIntoView(sn.dom.parentNode, false);
40315     }
40316
40317     /** 
40318     * @cfg {Boolean} grow 
40319     * @hide 
40320     */
40321     /** 
40322     * @cfg {Number} growMin 
40323     * @hide 
40324     */
40325     /** 
40326     * @cfg {Number} growMax 
40327     * @hide 
40328     */
40329     /**
40330      * @hide
40331      * @method autoSize
40332      */
40333 });/*
40334  * Copyright(c) 2010-2012, Roo J Solutions Limited
40335  *
40336  * Licence LGPL
40337  *
40338  */
40339
40340 /**
40341  * @class Roo.form.ComboBoxArray
40342  * @extends Roo.form.TextField
40343  * A facebook style adder... for lists of email / people / countries  etc...
40344  * pick multiple items from a combo box, and shows each one.
40345  *
40346  *  Fred [x]  Brian [x]  [Pick another |v]
40347  *
40348  *
40349  *  For this to work: it needs various extra information
40350  *    - normal combo problay has
40351  *      name, hiddenName
40352  *    + displayField, valueField
40353  *
40354  *    For our purpose...
40355  *
40356  *
40357  *   If we change from 'extends' to wrapping...
40358  *   
40359  *  
40360  *
40361  
40362  
40363  * @constructor
40364  * Create a new ComboBoxArray.
40365  * @param {Object} config Configuration options
40366  */
40367  
40368
40369 Roo.form.ComboBoxArray = function(config)
40370 {
40371     this.addEvents({
40372         /**
40373          * @event beforeremove
40374          * Fires before remove the value from the list
40375              * @param {Roo.form.ComboBoxArray} _self This combo box array
40376              * @param {Roo.form.ComboBoxArray.Item} item removed item
40377              */
40378         'beforeremove' : true,
40379         /**
40380          * @event remove
40381          * Fires when remove the value from the list
40382              * @param {Roo.form.ComboBoxArray} _self This combo box array
40383              * @param {Roo.form.ComboBoxArray.Item} item removed item
40384              */
40385         'remove' : true
40386         
40387         
40388     });
40389     
40390     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40391     
40392     this.items = new Roo.util.MixedCollection(false);
40393     
40394     // construct the child combo...
40395     
40396     
40397     
40398     
40399    
40400     
40401 }
40402
40403  
40404 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40405
40406     /**
40407      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40408      */
40409     
40410     lastData : false,
40411     
40412     // behavies liek a hiddne field
40413     inputType:      'hidden',
40414     /**
40415      * @cfg {Number} width The width of the box that displays the selected element
40416      */ 
40417     width:          300,
40418
40419     
40420     
40421     /**
40422      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40423      */
40424     name : false,
40425     /**
40426      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40427      */
40428     hiddenName : false,
40429     
40430     
40431     // private the array of items that are displayed..
40432     items  : false,
40433     // private - the hidden field el.
40434     hiddenEl : false,
40435     // private - the filed el..
40436     el : false,
40437     
40438     //validateValue : function() { return true; }, // all values are ok!
40439     //onAddClick: function() { },
40440     
40441     onRender : function(ct, position) 
40442     {
40443         
40444         // create the standard hidden element
40445         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40446         
40447         
40448         // give fake names to child combo;
40449         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40450         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40451         
40452         this.combo = Roo.factory(this.combo, Roo.form);
40453         this.combo.onRender(ct, position);
40454         if (typeof(this.combo.width) != 'undefined') {
40455             this.combo.onResize(this.combo.width,0);
40456         }
40457         
40458         this.combo.initEvents();
40459         
40460         // assigned so form know we need to do this..
40461         this.store          = this.combo.store;
40462         this.valueField     = this.combo.valueField;
40463         this.displayField   = this.combo.displayField ;
40464         
40465         
40466         this.combo.wrap.addClass('x-cbarray-grp');
40467         
40468         var cbwrap = this.combo.wrap.createChild(
40469             {tag: 'div', cls: 'x-cbarray-cb'},
40470             this.combo.el.dom
40471         );
40472         
40473              
40474         this.hiddenEl = this.combo.wrap.createChild({
40475             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40476         });
40477         this.el = this.combo.wrap.createChild({
40478             tag: 'input',  type:'hidden' , name: this.name, value : ''
40479         });
40480          //   this.el.dom.removeAttribute("name");
40481         
40482         
40483         this.outerWrap = this.combo.wrap;
40484         this.wrap = cbwrap;
40485         
40486         this.outerWrap.setWidth(this.width);
40487         this.outerWrap.dom.removeChild(this.el.dom);
40488         
40489         this.wrap.dom.appendChild(this.el.dom);
40490         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40491         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40492         
40493         this.combo.trigger.setStyle('position','relative');
40494         this.combo.trigger.setStyle('left', '0px');
40495         this.combo.trigger.setStyle('top', '2px');
40496         
40497         this.combo.el.setStyle('vertical-align', 'text-bottom');
40498         
40499         //this.trigger.setStyle('vertical-align', 'top');
40500         
40501         // this should use the code from combo really... on('add' ....)
40502         if (this.adder) {
40503             
40504         
40505             this.adder = this.outerWrap.createChild(
40506                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40507             var _t = this;
40508             this.adder.on('click', function(e) {
40509                 _t.fireEvent('adderclick', this, e);
40510             }, _t);
40511         }
40512         //var _t = this;
40513         //this.adder.on('click', this.onAddClick, _t);
40514         
40515         
40516         this.combo.on('select', function(cb, rec, ix) {
40517             this.addItem(rec.data);
40518             
40519             cb.setValue('');
40520             cb.el.dom.value = '';
40521             //cb.lastData = rec.data;
40522             // add to list
40523             
40524         }, this);
40525         
40526         
40527     },
40528     
40529     
40530     getName: function()
40531     {
40532         // returns hidden if it's set..
40533         if (!this.rendered) {return ''};
40534         return  this.hiddenName ? this.hiddenName : this.name;
40535         
40536     },
40537     
40538     
40539     onResize: function(w, h){
40540         
40541         return;
40542         // not sure if this is needed..
40543         //this.combo.onResize(w,h);
40544         
40545         if(typeof w != 'number'){
40546             // we do not handle it!?!?
40547             return;
40548         }
40549         var tw = this.combo.trigger.getWidth();
40550         tw += this.addicon ? this.addicon.getWidth() : 0;
40551         tw += this.editicon ? this.editicon.getWidth() : 0;
40552         var x = w - tw;
40553         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40554             
40555         this.combo.trigger.setStyle('left', '0px');
40556         
40557         if(this.list && this.listWidth === undefined){
40558             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40559             this.list.setWidth(lw);
40560             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40561         }
40562         
40563     
40564         
40565     },
40566     
40567     addItem: function(rec)
40568     {
40569         var valueField = this.combo.valueField;
40570         var displayField = this.combo.displayField;
40571         if (this.items.indexOfKey(rec[valueField]) > -1) {
40572             //console.log("GOT " + rec.data.id);
40573             return;
40574         }
40575         
40576         var x = new Roo.form.ComboBoxArray.Item({
40577             //id : rec[this.idField],
40578             data : rec,
40579             displayField : displayField ,
40580             tipField : displayField ,
40581             cb : this
40582         });
40583         // use the 
40584         this.items.add(rec[valueField],x);
40585         // add it before the element..
40586         this.updateHiddenEl();
40587         x.render(this.outerWrap, this.wrap.dom);
40588         // add the image handler..
40589     },
40590     
40591     updateHiddenEl : function()
40592     {
40593         this.validate();
40594         if (!this.hiddenEl) {
40595             return;
40596         }
40597         var ar = [];
40598         var idField = this.combo.valueField;
40599         
40600         this.items.each(function(f) {
40601             ar.push(f.data[idField]);
40602            
40603         });
40604         this.hiddenEl.dom.value = ar.join(',');
40605         this.validate();
40606     },
40607     
40608     reset : function()
40609     {
40610         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40611         this.items.each(function(f) {
40612            f.remove(); 
40613         });
40614         this.el.dom.value = '';
40615         if (this.hiddenEl) {
40616             this.hiddenEl.dom.value = '';
40617         }
40618         
40619     },
40620     getValue: function()
40621     {
40622         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40623     },
40624     setValue: function(v) // not a valid action - must use addItems..
40625     {
40626          
40627         this.reset();
40628         
40629         
40630         
40631         if (this.store.isLocal && (typeof(v) == 'string')) {
40632             // then we can use the store to find the values..
40633             // comma seperated at present.. this needs to allow JSON based encoding..
40634             this.hiddenEl.value  = v;
40635             var v_ar = [];
40636             Roo.each(v.split(','), function(k) {
40637                 Roo.log("CHECK " + this.valueField + ',' + k);
40638                 var li = this.store.query(this.valueField, k);
40639                 if (!li.length) {
40640                     return;
40641                 }
40642                 var add = {};
40643                 add[this.valueField] = k;
40644                 add[this.displayField] = li.item(0).data[this.displayField];
40645                 
40646                 this.addItem(add);
40647             }, this) 
40648              
40649         }
40650         if (typeof(v) == 'object' ) {
40651             // then let's assume it's an array of objects..
40652             Roo.each(v, function(l) {
40653                 this.addItem(l);
40654             }, this);
40655              
40656         }
40657         
40658         
40659     },
40660     setFromData: function(v)
40661     {
40662         // this recieves an object, if setValues is called.
40663         this.reset();
40664         this.el.dom.value = v[this.displayField];
40665         this.hiddenEl.dom.value = v[this.valueField];
40666         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40667             return;
40668         }
40669         var kv = v[this.valueField];
40670         var dv = v[this.displayField];
40671         kv = typeof(kv) != 'string' ? '' : kv;
40672         dv = typeof(dv) != 'string' ? '' : dv;
40673         
40674         
40675         var keys = kv.split(',');
40676         var display = dv.split(',');
40677         for (var i = 0 ; i < keys.length; i++) {
40678             
40679             add = {};
40680             add[this.valueField] = keys[i];
40681             add[this.displayField] = display[i];
40682             this.addItem(add);
40683         }
40684       
40685         
40686     },
40687     
40688     /**
40689      * Validates the combox array value
40690      * @return {Boolean} True if the value is valid, else false
40691      */
40692     validate : function(){
40693         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40694             this.clearInvalid();
40695             return true;
40696         }
40697         return false;
40698     },
40699     
40700     validateValue : function(value){
40701         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40702         
40703     },
40704     
40705     /*@
40706      * overide
40707      * 
40708      */
40709     isDirty : function() {
40710         if(this.disabled) {
40711             return false;
40712         }
40713         
40714         try {
40715             var d = Roo.decode(String(this.originalValue));
40716         } catch (e) {
40717             return String(this.getValue()) !== String(this.originalValue);
40718         }
40719         
40720         var originalValue = [];
40721         
40722         for (var i = 0; i < d.length; i++){
40723             originalValue.push(d[i][this.valueField]);
40724         }
40725         
40726         return String(this.getValue()) !== String(originalValue.join(','));
40727         
40728     }
40729     
40730 });
40731
40732
40733
40734 /**
40735  * @class Roo.form.ComboBoxArray.Item
40736  * @extends Roo.BoxComponent
40737  * A selected item in the list
40738  *  Fred [x]  Brian [x]  [Pick another |v]
40739  * 
40740  * @constructor
40741  * Create a new item.
40742  * @param {Object} config Configuration options
40743  */
40744  
40745 Roo.form.ComboBoxArray.Item = function(config) {
40746     config.id = Roo.id();
40747     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40748 }
40749
40750 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40751     data : {},
40752     cb: false,
40753     displayField : false,
40754     tipField : false,
40755     
40756     
40757     defaultAutoCreate : {
40758         tag: 'div',
40759         cls: 'x-cbarray-item',
40760         cn : [ 
40761             { tag: 'div' },
40762             {
40763                 tag: 'img',
40764                 width:16,
40765                 height : 16,
40766                 src : Roo.BLANK_IMAGE_URL ,
40767                 align: 'center'
40768             }
40769         ]
40770         
40771     },
40772     
40773  
40774     onRender : function(ct, position)
40775     {
40776         Roo.form.Field.superclass.onRender.call(this, ct, position);
40777         
40778         if(!this.el){
40779             var cfg = this.getAutoCreate();
40780             this.el = ct.createChild(cfg, position);
40781         }
40782         
40783         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40784         
40785         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40786             this.cb.renderer(this.data) :
40787             String.format('{0}',this.data[this.displayField]);
40788         
40789             
40790         this.el.child('div').dom.setAttribute('qtip',
40791                         String.format('{0}',this.data[this.tipField])
40792         );
40793         
40794         this.el.child('img').on('click', this.remove, this);
40795         
40796     },
40797    
40798     remove : function()
40799     {
40800         if(this.cb.disabled){
40801             return;
40802         }
40803         
40804         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40805             this.cb.items.remove(this);
40806             this.el.child('img').un('click', this.remove, this);
40807             this.el.remove();
40808             this.cb.updateHiddenEl();
40809
40810             this.cb.fireEvent('remove', this.cb, this);
40811         }
40812         
40813     }
40814 });/*
40815  * Based on:
40816  * Ext JS Library 1.1.1
40817  * Copyright(c) 2006-2007, Ext JS, LLC.
40818  *
40819  * Originally Released Under LGPL - original licence link has changed is not relivant.
40820  *
40821  * Fork - LGPL
40822  * <script type="text/javascript">
40823  */
40824 /**
40825  * @class Roo.form.Checkbox
40826  * @extends Roo.form.Field
40827  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40828  * @constructor
40829  * Creates a new Checkbox
40830  * @param {Object} config Configuration options
40831  */
40832 Roo.form.Checkbox = function(config){
40833     Roo.form.Checkbox.superclass.constructor.call(this, config);
40834     this.addEvents({
40835         /**
40836          * @event check
40837          * Fires when the checkbox is checked or unchecked.
40838              * @param {Roo.form.Checkbox} this This checkbox
40839              * @param {Boolean} checked The new checked value
40840              */
40841         check : true
40842     });
40843 };
40844
40845 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40846     /**
40847      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40848      */
40849     focusClass : undefined,
40850     /**
40851      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40852      */
40853     fieldClass: "x-form-field",
40854     /**
40855      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40856      */
40857     checked: false,
40858     /**
40859      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40860      * {tag: "input", type: "checkbox", autocomplete: "off"})
40861      */
40862     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40863     /**
40864      * @cfg {String} boxLabel The text that appears beside the checkbox
40865      */
40866     boxLabel : "",
40867     /**
40868      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40869      */  
40870     inputValue : '1',
40871     /**
40872      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40873      */
40874      valueOff: '0', // value when not checked..
40875
40876     actionMode : 'viewEl', 
40877     //
40878     // private
40879     itemCls : 'x-menu-check-item x-form-item',
40880     groupClass : 'x-menu-group-item',
40881     inputType : 'hidden',
40882     
40883     
40884     inSetChecked: false, // check that we are not calling self...
40885     
40886     inputElement: false, // real input element?
40887     basedOn: false, // ????
40888     
40889     isFormField: true, // not sure where this is needed!!!!
40890
40891     onResize : function(){
40892         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40893         if(!this.boxLabel){
40894             this.el.alignTo(this.wrap, 'c-c');
40895         }
40896     },
40897
40898     initEvents : function(){
40899         Roo.form.Checkbox.superclass.initEvents.call(this);
40900         this.el.on("click", this.onClick,  this);
40901         this.el.on("change", this.onClick,  this);
40902     },
40903
40904
40905     getResizeEl : function(){
40906         return this.wrap;
40907     },
40908
40909     getPositionEl : function(){
40910         return this.wrap;
40911     },
40912
40913     // private
40914     onRender : function(ct, position){
40915         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40916         /*
40917         if(this.inputValue !== undefined){
40918             this.el.dom.value = this.inputValue;
40919         }
40920         */
40921         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40922         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40923         var viewEl = this.wrap.createChild({ 
40924             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40925         this.viewEl = viewEl;   
40926         this.wrap.on('click', this.onClick,  this); 
40927         
40928         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40929         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40930         
40931         
40932         
40933         if(this.boxLabel){
40934             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40935         //    viewEl.on('click', this.onClick,  this); 
40936         }
40937         //if(this.checked){
40938             this.setChecked(this.checked);
40939         //}else{
40940             //this.checked = this.el.dom;
40941         //}
40942
40943     },
40944
40945     // private
40946     initValue : Roo.emptyFn,
40947
40948     /**
40949      * Returns the checked state of the checkbox.
40950      * @return {Boolean} True if checked, else false
40951      */
40952     getValue : function(){
40953         if(this.el){
40954             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40955         }
40956         return this.valueOff;
40957         
40958     },
40959
40960         // private
40961     onClick : function(){ 
40962         if (this.disabled) {
40963             return;
40964         }
40965         this.setChecked(!this.checked);
40966
40967         //if(this.el.dom.checked != this.checked){
40968         //    this.setValue(this.el.dom.checked);
40969        // }
40970     },
40971
40972     /**
40973      * Sets the checked state of the checkbox.
40974      * On is always based on a string comparison between inputValue and the param.
40975      * @param {Boolean/String} value - the value to set 
40976      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40977      */
40978     setValue : function(v,suppressEvent){
40979         
40980         
40981         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40982         //if(this.el && this.el.dom){
40983         //    this.el.dom.checked = this.checked;
40984         //    this.el.dom.defaultChecked = this.checked;
40985         //}
40986         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40987         //this.fireEvent("check", this, this.checked);
40988     },
40989     // private..
40990     setChecked : function(state,suppressEvent)
40991     {
40992         if (this.inSetChecked) {
40993             this.checked = state;
40994             return;
40995         }
40996         
40997     
40998         if(this.wrap){
40999             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41000         }
41001         this.checked = state;
41002         if(suppressEvent !== true){
41003             this.fireEvent('check', this, state);
41004         }
41005         this.inSetChecked = true;
41006         this.el.dom.value = state ? this.inputValue : this.valueOff;
41007         this.inSetChecked = false;
41008         
41009     },
41010     // handle setting of hidden value by some other method!!?!?
41011     setFromHidden: function()
41012     {
41013         if(!this.el){
41014             return;
41015         }
41016         //console.log("SET FROM HIDDEN");
41017         //alert('setFrom hidden');
41018         this.setValue(this.el.dom.value);
41019     },
41020     
41021     onDestroy : function()
41022     {
41023         if(this.viewEl){
41024             Roo.get(this.viewEl).remove();
41025         }
41026          
41027         Roo.form.Checkbox.superclass.onDestroy.call(this);
41028     }
41029
41030 });/*
41031  * Based on:
41032  * Ext JS Library 1.1.1
41033  * Copyright(c) 2006-2007, Ext JS, LLC.
41034  *
41035  * Originally Released Under LGPL - original licence link has changed is not relivant.
41036  *
41037  * Fork - LGPL
41038  * <script type="text/javascript">
41039  */
41040  
41041 /**
41042  * @class Roo.form.Radio
41043  * @extends Roo.form.Checkbox
41044  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41045  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41046  * @constructor
41047  * Creates a new Radio
41048  * @param {Object} config Configuration options
41049  */
41050 Roo.form.Radio = function(){
41051     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41052 };
41053 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41054     inputType: 'radio',
41055
41056     /**
41057      * If this radio is part of a group, it will return the selected value
41058      * @return {String}
41059      */
41060     getGroupValue : function(){
41061         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41062     },
41063     
41064     
41065     onRender : function(ct, position){
41066         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41067         
41068         if(this.inputValue !== undefined){
41069             this.el.dom.value = this.inputValue;
41070         }
41071          
41072         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41073         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41074         //var viewEl = this.wrap.createChild({ 
41075         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41076         //this.viewEl = viewEl;   
41077         //this.wrap.on('click', this.onClick,  this); 
41078         
41079         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41080         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41081         
41082         
41083         
41084         if(this.boxLabel){
41085             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41086         //    viewEl.on('click', this.onClick,  this); 
41087         }
41088          if(this.checked){
41089             this.el.dom.checked =   'checked' ;
41090         }
41091          
41092     } 
41093     
41094     
41095 });//<script type="text/javascript">
41096
41097 /*
41098  * Based  Ext JS Library 1.1.1
41099  * Copyright(c) 2006-2007, Ext JS, LLC.
41100  * LGPL
41101  *
41102  */
41103  
41104 /**
41105  * @class Roo.HtmlEditorCore
41106  * @extends Roo.Component
41107  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41108  *
41109  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41110  */
41111
41112 Roo.HtmlEditorCore = function(config){
41113     
41114     
41115     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41116     
41117     
41118     this.addEvents({
41119         /**
41120          * @event initialize
41121          * Fires when the editor is fully initialized (including the iframe)
41122          * @param {Roo.HtmlEditorCore} this
41123          */
41124         initialize: true,
41125         /**
41126          * @event activate
41127          * Fires when the editor is first receives the focus. Any insertion must wait
41128          * until after this event.
41129          * @param {Roo.HtmlEditorCore} this
41130          */
41131         activate: true,
41132          /**
41133          * @event beforesync
41134          * Fires before the textarea is updated with content from the editor iframe. Return false
41135          * to cancel the sync.
41136          * @param {Roo.HtmlEditorCore} this
41137          * @param {String} html
41138          */
41139         beforesync: true,
41140          /**
41141          * @event beforepush
41142          * Fires before the iframe editor is updated with content from the textarea. Return false
41143          * to cancel the push.
41144          * @param {Roo.HtmlEditorCore} this
41145          * @param {String} html
41146          */
41147         beforepush: true,
41148          /**
41149          * @event sync
41150          * Fires when the textarea is updated with content from the editor iframe.
41151          * @param {Roo.HtmlEditorCore} this
41152          * @param {String} html
41153          */
41154         sync: true,
41155          /**
41156          * @event push
41157          * Fires when the iframe editor is updated with content from the textarea.
41158          * @param {Roo.HtmlEditorCore} this
41159          * @param {String} html
41160          */
41161         push: true,
41162         
41163         /**
41164          * @event editorevent
41165          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41166          * @param {Roo.HtmlEditorCore} this
41167          */
41168         editorevent: true
41169         
41170     });
41171     
41172     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41173     
41174     // defaults : white / black...
41175     this.applyBlacklists();
41176     
41177     
41178     
41179 };
41180
41181
41182 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41183
41184
41185      /**
41186      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41187      */
41188     
41189     owner : false,
41190     
41191      /**
41192      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41193      *                        Roo.resizable.
41194      */
41195     resizable : false,
41196      /**
41197      * @cfg {Number} height (in pixels)
41198      */   
41199     height: 300,
41200    /**
41201      * @cfg {Number} width (in pixels)
41202      */   
41203     width: 500,
41204     
41205     /**
41206      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41207      * 
41208      */
41209     stylesheets: false,
41210     
41211     // id of frame..
41212     frameId: false,
41213     
41214     // private properties
41215     validationEvent : false,
41216     deferHeight: true,
41217     initialized : false,
41218     activated : false,
41219     sourceEditMode : false,
41220     onFocus : Roo.emptyFn,
41221     iframePad:3,
41222     hideMode:'offsets',
41223     
41224     clearUp: true,
41225     
41226     // blacklist + whitelisted elements..
41227     black: false,
41228     white: false,
41229      
41230     
41231
41232     /**
41233      * Protected method that will not generally be called directly. It
41234      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41235      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41236      */
41237     getDocMarkup : function(){
41238         // body styles..
41239         var st = '';
41240         
41241         // inherit styels from page...?? 
41242         if (this.stylesheets === false) {
41243             
41244             Roo.get(document.head).select('style').each(function(node) {
41245                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41246             });
41247             
41248             Roo.get(document.head).select('link').each(function(node) { 
41249                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41250             });
41251             
41252         } else if (!this.stylesheets.length) {
41253                 // simple..
41254                 st = '<style type="text/css">' +
41255                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41256                    '</style>';
41257         } else { 
41258             
41259         }
41260         
41261         st +=  '<style type="text/css">' +
41262             'IMG { cursor: pointer } ' +
41263         '</style>';
41264
41265         
41266         return '<html><head>' + st  +
41267             //<style type="text/css">' +
41268             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41269             //'</style>' +
41270             ' </head><body class="roo-htmleditor-body"></body></html>';
41271     },
41272
41273     // private
41274     onRender : function(ct, position)
41275     {
41276         var _t = this;
41277         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41278         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41279         
41280         
41281         this.el.dom.style.border = '0 none';
41282         this.el.dom.setAttribute('tabIndex', -1);
41283         this.el.addClass('x-hidden hide');
41284         
41285         
41286         
41287         if(Roo.isIE){ // fix IE 1px bogus margin
41288             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41289         }
41290        
41291         
41292         this.frameId = Roo.id();
41293         
41294          
41295         
41296         var iframe = this.owner.wrap.createChild({
41297             tag: 'iframe',
41298             cls: 'form-control', // bootstrap..
41299             id: this.frameId,
41300             name: this.frameId,
41301             frameBorder : 'no',
41302             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41303         }, this.el
41304         );
41305         
41306         
41307         this.iframe = iframe.dom;
41308
41309          this.assignDocWin();
41310         
41311         this.doc.designMode = 'on';
41312        
41313         this.doc.open();
41314         this.doc.write(this.getDocMarkup());
41315         this.doc.close();
41316
41317         
41318         var task = { // must defer to wait for browser to be ready
41319             run : function(){
41320                 //console.log("run task?" + this.doc.readyState);
41321                 this.assignDocWin();
41322                 if(this.doc.body || this.doc.readyState == 'complete'){
41323                     try {
41324                         this.doc.designMode="on";
41325                     } catch (e) {
41326                         return;
41327                     }
41328                     Roo.TaskMgr.stop(task);
41329                     this.initEditor.defer(10, this);
41330                 }
41331             },
41332             interval : 10,
41333             duration: 10000,
41334             scope: this
41335         };
41336         Roo.TaskMgr.start(task);
41337
41338     },
41339
41340     // private
41341     onResize : function(w, h)
41342     {
41343          Roo.log('resize: ' +w + ',' + h );
41344         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41345         if(!this.iframe){
41346             return;
41347         }
41348         if(typeof w == 'number'){
41349             
41350             this.iframe.style.width = w + 'px';
41351         }
41352         if(typeof h == 'number'){
41353             
41354             this.iframe.style.height = h + 'px';
41355             if(this.doc){
41356                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41357             }
41358         }
41359         
41360     },
41361
41362     /**
41363      * Toggles the editor between standard and source edit mode.
41364      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41365      */
41366     toggleSourceEdit : function(sourceEditMode){
41367         
41368         this.sourceEditMode = sourceEditMode === true;
41369         
41370         if(this.sourceEditMode){
41371  
41372             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41373             
41374         }else{
41375             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41376             //this.iframe.className = '';
41377             this.deferFocus();
41378         }
41379         //this.setSize(this.owner.wrap.getSize());
41380         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41381     },
41382
41383     
41384   
41385
41386     /**
41387      * Protected method that will not generally be called directly. If you need/want
41388      * custom HTML cleanup, this is the method you should override.
41389      * @param {String} html The HTML to be cleaned
41390      * return {String} The cleaned HTML
41391      */
41392     cleanHtml : function(html){
41393         html = String(html);
41394         if(html.length > 5){
41395             if(Roo.isSafari){ // strip safari nonsense
41396                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41397             }
41398         }
41399         if(html == '&nbsp;'){
41400             html = '';
41401         }
41402         return html;
41403     },
41404
41405     /**
41406      * HTML Editor -> Textarea
41407      * Protected method that will not generally be called directly. Syncs the contents
41408      * of the editor iframe with the textarea.
41409      */
41410     syncValue : function(){
41411         if(this.initialized){
41412             var bd = (this.doc.body || this.doc.documentElement);
41413             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41414             var html = bd.innerHTML;
41415             if(Roo.isSafari){
41416                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41417                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41418                 if(m && m[1]){
41419                     html = '<div style="'+m[0]+'">' + html + '</div>';
41420                 }
41421             }
41422             html = this.cleanHtml(html);
41423             // fix up the special chars.. normaly like back quotes in word...
41424             // however we do not want to do this with chinese..
41425             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41426                 var cc = b.charCodeAt();
41427                 if (
41428                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41429                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41430                     (cc >= 0xf900 && cc < 0xfb00 )
41431                 ) {
41432                         return b;
41433                 }
41434                 return "&#"+cc+";" 
41435             });
41436             if(this.owner.fireEvent('beforesync', this, html) !== false){
41437                 this.el.dom.value = html;
41438                 this.owner.fireEvent('sync', this, html);
41439             }
41440         }
41441     },
41442
41443     /**
41444      * Protected method that will not generally be called directly. Pushes the value of the textarea
41445      * into the iframe editor.
41446      */
41447     pushValue : function(){
41448         if(this.initialized){
41449             var v = this.el.dom.value.trim();
41450             
41451 //            if(v.length < 1){
41452 //                v = '&#160;';
41453 //            }
41454             
41455             if(this.owner.fireEvent('beforepush', this, v) !== false){
41456                 var d = (this.doc.body || this.doc.documentElement);
41457                 d.innerHTML = v;
41458                 this.cleanUpPaste();
41459                 this.el.dom.value = d.innerHTML;
41460                 this.owner.fireEvent('push', this, v);
41461             }
41462         }
41463     },
41464
41465     // private
41466     deferFocus : function(){
41467         this.focus.defer(10, this);
41468     },
41469
41470     // doc'ed in Field
41471     focus : function(){
41472         if(this.win && !this.sourceEditMode){
41473             this.win.focus();
41474         }else{
41475             this.el.focus();
41476         }
41477     },
41478     
41479     assignDocWin: function()
41480     {
41481         var iframe = this.iframe;
41482         
41483          if(Roo.isIE){
41484             this.doc = iframe.contentWindow.document;
41485             this.win = iframe.contentWindow;
41486         } else {
41487 //            if (!Roo.get(this.frameId)) {
41488 //                return;
41489 //            }
41490 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41491 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41492             
41493             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41494                 return;
41495             }
41496             
41497             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41498             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41499         }
41500     },
41501     
41502     // private
41503     initEditor : function(){
41504         //console.log("INIT EDITOR");
41505         this.assignDocWin();
41506         
41507         
41508         
41509         this.doc.designMode="on";
41510         this.doc.open();
41511         this.doc.write(this.getDocMarkup());
41512         this.doc.close();
41513         
41514         var dbody = (this.doc.body || this.doc.documentElement);
41515         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41516         // this copies styles from the containing element into thsi one..
41517         // not sure why we need all of this..
41518         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41519         
41520         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41521         //ss['background-attachment'] = 'fixed'; // w3c
41522         dbody.bgProperties = 'fixed'; // ie
41523         //Roo.DomHelper.applyStyles(dbody, ss);
41524         Roo.EventManager.on(this.doc, {
41525             //'mousedown': this.onEditorEvent,
41526             'mouseup': this.onEditorEvent,
41527             'dblclick': this.onEditorEvent,
41528             'click': this.onEditorEvent,
41529             'keyup': this.onEditorEvent,
41530             buffer:100,
41531             scope: this
41532         });
41533         if(Roo.isGecko){
41534             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41535         }
41536         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41537             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41538         }
41539         this.initialized = true;
41540
41541         this.owner.fireEvent('initialize', this);
41542         this.pushValue();
41543     },
41544
41545     // private
41546     onDestroy : function(){
41547         
41548         
41549         
41550         if(this.rendered){
41551             
41552             //for (var i =0; i < this.toolbars.length;i++) {
41553             //    // fixme - ask toolbars for heights?
41554             //    this.toolbars[i].onDestroy();
41555            // }
41556             
41557             //this.wrap.dom.innerHTML = '';
41558             //this.wrap.remove();
41559         }
41560     },
41561
41562     // private
41563     onFirstFocus : function(){
41564         
41565         this.assignDocWin();
41566         
41567         
41568         this.activated = true;
41569          
41570     
41571         if(Roo.isGecko){ // prevent silly gecko errors
41572             this.win.focus();
41573             var s = this.win.getSelection();
41574             if(!s.focusNode || s.focusNode.nodeType != 3){
41575                 var r = s.getRangeAt(0);
41576                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41577                 r.collapse(true);
41578                 this.deferFocus();
41579             }
41580             try{
41581                 this.execCmd('useCSS', true);
41582                 this.execCmd('styleWithCSS', false);
41583             }catch(e){}
41584         }
41585         this.owner.fireEvent('activate', this);
41586     },
41587
41588     // private
41589     adjustFont: function(btn){
41590         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41591         //if(Roo.isSafari){ // safari
41592         //    adjust *= 2;
41593        // }
41594         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41595         if(Roo.isSafari){ // safari
41596             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41597             v =  (v < 10) ? 10 : v;
41598             v =  (v > 48) ? 48 : v;
41599             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41600             
41601         }
41602         
41603         
41604         v = Math.max(1, v+adjust);
41605         
41606         this.execCmd('FontSize', v  );
41607     },
41608
41609     onEditorEvent : function(e){
41610         this.owner.fireEvent('editorevent', this, e);
41611       //  this.updateToolbar();
41612         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41613     },
41614
41615     insertTag : function(tg)
41616     {
41617         // could be a bit smarter... -> wrap the current selected tRoo..
41618         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41619             
41620             range = this.createRange(this.getSelection());
41621             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41622             wrappingNode.appendChild(range.extractContents());
41623             range.insertNode(wrappingNode);
41624
41625             return;
41626             
41627             
41628             
41629         }
41630         this.execCmd("formatblock",   tg);
41631         
41632     },
41633     
41634     insertText : function(txt)
41635     {
41636         
41637         
41638         var range = this.createRange();
41639         range.deleteContents();
41640                //alert(Sender.getAttribute('label'));
41641                
41642         range.insertNode(this.doc.createTextNode(txt));
41643     } ,
41644     
41645      
41646
41647     /**
41648      * Executes a Midas editor command on the editor document and performs necessary focus and
41649      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41650      * @param {String} cmd The Midas command
41651      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41652      */
41653     relayCmd : function(cmd, value){
41654         this.win.focus();
41655         this.execCmd(cmd, value);
41656         this.owner.fireEvent('editorevent', this);
41657         //this.updateToolbar();
41658         this.owner.deferFocus();
41659     },
41660
41661     /**
41662      * Executes a Midas editor command directly on the editor document.
41663      * For visual commands, you should use {@link #relayCmd} instead.
41664      * <b>This should only be called after the editor is initialized.</b>
41665      * @param {String} cmd The Midas command
41666      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41667      */
41668     execCmd : function(cmd, value){
41669         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41670         this.syncValue();
41671     },
41672  
41673  
41674    
41675     /**
41676      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41677      * to insert tRoo.
41678      * @param {String} text | dom node.. 
41679      */
41680     insertAtCursor : function(text)
41681     {
41682         
41683         
41684         
41685         if(!this.activated){
41686             return;
41687         }
41688         /*
41689         if(Roo.isIE){
41690             this.win.focus();
41691             var r = this.doc.selection.createRange();
41692             if(r){
41693                 r.collapse(true);
41694                 r.pasteHTML(text);
41695                 this.syncValue();
41696                 this.deferFocus();
41697             
41698             }
41699             return;
41700         }
41701         */
41702         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41703             this.win.focus();
41704             
41705             
41706             // from jquery ui (MIT licenced)
41707             var range, node;
41708             var win = this.win;
41709             
41710             if (win.getSelection && win.getSelection().getRangeAt) {
41711                 range = win.getSelection().getRangeAt(0);
41712                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41713                 range.insertNode(node);
41714             } else if (win.document.selection && win.document.selection.createRange) {
41715                 // no firefox support
41716                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41717                 win.document.selection.createRange().pasteHTML(txt);
41718             } else {
41719                 // no firefox support
41720                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41721                 this.execCmd('InsertHTML', txt);
41722             } 
41723             
41724             this.syncValue();
41725             
41726             this.deferFocus();
41727         }
41728     },
41729  // private
41730     mozKeyPress : function(e){
41731         if(e.ctrlKey){
41732             var c = e.getCharCode(), cmd;
41733           
41734             if(c > 0){
41735                 c = String.fromCharCode(c).toLowerCase();
41736                 switch(c){
41737                     case 'b':
41738                         cmd = 'bold';
41739                         break;
41740                     case 'i':
41741                         cmd = 'italic';
41742                         break;
41743                     
41744                     case 'u':
41745                         cmd = 'underline';
41746                         break;
41747                     
41748                     case 'v':
41749                         this.cleanUpPaste.defer(100, this);
41750                         return;
41751                         
41752                 }
41753                 if(cmd){
41754                     this.win.focus();
41755                     this.execCmd(cmd);
41756                     this.deferFocus();
41757                     e.preventDefault();
41758                 }
41759                 
41760             }
41761         }
41762     },
41763
41764     // private
41765     fixKeys : function(){ // load time branching for fastest keydown performance
41766         if(Roo.isIE){
41767             return function(e){
41768                 var k = e.getKey(), r;
41769                 if(k == e.TAB){
41770                     e.stopEvent();
41771                     r = this.doc.selection.createRange();
41772                     if(r){
41773                         r.collapse(true);
41774                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41775                         this.deferFocus();
41776                     }
41777                     return;
41778                 }
41779                 
41780                 if(k == e.ENTER){
41781                     r = this.doc.selection.createRange();
41782                     if(r){
41783                         var target = r.parentElement();
41784                         if(!target || target.tagName.toLowerCase() != 'li'){
41785                             e.stopEvent();
41786                             r.pasteHTML('<br />');
41787                             r.collapse(false);
41788                             r.select();
41789                         }
41790                     }
41791                 }
41792                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41793                     this.cleanUpPaste.defer(100, this);
41794                     return;
41795                 }
41796                 
41797                 
41798             };
41799         }else if(Roo.isOpera){
41800             return function(e){
41801                 var k = e.getKey();
41802                 if(k == e.TAB){
41803                     e.stopEvent();
41804                     this.win.focus();
41805                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41806                     this.deferFocus();
41807                 }
41808                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41809                     this.cleanUpPaste.defer(100, this);
41810                     return;
41811                 }
41812                 
41813             };
41814         }else if(Roo.isSafari){
41815             return function(e){
41816                 var k = e.getKey();
41817                 
41818                 if(k == e.TAB){
41819                     e.stopEvent();
41820                     this.execCmd('InsertText','\t');
41821                     this.deferFocus();
41822                     return;
41823                 }
41824                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41825                     this.cleanUpPaste.defer(100, this);
41826                     return;
41827                 }
41828                 
41829              };
41830         }
41831     }(),
41832     
41833     getAllAncestors: function()
41834     {
41835         var p = this.getSelectedNode();
41836         var a = [];
41837         if (!p) {
41838             a.push(p); // push blank onto stack..
41839             p = this.getParentElement();
41840         }
41841         
41842         
41843         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41844             a.push(p);
41845             p = p.parentNode;
41846         }
41847         a.push(this.doc.body);
41848         return a;
41849     },
41850     lastSel : false,
41851     lastSelNode : false,
41852     
41853     
41854     getSelection : function() 
41855     {
41856         this.assignDocWin();
41857         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41858     },
41859     
41860     getSelectedNode: function() 
41861     {
41862         // this may only work on Gecko!!!
41863         
41864         // should we cache this!!!!
41865         
41866         
41867         
41868          
41869         var range = this.createRange(this.getSelection()).cloneRange();
41870         
41871         if (Roo.isIE) {
41872             var parent = range.parentElement();
41873             while (true) {
41874                 var testRange = range.duplicate();
41875                 testRange.moveToElementText(parent);
41876                 if (testRange.inRange(range)) {
41877                     break;
41878                 }
41879                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41880                     break;
41881                 }
41882                 parent = parent.parentElement;
41883             }
41884             return parent;
41885         }
41886         
41887         // is ancestor a text element.
41888         var ac =  range.commonAncestorContainer;
41889         if (ac.nodeType == 3) {
41890             ac = ac.parentNode;
41891         }
41892         
41893         var ar = ac.childNodes;
41894          
41895         var nodes = [];
41896         var other_nodes = [];
41897         var has_other_nodes = false;
41898         for (var i=0;i<ar.length;i++) {
41899             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41900                 continue;
41901             }
41902             // fullly contained node.
41903             
41904             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41905                 nodes.push(ar[i]);
41906                 continue;
41907             }
41908             
41909             // probably selected..
41910             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41911                 other_nodes.push(ar[i]);
41912                 continue;
41913             }
41914             // outer..
41915             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41916                 continue;
41917             }
41918             
41919             
41920             has_other_nodes = true;
41921         }
41922         if (!nodes.length && other_nodes.length) {
41923             nodes= other_nodes;
41924         }
41925         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41926             return false;
41927         }
41928         
41929         return nodes[0];
41930     },
41931     createRange: function(sel)
41932     {
41933         // this has strange effects when using with 
41934         // top toolbar - not sure if it's a great idea.
41935         //this.editor.contentWindow.focus();
41936         if (typeof sel != "undefined") {
41937             try {
41938                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41939             } catch(e) {
41940                 return this.doc.createRange();
41941             }
41942         } else {
41943             return this.doc.createRange();
41944         }
41945     },
41946     getParentElement: function()
41947     {
41948         
41949         this.assignDocWin();
41950         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41951         
41952         var range = this.createRange(sel);
41953          
41954         try {
41955             var p = range.commonAncestorContainer;
41956             while (p.nodeType == 3) { // text node
41957                 p = p.parentNode;
41958             }
41959             return p;
41960         } catch (e) {
41961             return null;
41962         }
41963     
41964     },
41965     /***
41966      *
41967      * Range intersection.. the hard stuff...
41968      *  '-1' = before
41969      *  '0' = hits..
41970      *  '1' = after.
41971      *         [ -- selected range --- ]
41972      *   [fail]                        [fail]
41973      *
41974      *    basically..
41975      *      if end is before start or  hits it. fail.
41976      *      if start is after end or hits it fail.
41977      *
41978      *   if either hits (but other is outside. - then it's not 
41979      *   
41980      *    
41981      **/
41982     
41983     
41984     // @see http://www.thismuchiknow.co.uk/?p=64.
41985     rangeIntersectsNode : function(range, node)
41986     {
41987         var nodeRange = node.ownerDocument.createRange();
41988         try {
41989             nodeRange.selectNode(node);
41990         } catch (e) {
41991             nodeRange.selectNodeContents(node);
41992         }
41993     
41994         var rangeStartRange = range.cloneRange();
41995         rangeStartRange.collapse(true);
41996     
41997         var rangeEndRange = range.cloneRange();
41998         rangeEndRange.collapse(false);
41999     
42000         var nodeStartRange = nodeRange.cloneRange();
42001         nodeStartRange.collapse(true);
42002     
42003         var nodeEndRange = nodeRange.cloneRange();
42004         nodeEndRange.collapse(false);
42005     
42006         return rangeStartRange.compareBoundaryPoints(
42007                  Range.START_TO_START, nodeEndRange) == -1 &&
42008                rangeEndRange.compareBoundaryPoints(
42009                  Range.START_TO_START, nodeStartRange) == 1;
42010         
42011          
42012     },
42013     rangeCompareNode : function(range, node)
42014     {
42015         var nodeRange = node.ownerDocument.createRange();
42016         try {
42017             nodeRange.selectNode(node);
42018         } catch (e) {
42019             nodeRange.selectNodeContents(node);
42020         }
42021         
42022         
42023         range.collapse(true);
42024     
42025         nodeRange.collapse(true);
42026      
42027         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42028         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42029          
42030         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42031         
42032         var nodeIsBefore   =  ss == 1;
42033         var nodeIsAfter    = ee == -1;
42034         
42035         if (nodeIsBefore && nodeIsAfter)
42036             return 0; // outer
42037         if (!nodeIsBefore && nodeIsAfter)
42038             return 1; //right trailed.
42039         
42040         if (nodeIsBefore && !nodeIsAfter)
42041             return 2;  // left trailed.
42042         // fully contined.
42043         return 3;
42044     },
42045
42046     // private? - in a new class?
42047     cleanUpPaste :  function()
42048     {
42049         // cleans up the whole document..
42050         Roo.log('cleanuppaste');
42051         
42052         this.cleanUpChildren(this.doc.body);
42053         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42054         if (clean != this.doc.body.innerHTML) {
42055             this.doc.body.innerHTML = clean;
42056         }
42057         
42058     },
42059     
42060     cleanWordChars : function(input) {// change the chars to hex code
42061         var he = Roo.HtmlEditorCore;
42062         
42063         var output = input;
42064         Roo.each(he.swapCodes, function(sw) { 
42065             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42066             
42067             output = output.replace(swapper, sw[1]);
42068         });
42069         
42070         return output;
42071     },
42072     
42073     
42074     cleanUpChildren : function (n)
42075     {
42076         if (!n.childNodes.length) {
42077             return;
42078         }
42079         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42080            this.cleanUpChild(n.childNodes[i]);
42081         }
42082     },
42083     
42084     
42085         
42086     
42087     cleanUpChild : function (node)
42088     {
42089         var ed = this;
42090         //console.log(node);
42091         if (node.nodeName == "#text") {
42092             // clean up silly Windows -- stuff?
42093             return; 
42094         }
42095         if (node.nodeName == "#comment") {
42096             node.parentNode.removeChild(node);
42097             // clean up silly Windows -- stuff?
42098             return; 
42099         }
42100         var lcname = node.tagName.toLowerCase();
42101         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42102         // whitelist of tags..
42103         
42104         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42105             // remove node.
42106             node.parentNode.removeChild(node);
42107             return;
42108             
42109         }
42110         
42111         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42112         
42113         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42114         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42115         
42116         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42117         //    remove_keep_children = true;
42118         //}
42119         
42120         if (remove_keep_children) {
42121             this.cleanUpChildren(node);
42122             // inserts everything just before this node...
42123             while (node.childNodes.length) {
42124                 var cn = node.childNodes[0];
42125                 node.removeChild(cn);
42126                 node.parentNode.insertBefore(cn, node);
42127             }
42128             node.parentNode.removeChild(node);
42129             return;
42130         }
42131         
42132         if (!node.attributes || !node.attributes.length) {
42133             this.cleanUpChildren(node);
42134             return;
42135         }
42136         
42137         function cleanAttr(n,v)
42138         {
42139             
42140             if (v.match(/^\./) || v.match(/^\//)) {
42141                 return;
42142             }
42143             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42144                 return;
42145             }
42146             if (v.match(/^#/)) {
42147                 return;
42148             }
42149 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42150             node.removeAttribute(n);
42151             
42152         }
42153         
42154         var cwhite = this.cwhite;
42155         var cblack = this.cblack;
42156             
42157         function cleanStyle(n,v)
42158         {
42159             if (v.match(/expression/)) { //XSS?? should we even bother..
42160                 node.removeAttribute(n);
42161                 return;
42162             }
42163             
42164             var parts = v.split(/;/);
42165             var clean = [];
42166             
42167             Roo.each(parts, function(p) {
42168                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42169                 if (!p.length) {
42170                     return true;
42171                 }
42172                 var l = p.split(':').shift().replace(/\s+/g,'');
42173                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42174                 
42175                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42176 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42177                     //node.removeAttribute(n);
42178                     return true;
42179                 }
42180                 //Roo.log()
42181                 // only allow 'c whitelisted system attributes'
42182                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42183 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42184                     //node.removeAttribute(n);
42185                     return true;
42186                 }
42187                 
42188                 
42189                  
42190                 
42191                 clean.push(p);
42192                 return true;
42193             });
42194             if (clean.length) { 
42195                 node.setAttribute(n, clean.join(';'));
42196             } else {
42197                 node.removeAttribute(n);
42198             }
42199             
42200         }
42201         
42202         
42203         for (var i = node.attributes.length-1; i > -1 ; i--) {
42204             var a = node.attributes[i];
42205             //console.log(a);
42206             
42207             if (a.name.toLowerCase().substr(0,2)=='on')  {
42208                 node.removeAttribute(a.name);
42209                 continue;
42210             }
42211             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42212                 node.removeAttribute(a.name);
42213                 continue;
42214             }
42215             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42216                 cleanAttr(a.name,a.value); // fixme..
42217                 continue;
42218             }
42219             if (a.name == 'style') {
42220                 cleanStyle(a.name,a.value);
42221                 continue;
42222             }
42223             /// clean up MS crap..
42224             // tecnically this should be a list of valid class'es..
42225             
42226             
42227             if (a.name == 'class') {
42228                 if (a.value.match(/^Mso/)) {
42229                     node.className = '';
42230                 }
42231                 
42232                 if (a.value.match(/body/)) {
42233                     node.className = '';
42234                 }
42235                 continue;
42236             }
42237             
42238             // style cleanup!?
42239             // class cleanup?
42240             
42241         }
42242         
42243         
42244         this.cleanUpChildren(node);
42245         
42246         
42247     },
42248     /**
42249      * Clean up MS wordisms...
42250      */
42251     cleanWord : function(node)
42252     {
42253         var _t = this;
42254         var cleanWordChildren = function()
42255         {
42256             if (!node.childNodes.length) {
42257                 return;
42258             }
42259             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42260                _t.cleanWord(node.childNodes[i]);
42261             }
42262         }
42263         
42264         
42265         if (!node) {
42266             this.cleanWord(this.doc.body);
42267             return;
42268         }
42269         if (node.nodeName == "#text") {
42270             // clean up silly Windows -- stuff?
42271             return; 
42272         }
42273         if (node.nodeName == "#comment") {
42274             node.parentNode.removeChild(node);
42275             // clean up silly Windows -- stuff?
42276             return; 
42277         }
42278         
42279         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42280             node.parentNode.removeChild(node);
42281             return;
42282         }
42283         
42284         // remove - but keep children..
42285         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42286             while (node.childNodes.length) {
42287                 var cn = node.childNodes[0];
42288                 node.removeChild(cn);
42289                 node.parentNode.insertBefore(cn, node);
42290             }
42291             node.parentNode.removeChild(node);
42292             cleanWordChildren();
42293             return;
42294         }
42295         // clean styles
42296         if (node.className.length) {
42297             
42298             var cn = node.className.split(/\W+/);
42299             var cna = [];
42300             Roo.each(cn, function(cls) {
42301                 if (cls.match(/Mso[a-zA-Z]+/)) {
42302                     return;
42303                 }
42304                 cna.push(cls);
42305             });
42306             node.className = cna.length ? cna.join(' ') : '';
42307             if (!cna.length) {
42308                 node.removeAttribute("class");
42309             }
42310         }
42311         
42312         if (node.hasAttribute("lang")) {
42313             node.removeAttribute("lang");
42314         }
42315         
42316         if (node.hasAttribute("style")) {
42317             
42318             var styles = node.getAttribute("style").split(";");
42319             var nstyle = [];
42320             Roo.each(styles, function(s) {
42321                 if (!s.match(/:/)) {
42322                     return;
42323                 }
42324                 var kv = s.split(":");
42325                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42326                     return;
42327                 }
42328                 // what ever is left... we allow.
42329                 nstyle.push(s);
42330             });
42331             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42332             if (!nstyle.length) {
42333                 node.removeAttribute('style');
42334             }
42335         }
42336         
42337         cleanWordChildren();
42338         
42339         
42340     },
42341     domToHTML : function(currentElement, depth, nopadtext) {
42342         
42343         depth = depth || 0;
42344         nopadtext = nopadtext || false;
42345     
42346         if (!currentElement) {
42347             return this.domToHTML(this.doc.body);
42348         }
42349         
42350         //Roo.log(currentElement);
42351         var j;
42352         var allText = false;
42353         var nodeName = currentElement.nodeName;
42354         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42355         
42356         if  (nodeName == '#text') {
42357             
42358             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42359         }
42360         
42361         
42362         var ret = '';
42363         if (nodeName != 'BODY') {
42364              
42365             var i = 0;
42366             // Prints the node tagName, such as <A>, <IMG>, etc
42367             if (tagName) {
42368                 var attr = [];
42369                 for(i = 0; i < currentElement.attributes.length;i++) {
42370                     // quoting?
42371                     var aname = currentElement.attributes.item(i).name;
42372                     if (!currentElement.attributes.item(i).value.length) {
42373                         continue;
42374                     }
42375                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42376                 }
42377                 
42378                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42379             } 
42380             else {
42381                 
42382                 // eack
42383             }
42384         } else {
42385             tagName = false;
42386         }
42387         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42388             return ret;
42389         }
42390         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42391             nopadtext = true;
42392         }
42393         
42394         
42395         // Traverse the tree
42396         i = 0;
42397         var currentElementChild = currentElement.childNodes.item(i);
42398         var allText = true;
42399         var innerHTML  = '';
42400         lastnode = '';
42401         while (currentElementChild) {
42402             // Formatting code (indent the tree so it looks nice on the screen)
42403             var nopad = nopadtext;
42404             if (lastnode == 'SPAN') {
42405                 nopad  = true;
42406             }
42407             // text
42408             if  (currentElementChild.nodeName == '#text') {
42409                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42410                 toadd = nopadtext ? toadd : toadd.trim();
42411                 if (!nopad && toadd.length > 80) {
42412                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42413                 }
42414                 innerHTML  += toadd;
42415                 
42416                 i++;
42417                 currentElementChild = currentElement.childNodes.item(i);
42418                 lastNode = '';
42419                 continue;
42420             }
42421             allText = false;
42422             
42423             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42424                 
42425             // Recursively traverse the tree structure of the child node
42426             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42427             lastnode = currentElementChild.nodeName;
42428             i++;
42429             currentElementChild=currentElement.childNodes.item(i);
42430         }
42431         
42432         ret += innerHTML;
42433         
42434         if (!allText) {
42435                 // The remaining code is mostly for formatting the tree
42436             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42437         }
42438         
42439         
42440         if (tagName) {
42441             ret+= "</"+tagName+">";
42442         }
42443         return ret;
42444         
42445     },
42446         
42447     applyBlacklists : function()
42448     {
42449         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42450         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42451         
42452         this.white = [];
42453         this.black = [];
42454         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42455             if (b.indexOf(tag) > -1) {
42456                 return;
42457             }
42458             this.white.push(tag);
42459             
42460         }, this);
42461         
42462         Roo.each(w, function(tag) {
42463             if (b.indexOf(tag) > -1) {
42464                 return;
42465             }
42466             if (this.white.indexOf(tag) > -1) {
42467                 return;
42468             }
42469             this.white.push(tag);
42470             
42471         }, this);
42472         
42473         
42474         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42475             if (w.indexOf(tag) > -1) {
42476                 return;
42477             }
42478             this.black.push(tag);
42479             
42480         }, this);
42481         
42482         Roo.each(b, function(tag) {
42483             if (w.indexOf(tag) > -1) {
42484                 return;
42485             }
42486             if (this.black.indexOf(tag) > -1) {
42487                 return;
42488             }
42489             this.black.push(tag);
42490             
42491         }, this);
42492         
42493         
42494         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42495         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42496         
42497         this.cwhite = [];
42498         this.cblack = [];
42499         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42500             if (b.indexOf(tag) > -1) {
42501                 return;
42502             }
42503             this.cwhite.push(tag);
42504             
42505         }, this);
42506         
42507         Roo.each(w, function(tag) {
42508             if (b.indexOf(tag) > -1) {
42509                 return;
42510             }
42511             if (this.cwhite.indexOf(tag) > -1) {
42512                 return;
42513             }
42514             this.cwhite.push(tag);
42515             
42516         }, this);
42517         
42518         
42519         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42520             if (w.indexOf(tag) > -1) {
42521                 return;
42522             }
42523             this.cblack.push(tag);
42524             
42525         }, this);
42526         
42527         Roo.each(b, function(tag) {
42528             if (w.indexOf(tag) > -1) {
42529                 return;
42530             }
42531             if (this.cblack.indexOf(tag) > -1) {
42532                 return;
42533             }
42534             this.cblack.push(tag);
42535             
42536         }, this);
42537     },
42538     
42539     setStylesheets : function(stylesheets)
42540     {
42541         if(typeof(stylesheets) == 'string'){
42542             Roo.get(this.iframe.contentDocument.head).createChild({
42543                 tag : 'link',
42544                 rel : 'stylesheet',
42545                 type : 'text/css',
42546                 href : stylesheets
42547             });
42548             
42549             return;
42550         }
42551         var _this = this;
42552      
42553         Roo.each(stylesheets, function(s) {
42554             if(!s.length){
42555                 return;
42556             }
42557             
42558             Roo.get(_this.iframe.contentDocument.head).createChild({
42559                 tag : 'link',
42560                 rel : 'stylesheet',
42561                 type : 'text/css',
42562                 href : s
42563             });
42564         });
42565
42566         
42567     },
42568     
42569     removeStylesheets : function()
42570     {
42571         var _this = this;
42572         
42573         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42574             s.remove();
42575         });
42576     }
42577     
42578     // hide stuff that is not compatible
42579     /**
42580      * @event blur
42581      * @hide
42582      */
42583     /**
42584      * @event change
42585      * @hide
42586      */
42587     /**
42588      * @event focus
42589      * @hide
42590      */
42591     /**
42592      * @event specialkey
42593      * @hide
42594      */
42595     /**
42596      * @cfg {String} fieldClass @hide
42597      */
42598     /**
42599      * @cfg {String} focusClass @hide
42600      */
42601     /**
42602      * @cfg {String} autoCreate @hide
42603      */
42604     /**
42605      * @cfg {String} inputType @hide
42606      */
42607     /**
42608      * @cfg {String} invalidClass @hide
42609      */
42610     /**
42611      * @cfg {String} invalidText @hide
42612      */
42613     /**
42614      * @cfg {String} msgFx @hide
42615      */
42616     /**
42617      * @cfg {String} validateOnBlur @hide
42618      */
42619 });
42620
42621 Roo.HtmlEditorCore.white = [
42622         'area', 'br', 'img', 'input', 'hr', 'wbr',
42623         
42624        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42625        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42626        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42627        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42628        'table',   'ul',         'xmp', 
42629        
42630        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42631       'thead',   'tr', 
42632      
42633       'dir', 'menu', 'ol', 'ul', 'dl',
42634        
42635       'embed',  'object'
42636 ];
42637
42638
42639 Roo.HtmlEditorCore.black = [
42640     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42641         'applet', // 
42642         'base',   'basefont', 'bgsound', 'blink',  'body', 
42643         'frame',  'frameset', 'head',    'html',   'ilayer', 
42644         'iframe', 'layer',  'link',     'meta',    'object',   
42645         'script', 'style' ,'title',  'xml' // clean later..
42646 ];
42647 Roo.HtmlEditorCore.clean = [
42648     'script', 'style', 'title', 'xml'
42649 ];
42650 Roo.HtmlEditorCore.remove = [
42651     'font'
42652 ];
42653 // attributes..
42654
42655 Roo.HtmlEditorCore.ablack = [
42656     'on'
42657 ];
42658     
42659 Roo.HtmlEditorCore.aclean = [ 
42660     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42661 ];
42662
42663 // protocols..
42664 Roo.HtmlEditorCore.pwhite= [
42665         'http',  'https',  'mailto'
42666 ];
42667
42668 // white listed style attributes.
42669 Roo.HtmlEditorCore.cwhite= [
42670       //  'text-align', /// default is to allow most things..
42671       
42672          
42673 //        'font-size'//??
42674 ];
42675
42676 // black listed style attributes.
42677 Roo.HtmlEditorCore.cblack= [
42678       //  'font-size' -- this can be set by the project 
42679 ];
42680
42681
42682 Roo.HtmlEditorCore.swapCodes   =[ 
42683     [    8211, "--" ], 
42684     [    8212, "--" ], 
42685     [    8216,  "'" ],  
42686     [    8217, "'" ],  
42687     [    8220, '"' ],  
42688     [    8221, '"' ],  
42689     [    8226, "*" ],  
42690     [    8230, "..." ]
42691 ]; 
42692
42693     //<script type="text/javascript">
42694
42695 /*
42696  * Ext JS Library 1.1.1
42697  * Copyright(c) 2006-2007, Ext JS, LLC.
42698  * Licence LGPL
42699  * 
42700  */
42701  
42702  
42703 Roo.form.HtmlEditor = function(config){
42704     
42705     
42706     
42707     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42708     
42709     if (!this.toolbars) {
42710         this.toolbars = [];
42711     }
42712     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42713     
42714     
42715 };
42716
42717 /**
42718  * @class Roo.form.HtmlEditor
42719  * @extends Roo.form.Field
42720  * Provides a lightweight HTML Editor component.
42721  *
42722  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42723  * 
42724  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42725  * supported by this editor.</b><br/><br/>
42726  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42727  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42728  */
42729 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42730     /**
42731      * @cfg {Boolean} clearUp
42732      */
42733     clearUp : true,
42734       /**
42735      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42736      */
42737     toolbars : false,
42738    
42739      /**
42740      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42741      *                        Roo.resizable.
42742      */
42743     resizable : false,
42744      /**
42745      * @cfg {Number} height (in pixels)
42746      */   
42747     height: 300,
42748    /**
42749      * @cfg {Number} width (in pixels)
42750      */   
42751     width: 500,
42752     
42753     /**
42754      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42755      * 
42756      */
42757     stylesheets: false,
42758     
42759     
42760      /**
42761      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42762      * 
42763      */
42764     cblack: false,
42765     /**
42766      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42767      * 
42768      */
42769     cwhite: false,
42770     
42771      /**
42772      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42773      * 
42774      */
42775     black: false,
42776     /**
42777      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42778      * 
42779      */
42780     white: false,
42781     
42782     // id of frame..
42783     frameId: false,
42784     
42785     // private properties
42786     validationEvent : false,
42787     deferHeight: true,
42788     initialized : false,
42789     activated : false,
42790     
42791     onFocus : Roo.emptyFn,
42792     iframePad:3,
42793     hideMode:'offsets',
42794     
42795     actionMode : 'container', // defaults to hiding it...
42796     
42797     defaultAutoCreate : { // modified by initCompnoent..
42798         tag: "textarea",
42799         style:"width:500px;height:300px;",
42800         autocomplete: "new-password"
42801     },
42802
42803     // private
42804     initComponent : function(){
42805         this.addEvents({
42806             /**
42807              * @event initialize
42808              * Fires when the editor is fully initialized (including the iframe)
42809              * @param {HtmlEditor} this
42810              */
42811             initialize: true,
42812             /**
42813              * @event activate
42814              * Fires when the editor is first receives the focus. Any insertion must wait
42815              * until after this event.
42816              * @param {HtmlEditor} this
42817              */
42818             activate: true,
42819              /**
42820              * @event beforesync
42821              * Fires before the textarea is updated with content from the editor iframe. Return false
42822              * to cancel the sync.
42823              * @param {HtmlEditor} this
42824              * @param {String} html
42825              */
42826             beforesync: true,
42827              /**
42828              * @event beforepush
42829              * Fires before the iframe editor is updated with content from the textarea. Return false
42830              * to cancel the push.
42831              * @param {HtmlEditor} this
42832              * @param {String} html
42833              */
42834             beforepush: true,
42835              /**
42836              * @event sync
42837              * Fires when the textarea is updated with content from the editor iframe.
42838              * @param {HtmlEditor} this
42839              * @param {String} html
42840              */
42841             sync: true,
42842              /**
42843              * @event push
42844              * Fires when the iframe editor is updated with content from the textarea.
42845              * @param {HtmlEditor} this
42846              * @param {String} html
42847              */
42848             push: true,
42849              /**
42850              * @event editmodechange
42851              * Fires when the editor switches edit modes
42852              * @param {HtmlEditor} this
42853              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42854              */
42855             editmodechange: true,
42856             /**
42857              * @event editorevent
42858              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42859              * @param {HtmlEditor} this
42860              */
42861             editorevent: true,
42862             /**
42863              * @event firstfocus
42864              * Fires when on first focus - needed by toolbars..
42865              * @param {HtmlEditor} this
42866              */
42867             firstfocus: true,
42868             /**
42869              * @event autosave
42870              * Auto save the htmlEditor value as a file into Events
42871              * @param {HtmlEditor} this
42872              */
42873             autosave: true,
42874             /**
42875              * @event savedpreview
42876              * preview the saved version of htmlEditor
42877              * @param {HtmlEditor} this
42878              */
42879             savedpreview: true,
42880             
42881             /**
42882             * @event stylesheetsclick
42883             * Fires when press the Sytlesheets button
42884             * @param {Roo.HtmlEditorCore} this
42885             */
42886             stylesheetsclick: true
42887         });
42888         this.defaultAutoCreate =  {
42889             tag: "textarea",
42890             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42891             autocomplete: "new-password"
42892         };
42893     },
42894
42895     /**
42896      * Protected method that will not generally be called directly. It
42897      * is called when the editor creates its toolbar. Override this method if you need to
42898      * add custom toolbar buttons.
42899      * @param {HtmlEditor} editor
42900      */
42901     createToolbar : function(editor){
42902         Roo.log("create toolbars");
42903         if (!editor.toolbars || !editor.toolbars.length) {
42904             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42905         }
42906         
42907         for (var i =0 ; i < editor.toolbars.length;i++) {
42908             editor.toolbars[i] = Roo.factory(
42909                     typeof(editor.toolbars[i]) == 'string' ?
42910                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42911                 Roo.form.HtmlEditor);
42912             editor.toolbars[i].init(editor);
42913         }
42914          
42915         
42916     },
42917
42918      
42919     // private
42920     onRender : function(ct, position)
42921     {
42922         var _t = this;
42923         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42924         
42925         this.wrap = this.el.wrap({
42926             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42927         });
42928         
42929         this.editorcore.onRender(ct, position);
42930          
42931         if (this.resizable) {
42932             this.resizeEl = new Roo.Resizable(this.wrap, {
42933                 pinned : true,
42934                 wrap: true,
42935                 dynamic : true,
42936                 minHeight : this.height,
42937                 height: this.height,
42938                 handles : this.resizable,
42939                 width: this.width,
42940                 listeners : {
42941                     resize : function(r, w, h) {
42942                         _t.onResize(w,h); // -something
42943                     }
42944                 }
42945             });
42946             
42947         }
42948         this.createToolbar(this);
42949        
42950         
42951         if(!this.width){
42952             this.setSize(this.wrap.getSize());
42953         }
42954         if (this.resizeEl) {
42955             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42956             // should trigger onReize..
42957         }
42958         
42959         this.keyNav = new Roo.KeyNav(this.el, {
42960             
42961             "tab" : function(e){
42962                 e.preventDefault();
42963                 
42964                 var value = this.getValue();
42965                 
42966                 var start = this.el.dom.selectionStart;
42967                 var end = this.el.dom.selectionEnd;
42968                 
42969                 if(!e.shiftKey){
42970                     
42971                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
42972                     this.el.dom.setSelectionRange(end + 1, end + 1);
42973                     return;
42974                 }
42975                 
42976                 var f = value.substring(0, start).split("\t");
42977                 
42978                 if(f.pop().length != 0){
42979                     return;
42980                 }
42981                 
42982                 this.setValue(f.join("\t") + value.substring(end));
42983                 this.el.dom.setSelectionRange(start - 1, start - 1);
42984                 
42985             },
42986             
42987             "home" : function(e){
42988                 e.preventDefault();
42989                 
42990                 var curr = this.el.dom.selectionStart;
42991                 var lines = this.getValue().split("\n");
42992                 
42993                 if(!lines.length){
42994                     return;
42995                 }
42996                 
42997                 if(e.ctrlKey){
42998                     this.el.dom.setSelectionRange(0, 0);
42999                     return;
43000                 }
43001                 
43002                 var pos = 0;
43003                 
43004                 for (var i = 0; i < lines.length;i++) {
43005                     pos += lines[i].length;
43006                     
43007                     if(i != 0){
43008                         pos += 1;
43009                     }
43010                     
43011                     if(pos < curr){
43012                         continue;
43013                     }
43014                     
43015                     pos -= lines[i].length;
43016                     
43017                     break;
43018                 }
43019                 
43020                 if(!e.shiftKey){
43021                     this.el.dom.setSelectionRange(pos, pos);
43022                     return;
43023                 }
43024                 
43025                 this.el.dom.selectionStart = pos;
43026                 this.el.dom.selectionEnd = curr;
43027             },
43028             
43029             "end" : function(e){
43030                 e.preventDefault();
43031                 
43032                 var curr = this.el.dom.selectionStart;
43033                 var lines = this.getValue().split("\n");
43034                 
43035                 if(!lines.length){
43036                     return;
43037                 }
43038                 
43039                 if(e.ctrlKey){
43040                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43041                     return;
43042                 }
43043                 
43044                 var pos = 0;
43045                 
43046                 for (var i = 0; i < lines.length;i++) {
43047                     
43048                     pos += lines[i].length;
43049                     
43050                     if(i != 0){
43051                         pos += 1;
43052                     }
43053                     
43054                     if(pos < curr){
43055                         continue;
43056                     }
43057                     
43058                     break;
43059                 }
43060                 
43061                 if(!e.shiftKey){
43062                     this.el.dom.setSelectionRange(pos, pos);
43063                     return;
43064                 }
43065                 
43066                 this.el.dom.selectionStart = curr;
43067                 this.el.dom.selectionEnd = pos;
43068             },
43069
43070             scope : this,
43071
43072             doRelay : function(foo, bar, hname){
43073                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43074             },
43075
43076             forceKeyDown: true
43077         });
43078         
43079 //        if(this.autosave && this.w){
43080 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43081 //        }
43082     },
43083
43084     // private
43085     onResize : function(w, h)
43086     {
43087         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43088         var ew = false;
43089         var eh = false;
43090         
43091         if(this.el ){
43092             if(typeof w == 'number'){
43093                 var aw = w - this.wrap.getFrameWidth('lr');
43094                 this.el.setWidth(this.adjustWidth('textarea', aw));
43095                 ew = aw;
43096             }
43097             if(typeof h == 'number'){
43098                 var tbh = 0;
43099                 for (var i =0; i < this.toolbars.length;i++) {
43100                     // fixme - ask toolbars for heights?
43101                     tbh += this.toolbars[i].tb.el.getHeight();
43102                     if (this.toolbars[i].footer) {
43103                         tbh += this.toolbars[i].footer.el.getHeight();
43104                     }
43105                 }
43106                 
43107                 
43108                 
43109                 
43110                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43111                 ah -= 5; // knock a few pixes off for look..
43112 //                Roo.log(ah);
43113                 this.el.setHeight(this.adjustWidth('textarea', ah));
43114                 var eh = ah;
43115             }
43116         }
43117         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43118         this.editorcore.onResize(ew,eh);
43119         
43120     },
43121
43122     /**
43123      * Toggles the editor between standard and source edit mode.
43124      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43125      */
43126     toggleSourceEdit : function(sourceEditMode)
43127     {
43128         this.editorcore.toggleSourceEdit(sourceEditMode);
43129         
43130         if(this.editorcore.sourceEditMode){
43131             Roo.log('editor - showing textarea');
43132             
43133 //            Roo.log('in');
43134 //            Roo.log(this.syncValue());
43135             this.editorcore.syncValue();
43136             this.el.removeClass('x-hidden');
43137             this.el.dom.removeAttribute('tabIndex');
43138             this.el.focus();
43139             
43140             for (var i = 0; i < this.toolbars.length; i++) {
43141                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43142                     this.toolbars[i].tb.hide();
43143                     this.toolbars[i].footer.hide();
43144                 }
43145             }
43146             
43147         }else{
43148             Roo.log('editor - hiding textarea');
43149 //            Roo.log('out')
43150 //            Roo.log(this.pushValue()); 
43151             this.editorcore.pushValue();
43152             
43153             this.el.addClass('x-hidden');
43154             this.el.dom.setAttribute('tabIndex', -1);
43155             
43156             for (var i = 0; i < this.toolbars.length; i++) {
43157                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43158                     this.toolbars[i].tb.show();
43159                     this.toolbars[i].footer.show();
43160                 }
43161             }
43162             
43163             //this.deferFocus();
43164         }
43165         
43166         this.setSize(this.wrap.getSize());
43167         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43168         
43169         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43170     },
43171  
43172     // private (for BoxComponent)
43173     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43174
43175     // private (for BoxComponent)
43176     getResizeEl : function(){
43177         return this.wrap;
43178     },
43179
43180     // private (for BoxComponent)
43181     getPositionEl : function(){
43182         return this.wrap;
43183     },
43184
43185     // private
43186     initEvents : function(){
43187         this.originalValue = this.getValue();
43188     },
43189
43190     /**
43191      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43192      * @method
43193      */
43194     markInvalid : Roo.emptyFn,
43195     /**
43196      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43197      * @method
43198      */
43199     clearInvalid : Roo.emptyFn,
43200
43201     setValue : function(v){
43202         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43203         this.editorcore.pushValue();
43204     },
43205
43206      
43207     // private
43208     deferFocus : function(){
43209         this.focus.defer(10, this);
43210     },
43211
43212     // doc'ed in Field
43213     focus : function(){
43214         this.editorcore.focus();
43215         
43216     },
43217       
43218
43219     // private
43220     onDestroy : function(){
43221         
43222         
43223         
43224         if(this.rendered){
43225             
43226             for (var i =0; i < this.toolbars.length;i++) {
43227                 // fixme - ask toolbars for heights?
43228                 this.toolbars[i].onDestroy();
43229             }
43230             
43231             this.wrap.dom.innerHTML = '';
43232             this.wrap.remove();
43233         }
43234     },
43235
43236     // private
43237     onFirstFocus : function(){
43238         //Roo.log("onFirstFocus");
43239         this.editorcore.onFirstFocus();
43240          for (var i =0; i < this.toolbars.length;i++) {
43241             this.toolbars[i].onFirstFocus();
43242         }
43243         
43244     },
43245     
43246     // private
43247     syncValue : function()
43248     {
43249         this.editorcore.syncValue();
43250     },
43251     
43252     pushValue : function()
43253     {
43254         this.editorcore.pushValue();
43255     },
43256     
43257     setStylesheets : function(stylesheets)
43258     {
43259         this.editorcore.setStylesheets(stylesheets);
43260     },
43261     
43262     removeStylesheets : function()
43263     {
43264         this.editorcore.removeStylesheets();
43265     }
43266      
43267     
43268     // hide stuff that is not compatible
43269     /**
43270      * @event blur
43271      * @hide
43272      */
43273     /**
43274      * @event change
43275      * @hide
43276      */
43277     /**
43278      * @event focus
43279      * @hide
43280      */
43281     /**
43282      * @event specialkey
43283      * @hide
43284      */
43285     /**
43286      * @cfg {String} fieldClass @hide
43287      */
43288     /**
43289      * @cfg {String} focusClass @hide
43290      */
43291     /**
43292      * @cfg {String} autoCreate @hide
43293      */
43294     /**
43295      * @cfg {String} inputType @hide
43296      */
43297     /**
43298      * @cfg {String} invalidClass @hide
43299      */
43300     /**
43301      * @cfg {String} invalidText @hide
43302      */
43303     /**
43304      * @cfg {String} msgFx @hide
43305      */
43306     /**
43307      * @cfg {String} validateOnBlur @hide
43308      */
43309 });
43310  
43311     // <script type="text/javascript">
43312 /*
43313  * Based on
43314  * Ext JS Library 1.1.1
43315  * Copyright(c) 2006-2007, Ext JS, LLC.
43316  *  
43317  
43318  */
43319
43320 /**
43321  * @class Roo.form.HtmlEditorToolbar1
43322  * Basic Toolbar
43323  * 
43324  * Usage:
43325  *
43326  new Roo.form.HtmlEditor({
43327     ....
43328     toolbars : [
43329         new Roo.form.HtmlEditorToolbar1({
43330             disable : { fonts: 1 , format: 1, ..., ... , ...],
43331             btns : [ .... ]
43332         })
43333     }
43334      
43335  * 
43336  * @cfg {Object} disable List of elements to disable..
43337  * @cfg {Array} btns List of additional buttons.
43338  * 
43339  * 
43340  * NEEDS Extra CSS? 
43341  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43342  */
43343  
43344 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43345 {
43346     
43347     Roo.apply(this, config);
43348     
43349     // default disabled, based on 'good practice'..
43350     this.disable = this.disable || {};
43351     Roo.applyIf(this.disable, {
43352         fontSize : true,
43353         colors : true,
43354         specialElements : true
43355     });
43356     
43357     
43358     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43359     // dont call parent... till later.
43360 }
43361
43362 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43363     
43364     tb: false,
43365     
43366     rendered: false,
43367     
43368     editor : false,
43369     editorcore : false,
43370     /**
43371      * @cfg {Object} disable  List of toolbar elements to disable
43372          
43373      */
43374     disable : false,
43375     
43376     
43377      /**
43378      * @cfg {String} createLinkText The default text for the create link prompt
43379      */
43380     createLinkText : 'Please enter the URL for the link:',
43381     /**
43382      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43383      */
43384     defaultLinkValue : 'http:/'+'/',
43385    
43386     
43387       /**
43388      * @cfg {Array} fontFamilies An array of available font families
43389      */
43390     fontFamilies : [
43391         'Arial',
43392         'Courier New',
43393         'Tahoma',
43394         'Times New Roman',
43395         'Verdana'
43396     ],
43397     
43398     specialChars : [
43399            "&#169;",
43400           "&#174;",     
43401           "&#8482;",    
43402           "&#163;" ,    
43403          // "&#8212;",    
43404           "&#8230;",    
43405           "&#247;" ,    
43406         //  "&#225;" ,     ?? a acute?
43407            "&#8364;"    , //Euro
43408        //   "&#8220;"    ,
43409         //  "&#8221;"    ,
43410         //  "&#8226;"    ,
43411           "&#176;"  //   , // degrees
43412
43413          // "&#233;"     , // e ecute
43414          // "&#250;"     , // u ecute?
43415     ],
43416     
43417     specialElements : [
43418         {
43419             text: "Insert Table",
43420             xtype: 'MenuItem',
43421             xns : Roo.Menu,
43422             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43423                 
43424         },
43425         {    
43426             text: "Insert Image",
43427             xtype: 'MenuItem',
43428             xns : Roo.Menu,
43429             ihtml : '<img src="about:blank"/>'
43430             
43431         }
43432         
43433          
43434     ],
43435     
43436     
43437     inputElements : [ 
43438             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43439             "input:submit", "input:button", "select", "textarea", "label" ],
43440     formats : [
43441         ["p"] ,  
43442         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43443         ["pre"],[ "code"], 
43444         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43445         ['div'],['span']
43446     ],
43447     
43448     cleanStyles : [
43449         "font-size"
43450     ],
43451      /**
43452      * @cfg {String} defaultFont default font to use.
43453      */
43454     defaultFont: 'tahoma',
43455    
43456     fontSelect : false,
43457     
43458     
43459     formatCombo : false,
43460     
43461     init : function(editor)
43462     {
43463         this.editor = editor;
43464         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43465         var editorcore = this.editorcore;
43466         
43467         var _t = this;
43468         
43469         var fid = editorcore.frameId;
43470         var etb = this;
43471         function btn(id, toggle, handler){
43472             var xid = fid + '-'+ id ;
43473             return {
43474                 id : xid,
43475                 cmd : id,
43476                 cls : 'x-btn-icon x-edit-'+id,
43477                 enableToggle:toggle !== false,
43478                 scope: _t, // was editor...
43479                 handler:handler||_t.relayBtnCmd,
43480                 clickEvent:'mousedown',
43481                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43482                 tabIndex:-1
43483             };
43484         }
43485         
43486         
43487         
43488         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43489         this.tb = tb;
43490          // stop form submits
43491         tb.el.on('click', function(e){
43492             e.preventDefault(); // what does this do?
43493         });
43494
43495         if(!this.disable.font) { // && !Roo.isSafari){
43496             /* why no safari for fonts 
43497             editor.fontSelect = tb.el.createChild({
43498                 tag:'select',
43499                 tabIndex: -1,
43500                 cls:'x-font-select',
43501                 html: this.createFontOptions()
43502             });
43503             
43504             editor.fontSelect.on('change', function(){
43505                 var font = editor.fontSelect.dom.value;
43506                 editor.relayCmd('fontname', font);
43507                 editor.deferFocus();
43508             }, editor);
43509             
43510             tb.add(
43511                 editor.fontSelect.dom,
43512                 '-'
43513             );
43514             */
43515             
43516         };
43517         if(!this.disable.formats){
43518             this.formatCombo = new Roo.form.ComboBox({
43519                 store: new Roo.data.SimpleStore({
43520                     id : 'tag',
43521                     fields: ['tag'],
43522                     data : this.formats // from states.js
43523                 }),
43524                 blockFocus : true,
43525                 name : '',
43526                 //autoCreate : {tag: "div",  size: "20"},
43527                 displayField:'tag',
43528                 typeAhead: false,
43529                 mode: 'local',
43530                 editable : false,
43531                 triggerAction: 'all',
43532                 emptyText:'Add tag',
43533                 selectOnFocus:true,
43534                 width:135,
43535                 listeners : {
43536                     'select': function(c, r, i) {
43537                         editorcore.insertTag(r.get('tag'));
43538                         editor.focus();
43539                     }
43540                 }
43541
43542             });
43543             tb.addField(this.formatCombo);
43544             
43545         }
43546         
43547         if(!this.disable.format){
43548             tb.add(
43549                 btn('bold'),
43550                 btn('italic'),
43551                 btn('underline')
43552             );
43553         };
43554         if(!this.disable.fontSize){
43555             tb.add(
43556                 '-',
43557                 
43558                 
43559                 btn('increasefontsize', false, editorcore.adjustFont),
43560                 btn('decreasefontsize', false, editorcore.adjustFont)
43561             );
43562         };
43563         
43564         
43565         if(!this.disable.colors){
43566             tb.add(
43567                 '-', {
43568                     id:editorcore.frameId +'-forecolor',
43569                     cls:'x-btn-icon x-edit-forecolor',
43570                     clickEvent:'mousedown',
43571                     tooltip: this.buttonTips['forecolor'] || undefined,
43572                     tabIndex:-1,
43573                     menu : new Roo.menu.ColorMenu({
43574                         allowReselect: true,
43575                         focus: Roo.emptyFn,
43576                         value:'000000',
43577                         plain:true,
43578                         selectHandler: function(cp, color){
43579                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43580                             editor.deferFocus();
43581                         },
43582                         scope: editorcore,
43583                         clickEvent:'mousedown'
43584                     })
43585                 }, {
43586                     id:editorcore.frameId +'backcolor',
43587                     cls:'x-btn-icon x-edit-backcolor',
43588                     clickEvent:'mousedown',
43589                     tooltip: this.buttonTips['backcolor'] || undefined,
43590                     tabIndex:-1,
43591                     menu : new Roo.menu.ColorMenu({
43592                         focus: Roo.emptyFn,
43593                         value:'FFFFFF',
43594                         plain:true,
43595                         allowReselect: true,
43596                         selectHandler: function(cp, color){
43597                             if(Roo.isGecko){
43598                                 editorcore.execCmd('useCSS', false);
43599                                 editorcore.execCmd('hilitecolor', color);
43600                                 editorcore.execCmd('useCSS', true);
43601                                 editor.deferFocus();
43602                             }else{
43603                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43604                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43605                                 editor.deferFocus();
43606                             }
43607                         },
43608                         scope:editorcore,
43609                         clickEvent:'mousedown'
43610                     })
43611                 }
43612             );
43613         };
43614         // now add all the items...
43615         
43616
43617         if(!this.disable.alignments){
43618             tb.add(
43619                 '-',
43620                 btn('justifyleft'),
43621                 btn('justifycenter'),
43622                 btn('justifyright')
43623             );
43624         };
43625
43626         //if(!Roo.isSafari){
43627             if(!this.disable.links){
43628                 tb.add(
43629                     '-',
43630                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43631                 );
43632             };
43633
43634             if(!this.disable.lists){
43635                 tb.add(
43636                     '-',
43637                     btn('insertorderedlist'),
43638                     btn('insertunorderedlist')
43639                 );
43640             }
43641             if(!this.disable.sourceEdit){
43642                 tb.add(
43643                     '-',
43644                     btn('sourceedit', true, function(btn){
43645                         this.toggleSourceEdit(btn.pressed);
43646                     })
43647                 );
43648             }
43649         //}
43650         
43651         var smenu = { };
43652         // special menu.. - needs to be tidied up..
43653         if (!this.disable.special) {
43654             smenu = {
43655                 text: "&#169;",
43656                 cls: 'x-edit-none',
43657                 
43658                 menu : {
43659                     items : []
43660                 }
43661             };
43662             for (var i =0; i < this.specialChars.length; i++) {
43663                 smenu.menu.items.push({
43664                     
43665                     html: this.specialChars[i],
43666                     handler: function(a,b) {
43667                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43668                         //editor.insertAtCursor(a.html);
43669                         
43670                     },
43671                     tabIndex:-1
43672                 });
43673             }
43674             
43675             
43676             tb.add(smenu);
43677             
43678             
43679         }
43680         
43681         var cmenu = { };
43682         if (!this.disable.cleanStyles) {
43683             cmenu = {
43684                 cls: 'x-btn-icon x-btn-clear',
43685                 
43686                 menu : {
43687                     items : []
43688                 }
43689             };
43690             for (var i =0; i < this.cleanStyles.length; i++) {
43691                 cmenu.menu.items.push({
43692                     actiontype : this.cleanStyles[i],
43693                     html: 'Remove ' + this.cleanStyles[i],
43694                     handler: function(a,b) {
43695 //                        Roo.log(a);
43696 //                        Roo.log(b);
43697                         var c = Roo.get(editorcore.doc.body);
43698                         c.select('[style]').each(function(s) {
43699                             s.dom.style.removeProperty(a.actiontype);
43700                         });
43701                         editorcore.syncValue();
43702                     },
43703                     tabIndex:-1
43704                 });
43705             }
43706             cmenu.menu.items.push({
43707                 actiontype : 'word',
43708                 html: 'Remove MS Word Formating',
43709                 handler: function(a,b) {
43710                     editorcore.cleanWord();
43711                     editorcore.syncValue();
43712                 },
43713                 tabIndex:-1
43714             });
43715             
43716             cmenu.menu.items.push({
43717                 actiontype : 'all',
43718                 html: 'Remove All Styles',
43719                 handler: function(a,b) {
43720                     
43721                     var c = Roo.get(editorcore.doc.body);
43722                     c.select('[style]').each(function(s) {
43723                         s.dom.removeAttribute('style');
43724                     });
43725                     editorcore.syncValue();
43726                 },
43727                 tabIndex:-1
43728             });
43729              cmenu.menu.items.push({
43730                 actiontype : 'word',
43731                 html: 'Tidy HTML Source',
43732                 handler: function(a,b) {
43733                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43734                     editorcore.syncValue();
43735                 },
43736                 tabIndex:-1
43737             });
43738             
43739             
43740             tb.add(cmenu);
43741         }
43742          
43743         if (!this.disable.specialElements) {
43744             var semenu = {
43745                 text: "Other;",
43746                 cls: 'x-edit-none',
43747                 menu : {
43748                     items : []
43749                 }
43750             };
43751             for (var i =0; i < this.specialElements.length; i++) {
43752                 semenu.menu.items.push(
43753                     Roo.apply({ 
43754                         handler: function(a,b) {
43755                             editor.insertAtCursor(this.ihtml);
43756                         }
43757                     }, this.specialElements[i])
43758                 );
43759                     
43760             }
43761             
43762             tb.add(semenu);
43763             
43764             
43765         }
43766          
43767         
43768         if (this.btns) {
43769             for(var i =0; i< this.btns.length;i++) {
43770                 var b = Roo.factory(this.btns[i],Roo.form);
43771                 b.cls =  'x-edit-none';
43772                 
43773                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43774                     b.cls += ' x-init-enable';
43775                 }
43776                 
43777                 b.scope = editorcore;
43778                 tb.add(b);
43779             }
43780         
43781         }
43782         
43783         
43784         
43785         // disable everything...
43786         
43787         this.tb.items.each(function(item){
43788             
43789            if(
43790                 item.id != editorcore.frameId+ '-sourceedit' && 
43791                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43792             ){
43793                 
43794                 item.disable();
43795             }
43796         });
43797         this.rendered = true;
43798         
43799         // the all the btns;
43800         editor.on('editorevent', this.updateToolbar, this);
43801         // other toolbars need to implement this..
43802         //editor.on('editmodechange', this.updateToolbar, this);
43803     },
43804     
43805     
43806     relayBtnCmd : function(btn) {
43807         this.editorcore.relayCmd(btn.cmd);
43808     },
43809     // private used internally
43810     createLink : function(){
43811         Roo.log("create link?");
43812         var url = prompt(this.createLinkText, this.defaultLinkValue);
43813         if(url && url != 'http:/'+'/'){
43814             this.editorcore.relayCmd('createlink', url);
43815         }
43816     },
43817
43818     
43819     /**
43820      * Protected method that will not generally be called directly. It triggers
43821      * a toolbar update by reading the markup state of the current selection in the editor.
43822      */
43823     updateToolbar: function(){
43824
43825         if(!this.editorcore.activated){
43826             this.editor.onFirstFocus();
43827             return;
43828         }
43829
43830         var btns = this.tb.items.map, 
43831             doc = this.editorcore.doc,
43832             frameId = this.editorcore.frameId;
43833
43834         if(!this.disable.font && !Roo.isSafari){
43835             /*
43836             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43837             if(name != this.fontSelect.dom.value){
43838                 this.fontSelect.dom.value = name;
43839             }
43840             */
43841         }
43842         if(!this.disable.format){
43843             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43844             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43845             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43846         }
43847         if(!this.disable.alignments){
43848             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43849             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43850             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43851         }
43852         if(!Roo.isSafari && !this.disable.lists){
43853             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43854             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43855         }
43856         
43857         var ans = this.editorcore.getAllAncestors();
43858         if (this.formatCombo) {
43859             
43860             
43861             var store = this.formatCombo.store;
43862             this.formatCombo.setValue("");
43863             for (var i =0; i < ans.length;i++) {
43864                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43865                     // select it..
43866                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43867                     break;
43868                 }
43869             }
43870         }
43871         
43872         
43873         
43874         // hides menus... - so this cant be on a menu...
43875         Roo.menu.MenuMgr.hideAll();
43876
43877         //this.editorsyncValue();
43878     },
43879    
43880     
43881     createFontOptions : function(){
43882         var buf = [], fs = this.fontFamilies, ff, lc;
43883         
43884         
43885         
43886         for(var i = 0, len = fs.length; i< len; i++){
43887             ff = fs[i];
43888             lc = ff.toLowerCase();
43889             buf.push(
43890                 '<option value="',lc,'" style="font-family:',ff,';"',
43891                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43892                     ff,
43893                 '</option>'
43894             );
43895         }
43896         return buf.join('');
43897     },
43898     
43899     toggleSourceEdit : function(sourceEditMode){
43900         
43901         Roo.log("toolbar toogle");
43902         if(sourceEditMode === undefined){
43903             sourceEditMode = !this.sourceEditMode;
43904         }
43905         this.sourceEditMode = sourceEditMode === true;
43906         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43907         // just toggle the button?
43908         if(btn.pressed !== this.sourceEditMode){
43909             btn.toggle(this.sourceEditMode);
43910             return;
43911         }
43912         
43913         if(sourceEditMode){
43914             Roo.log("disabling buttons");
43915             this.tb.items.each(function(item){
43916                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43917                     item.disable();
43918                 }
43919             });
43920           
43921         }else{
43922             Roo.log("enabling buttons");
43923             if(this.editorcore.initialized){
43924                 this.tb.items.each(function(item){
43925                     item.enable();
43926                 });
43927             }
43928             
43929         }
43930         Roo.log("calling toggole on editor");
43931         // tell the editor that it's been pressed..
43932         this.editor.toggleSourceEdit(sourceEditMode);
43933        
43934     },
43935      /**
43936      * Object collection of toolbar tooltips for the buttons in the editor. The key
43937      * is the command id associated with that button and the value is a valid QuickTips object.
43938      * For example:
43939 <pre><code>
43940 {
43941     bold : {
43942         title: 'Bold (Ctrl+B)',
43943         text: 'Make the selected text bold.',
43944         cls: 'x-html-editor-tip'
43945     },
43946     italic : {
43947         title: 'Italic (Ctrl+I)',
43948         text: 'Make the selected text italic.',
43949         cls: 'x-html-editor-tip'
43950     },
43951     ...
43952 </code></pre>
43953     * @type Object
43954      */
43955     buttonTips : {
43956         bold : {
43957             title: 'Bold (Ctrl+B)',
43958             text: 'Make the selected text bold.',
43959             cls: 'x-html-editor-tip'
43960         },
43961         italic : {
43962             title: 'Italic (Ctrl+I)',
43963             text: 'Make the selected text italic.',
43964             cls: 'x-html-editor-tip'
43965         },
43966         underline : {
43967             title: 'Underline (Ctrl+U)',
43968             text: 'Underline the selected text.',
43969             cls: 'x-html-editor-tip'
43970         },
43971         increasefontsize : {
43972             title: 'Grow Text',
43973             text: 'Increase the font size.',
43974             cls: 'x-html-editor-tip'
43975         },
43976         decreasefontsize : {
43977             title: 'Shrink Text',
43978             text: 'Decrease the font size.',
43979             cls: 'x-html-editor-tip'
43980         },
43981         backcolor : {
43982             title: 'Text Highlight Color',
43983             text: 'Change the background color of the selected text.',
43984             cls: 'x-html-editor-tip'
43985         },
43986         forecolor : {
43987             title: 'Font Color',
43988             text: 'Change the color of the selected text.',
43989             cls: 'x-html-editor-tip'
43990         },
43991         justifyleft : {
43992             title: 'Align Text Left',
43993             text: 'Align text to the left.',
43994             cls: 'x-html-editor-tip'
43995         },
43996         justifycenter : {
43997             title: 'Center Text',
43998             text: 'Center text in the editor.',
43999             cls: 'x-html-editor-tip'
44000         },
44001         justifyright : {
44002             title: 'Align Text Right',
44003             text: 'Align text to the right.',
44004             cls: 'x-html-editor-tip'
44005         },
44006         insertunorderedlist : {
44007             title: 'Bullet List',
44008             text: 'Start a bulleted list.',
44009             cls: 'x-html-editor-tip'
44010         },
44011         insertorderedlist : {
44012             title: 'Numbered List',
44013             text: 'Start a numbered list.',
44014             cls: 'x-html-editor-tip'
44015         },
44016         createlink : {
44017             title: 'Hyperlink',
44018             text: 'Make the selected text a hyperlink.',
44019             cls: 'x-html-editor-tip'
44020         },
44021         sourceedit : {
44022             title: 'Source Edit',
44023             text: 'Switch to source editing mode.',
44024             cls: 'x-html-editor-tip'
44025         }
44026     },
44027     // private
44028     onDestroy : function(){
44029         if(this.rendered){
44030             
44031             this.tb.items.each(function(item){
44032                 if(item.menu){
44033                     item.menu.removeAll();
44034                     if(item.menu.el){
44035                         item.menu.el.destroy();
44036                     }
44037                 }
44038                 item.destroy();
44039             });
44040              
44041         }
44042     },
44043     onFirstFocus: function() {
44044         this.tb.items.each(function(item){
44045            item.enable();
44046         });
44047     }
44048 });
44049
44050
44051
44052
44053 // <script type="text/javascript">
44054 /*
44055  * Based on
44056  * Ext JS Library 1.1.1
44057  * Copyright(c) 2006-2007, Ext JS, LLC.
44058  *  
44059  
44060  */
44061
44062  
44063 /**
44064  * @class Roo.form.HtmlEditor.ToolbarContext
44065  * Context Toolbar
44066  * 
44067  * Usage:
44068  *
44069  new Roo.form.HtmlEditor({
44070     ....
44071     toolbars : [
44072         { xtype: 'ToolbarStandard', styles : {} }
44073         { xtype: 'ToolbarContext', disable : {} }
44074     ]
44075 })
44076
44077      
44078  * 
44079  * @config : {Object} disable List of elements to disable.. (not done yet.)
44080  * @config : {Object} styles  Map of styles available.
44081  * 
44082  */
44083
44084 Roo.form.HtmlEditor.ToolbarContext = function(config)
44085 {
44086     
44087     Roo.apply(this, config);
44088     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44089     // dont call parent... till later.
44090     this.styles = this.styles || {};
44091 }
44092
44093  
44094
44095 Roo.form.HtmlEditor.ToolbarContext.types = {
44096     'IMG' : {
44097         width : {
44098             title: "Width",
44099             width: 40
44100         },
44101         height:  {
44102             title: "Height",
44103             width: 40
44104         },
44105         align: {
44106             title: "Align",
44107             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44108             width : 80
44109             
44110         },
44111         border: {
44112             title: "Border",
44113             width: 40
44114         },
44115         alt: {
44116             title: "Alt",
44117             width: 120
44118         },
44119         src : {
44120             title: "Src",
44121             width: 220
44122         }
44123         
44124     },
44125     'A' : {
44126         name : {
44127             title: "Name",
44128             width: 50
44129         },
44130         target:  {
44131             title: "Target",
44132             width: 120
44133         },
44134         href:  {
44135             title: "Href",
44136             width: 220
44137         } // border?
44138         
44139     },
44140     'TABLE' : {
44141         rows : {
44142             title: "Rows",
44143             width: 20
44144         },
44145         cols : {
44146             title: "Cols",
44147             width: 20
44148         },
44149         width : {
44150             title: "Width",
44151             width: 40
44152         },
44153         height : {
44154             title: "Height",
44155             width: 40
44156         },
44157         border : {
44158             title: "Border",
44159             width: 20
44160         }
44161     },
44162     'TD' : {
44163         width : {
44164             title: "Width",
44165             width: 40
44166         },
44167         height : {
44168             title: "Height",
44169             width: 40
44170         },   
44171         align: {
44172             title: "Align",
44173             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44174             width: 80
44175         },
44176         valign: {
44177             title: "Valign",
44178             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44179             width: 80
44180         },
44181         colspan: {
44182             title: "Colspan",
44183             width: 20
44184             
44185         },
44186          'font-family'  : {
44187             title : "Font",
44188             style : 'fontFamily',
44189             displayField: 'display',
44190             optname : 'font-family',
44191             width: 140
44192         }
44193     },
44194     'INPUT' : {
44195         name : {
44196             title: "name",
44197             width: 120
44198         },
44199         value : {
44200             title: "Value",
44201             width: 120
44202         },
44203         width : {
44204             title: "Width",
44205             width: 40
44206         }
44207     },
44208     'LABEL' : {
44209         'for' : {
44210             title: "For",
44211             width: 120
44212         }
44213     },
44214     'TEXTAREA' : {
44215           name : {
44216             title: "name",
44217             width: 120
44218         },
44219         rows : {
44220             title: "Rows",
44221             width: 20
44222         },
44223         cols : {
44224             title: "Cols",
44225             width: 20
44226         }
44227     },
44228     'SELECT' : {
44229         name : {
44230             title: "name",
44231             width: 120
44232         },
44233         selectoptions : {
44234             title: "Options",
44235             width: 200
44236         }
44237     },
44238     
44239     // should we really allow this??
44240     // should this just be 
44241     'BODY' : {
44242         title : {
44243             title: "Title",
44244             width: 200,
44245             disabled : true
44246         }
44247     },
44248     'SPAN' : {
44249         'font-family'  : {
44250             title : "Font",
44251             style : 'fontFamily',
44252             displayField: 'display',
44253             optname : 'font-family',
44254             width: 140
44255         }
44256     },
44257     'DIV' : {
44258         'font-family'  : {
44259             title : "Font",
44260             style : 'fontFamily',
44261             displayField: 'display',
44262             optname : 'font-family',
44263             width: 140
44264         }
44265     },
44266      'P' : {
44267         'font-family'  : {
44268             title : "Font",
44269             style : 'fontFamily',
44270             displayField: 'display',
44271             optname : 'font-family',
44272             width: 140
44273         }
44274     },
44275     
44276     '*' : {
44277         // empty..
44278     }
44279
44280 };
44281
44282 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44283 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44284
44285 Roo.form.HtmlEditor.ToolbarContext.options = {
44286         'font-family'  : [ 
44287                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44288                 [ 'Courier New', 'Courier New'],
44289                 [ 'Tahoma', 'Tahoma'],
44290                 [ 'Times New Roman,serif', 'Times'],
44291                 [ 'Verdana','Verdana' ]
44292         ]
44293 };
44294
44295 // fixme - these need to be configurable..
44296  
44297
44298 Roo.form.HtmlEditor.ToolbarContext.types
44299
44300
44301 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44302     
44303     tb: false,
44304     
44305     rendered: false,
44306     
44307     editor : false,
44308     editorcore : false,
44309     /**
44310      * @cfg {Object} disable  List of toolbar elements to disable
44311          
44312      */
44313     disable : false,
44314     /**
44315      * @cfg {Object} styles List of styles 
44316      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44317      *
44318      * These must be defined in the page, so they get rendered correctly..
44319      * .headline { }
44320      * TD.underline { }
44321      * 
44322      */
44323     styles : false,
44324     
44325     options: false,
44326     
44327     toolbars : false,
44328     
44329     init : function(editor)
44330     {
44331         this.editor = editor;
44332         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44333         var editorcore = this.editorcore;
44334         
44335         var fid = editorcore.frameId;
44336         var etb = this;
44337         function btn(id, toggle, handler){
44338             var xid = fid + '-'+ id ;
44339             return {
44340                 id : xid,
44341                 cmd : id,
44342                 cls : 'x-btn-icon x-edit-'+id,
44343                 enableToggle:toggle !== false,
44344                 scope: editorcore, // was editor...
44345                 handler:handler||editorcore.relayBtnCmd,
44346                 clickEvent:'mousedown',
44347                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44348                 tabIndex:-1
44349             };
44350         }
44351         // create a new element.
44352         var wdiv = editor.wrap.createChild({
44353                 tag: 'div'
44354             }, editor.wrap.dom.firstChild.nextSibling, true);
44355         
44356         // can we do this more than once??
44357         
44358          // stop form submits
44359       
44360  
44361         // disable everything...
44362         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44363         this.toolbars = {};
44364            
44365         for (var i in  ty) {
44366           
44367             this.toolbars[i] = this.buildToolbar(ty[i],i);
44368         }
44369         this.tb = this.toolbars.BODY;
44370         this.tb.el.show();
44371         this.buildFooter();
44372         this.footer.show();
44373         editor.on('hide', function( ) { this.footer.hide() }, this);
44374         editor.on('show', function( ) { this.footer.show() }, this);
44375         
44376          
44377         this.rendered = true;
44378         
44379         // the all the btns;
44380         editor.on('editorevent', this.updateToolbar, this);
44381         // other toolbars need to implement this..
44382         //editor.on('editmodechange', this.updateToolbar, this);
44383     },
44384     
44385     
44386     
44387     /**
44388      * Protected method that will not generally be called directly. It triggers
44389      * a toolbar update by reading the markup state of the current selection in the editor.
44390      */
44391     updateToolbar: function(editor,ev,sel){
44392
44393         //Roo.log(ev);
44394         // capture mouse up - this is handy for selecting images..
44395         // perhaps should go somewhere else...
44396         if(!this.editorcore.activated){
44397              this.editor.onFirstFocus();
44398             return;
44399         }
44400         
44401         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44402         // selectNode - might want to handle IE?
44403         if (ev &&
44404             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44405             ev.target && ev.target.tagName == 'IMG') {
44406             // they have click on an image...
44407             // let's see if we can change the selection...
44408             sel = ev.target;
44409          
44410               var nodeRange = sel.ownerDocument.createRange();
44411             try {
44412                 nodeRange.selectNode(sel);
44413             } catch (e) {
44414                 nodeRange.selectNodeContents(sel);
44415             }
44416             //nodeRange.collapse(true);
44417             var s = this.editorcore.win.getSelection();
44418             s.removeAllRanges();
44419             s.addRange(nodeRange);
44420         }  
44421         
44422       
44423         var updateFooter = sel ? false : true;
44424         
44425         
44426         var ans = this.editorcore.getAllAncestors();
44427         
44428         // pick
44429         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44430         
44431         if (!sel) { 
44432             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44433             sel = sel ? sel : this.editorcore.doc.body;
44434             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44435             
44436         }
44437         // pick a menu that exists..
44438         var tn = sel.tagName.toUpperCase();
44439         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44440         
44441         tn = sel.tagName.toUpperCase();
44442         
44443         var lastSel = this.tb.selectedNode
44444         
44445         this.tb.selectedNode = sel;
44446         
44447         // if current menu does not match..
44448         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44449                 
44450             this.tb.el.hide();
44451             ///console.log("show: " + tn);
44452             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44453             this.tb.el.show();
44454             // update name
44455             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44456             
44457             
44458             // update attributes
44459             if (this.tb.fields) {
44460                 this.tb.fields.each(function(e) {
44461                     if (e.stylename) {
44462                         e.setValue(sel.style[e.stylename]);
44463                         return;
44464                     } 
44465                    e.setValue(sel.getAttribute(e.attrname));
44466                 });
44467             }
44468             
44469             var hasStyles = false;
44470             for(var i in this.styles) {
44471                 hasStyles = true;
44472                 break;
44473             }
44474             
44475             // update styles
44476             if (hasStyles) { 
44477                 var st = this.tb.fields.item(0);
44478                 
44479                 st.store.removeAll();
44480                
44481                 
44482                 var cn = sel.className.split(/\s+/);
44483                 
44484                 var avs = [];
44485                 if (this.styles['*']) {
44486                     
44487                     Roo.each(this.styles['*'], function(v) {
44488                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44489                     });
44490                 }
44491                 if (this.styles[tn]) { 
44492                     Roo.each(this.styles[tn], function(v) {
44493                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44494                     });
44495                 }
44496                 
44497                 st.store.loadData(avs);
44498                 st.collapse();
44499                 st.setValue(cn);
44500             }
44501             // flag our selected Node.
44502             this.tb.selectedNode = sel;
44503            
44504            
44505             Roo.menu.MenuMgr.hideAll();
44506
44507         }
44508         
44509         if (!updateFooter) {
44510             //this.footDisp.dom.innerHTML = ''; 
44511             return;
44512         }
44513         // update the footer
44514         //
44515         var html = '';
44516         
44517         this.footerEls = ans.reverse();
44518         Roo.each(this.footerEls, function(a,i) {
44519             if (!a) { return; }
44520             html += html.length ? ' &gt; '  :  '';
44521             
44522             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44523             
44524         });
44525        
44526         // 
44527         var sz = this.footDisp.up('td').getSize();
44528         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44529         this.footDisp.dom.style.marginLeft = '5px';
44530         
44531         this.footDisp.dom.style.overflow = 'hidden';
44532         
44533         this.footDisp.dom.innerHTML = html;
44534             
44535         //this.editorsyncValue();
44536     },
44537      
44538     
44539    
44540        
44541     // private
44542     onDestroy : function(){
44543         if(this.rendered){
44544             
44545             this.tb.items.each(function(item){
44546                 if(item.menu){
44547                     item.menu.removeAll();
44548                     if(item.menu.el){
44549                         item.menu.el.destroy();
44550                     }
44551                 }
44552                 item.destroy();
44553             });
44554              
44555         }
44556     },
44557     onFirstFocus: function() {
44558         // need to do this for all the toolbars..
44559         this.tb.items.each(function(item){
44560            item.enable();
44561         });
44562     },
44563     buildToolbar: function(tlist, nm)
44564     {
44565         var editor = this.editor;
44566         var editorcore = this.editorcore;
44567          // create a new element.
44568         var wdiv = editor.wrap.createChild({
44569                 tag: 'div'
44570             }, editor.wrap.dom.firstChild.nextSibling, true);
44571         
44572        
44573         var tb = new Roo.Toolbar(wdiv);
44574         // add the name..
44575         
44576         tb.add(nm+ ":&nbsp;");
44577         
44578         var styles = [];
44579         for(var i in this.styles) {
44580             styles.push(i);
44581         }
44582         
44583         // styles...
44584         if (styles && styles.length) {
44585             
44586             // this needs a multi-select checkbox...
44587             tb.addField( new Roo.form.ComboBox({
44588                 store: new Roo.data.SimpleStore({
44589                     id : 'val',
44590                     fields: ['val', 'selected'],
44591                     data : [] 
44592                 }),
44593                 name : '-roo-edit-className',
44594                 attrname : 'className',
44595                 displayField: 'val',
44596                 typeAhead: false,
44597                 mode: 'local',
44598                 editable : false,
44599                 triggerAction: 'all',
44600                 emptyText:'Select Style',
44601                 selectOnFocus:true,
44602                 width: 130,
44603                 listeners : {
44604                     'select': function(c, r, i) {
44605                         // initial support only for on class per el..
44606                         tb.selectedNode.className =  r ? r.get('val') : '';
44607                         editorcore.syncValue();
44608                     }
44609                 }
44610     
44611             }));
44612         }
44613         
44614         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44615         var tbops = tbc.options;
44616         
44617         for (var i in tlist) {
44618             
44619             var item = tlist[i];
44620             tb.add(item.title + ":&nbsp;");
44621             
44622             
44623             //optname == used so you can configure the options available..
44624             var opts = item.opts ? item.opts : false;
44625             if (item.optname) {
44626                 opts = tbops[item.optname];
44627            
44628             }
44629             
44630             if (opts) {
44631                 // opts == pulldown..
44632                 tb.addField( new Roo.form.ComboBox({
44633                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44634                         id : 'val',
44635                         fields: ['val', 'display'],
44636                         data : opts  
44637                     }),
44638                     name : '-roo-edit-' + i,
44639                     attrname : i,
44640                     stylename : item.style ? item.style : false,
44641                     displayField: item.displayField ? item.displayField : 'val',
44642                     valueField :  'val',
44643                     typeAhead: false,
44644                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44645                     editable : false,
44646                     triggerAction: 'all',
44647                     emptyText:'Select',
44648                     selectOnFocus:true,
44649                     width: item.width ? item.width  : 130,
44650                     listeners : {
44651                         'select': function(c, r, i) {
44652                             if (c.stylename) {
44653                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44654                                 return;
44655                             }
44656                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44657                         }
44658                     }
44659
44660                 }));
44661                 continue;
44662                     
44663                  
44664                 
44665                 tb.addField( new Roo.form.TextField({
44666                     name: i,
44667                     width: 100,
44668                     //allowBlank:false,
44669                     value: ''
44670                 }));
44671                 continue;
44672             }
44673             tb.addField( new Roo.form.TextField({
44674                 name: '-roo-edit-' + i,
44675                 attrname : i,
44676                 
44677                 width: item.width,
44678                 //allowBlank:true,
44679                 value: '',
44680                 listeners: {
44681                     'change' : function(f, nv, ov) {
44682                         tb.selectedNode.setAttribute(f.attrname, nv);
44683                     }
44684                 }
44685             }));
44686              
44687         }
44688         
44689         var _this = this;
44690         
44691         if(nm == 'BODY'){
44692             tb.addSeparator();
44693         
44694             tb.addButton( {
44695                 text: 'Stylesheets',
44696
44697                 listeners : {
44698                     click : function ()
44699                     {
44700                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44701                     }
44702                 }
44703             });
44704         }
44705         
44706         tb.addFill();
44707         tb.addButton( {
44708             text: 'Remove Tag',
44709     
44710             listeners : {
44711                 click : function ()
44712                 {
44713                     // remove
44714                     // undo does not work.
44715                      
44716                     var sn = tb.selectedNode;
44717                     
44718                     var pn = sn.parentNode;
44719                     
44720                     var stn =  sn.childNodes[0];
44721                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44722                     while (sn.childNodes.length) {
44723                         var node = sn.childNodes[0];
44724                         sn.removeChild(node);
44725                         //Roo.log(node);
44726                         pn.insertBefore(node, sn);
44727                         
44728                     }
44729                     pn.removeChild(sn);
44730                     var range = editorcore.createRange();
44731         
44732                     range.setStart(stn,0);
44733                     range.setEnd(en,0); //????
44734                     //range.selectNode(sel);
44735                     
44736                     
44737                     var selection = editorcore.getSelection();
44738                     selection.removeAllRanges();
44739                     selection.addRange(range);
44740                     
44741                     
44742                     
44743                     //_this.updateToolbar(null, null, pn);
44744                     _this.updateToolbar(null, null, null);
44745                     _this.footDisp.dom.innerHTML = ''; 
44746                 }
44747             }
44748             
44749                     
44750                 
44751             
44752         });
44753         
44754         
44755         tb.el.on('click', function(e){
44756             e.preventDefault(); // what does this do?
44757         });
44758         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44759         tb.el.hide();
44760         tb.name = nm;
44761         // dont need to disable them... as they will get hidden
44762         return tb;
44763          
44764         
44765     },
44766     buildFooter : function()
44767     {
44768         
44769         var fel = this.editor.wrap.createChild();
44770         this.footer = new Roo.Toolbar(fel);
44771         // toolbar has scrolly on left / right?
44772         var footDisp= new Roo.Toolbar.Fill();
44773         var _t = this;
44774         this.footer.add(
44775             {
44776                 text : '&lt;',
44777                 xtype: 'Button',
44778                 handler : function() {
44779                     _t.footDisp.scrollTo('left',0,true)
44780                 }
44781             }
44782         );
44783         this.footer.add( footDisp );
44784         this.footer.add( 
44785             {
44786                 text : '&gt;',
44787                 xtype: 'Button',
44788                 handler : function() {
44789                     // no animation..
44790                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44791                 }
44792             }
44793         );
44794         var fel = Roo.get(footDisp.el);
44795         fel.addClass('x-editor-context');
44796         this.footDispWrap = fel; 
44797         this.footDispWrap.overflow  = 'hidden';
44798         
44799         this.footDisp = fel.createChild();
44800         this.footDispWrap.on('click', this.onContextClick, this)
44801         
44802         
44803     },
44804     onContextClick : function (ev,dom)
44805     {
44806         ev.preventDefault();
44807         var  cn = dom.className;
44808         //Roo.log(cn);
44809         if (!cn.match(/x-ed-loc-/)) {
44810             return;
44811         }
44812         var n = cn.split('-').pop();
44813         var ans = this.footerEls;
44814         var sel = ans[n];
44815         
44816          // pick
44817         var range = this.editorcore.createRange();
44818         
44819         range.selectNodeContents(sel);
44820         //range.selectNode(sel);
44821         
44822         
44823         var selection = this.editorcore.getSelection();
44824         selection.removeAllRanges();
44825         selection.addRange(range);
44826         
44827         
44828         
44829         this.updateToolbar(null, null, sel);
44830         
44831         
44832     }
44833     
44834     
44835     
44836     
44837     
44838 });
44839
44840
44841
44842
44843
44844 /*
44845  * Based on:
44846  * Ext JS Library 1.1.1
44847  * Copyright(c) 2006-2007, Ext JS, LLC.
44848  *
44849  * Originally Released Under LGPL - original licence link has changed is not relivant.
44850  *
44851  * Fork - LGPL
44852  * <script type="text/javascript">
44853  */
44854  
44855 /**
44856  * @class Roo.form.BasicForm
44857  * @extends Roo.util.Observable
44858  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44859  * @constructor
44860  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44861  * @param {Object} config Configuration options
44862  */
44863 Roo.form.BasicForm = function(el, config){
44864     this.allItems = [];
44865     this.childForms = [];
44866     Roo.apply(this, config);
44867     /*
44868      * The Roo.form.Field items in this form.
44869      * @type MixedCollection
44870      */
44871      
44872      
44873     this.items = new Roo.util.MixedCollection(false, function(o){
44874         return o.id || (o.id = Roo.id());
44875     });
44876     this.addEvents({
44877         /**
44878          * @event beforeaction
44879          * Fires before any action is performed. Return false to cancel the action.
44880          * @param {Form} this
44881          * @param {Action} action The action to be performed
44882          */
44883         beforeaction: true,
44884         /**
44885          * @event actionfailed
44886          * Fires when an action fails.
44887          * @param {Form} this
44888          * @param {Action} action The action that failed
44889          */
44890         actionfailed : true,
44891         /**
44892          * @event actioncomplete
44893          * Fires when an action is completed.
44894          * @param {Form} this
44895          * @param {Action} action The action that completed
44896          */
44897         actioncomplete : true
44898     });
44899     if(el){
44900         this.initEl(el);
44901     }
44902     Roo.form.BasicForm.superclass.constructor.call(this);
44903 };
44904
44905 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44906     /**
44907      * @cfg {String} method
44908      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44909      */
44910     /**
44911      * @cfg {DataReader} reader
44912      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44913      * This is optional as there is built-in support for processing JSON.
44914      */
44915     /**
44916      * @cfg {DataReader} errorReader
44917      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44918      * This is completely optional as there is built-in support for processing JSON.
44919      */
44920     /**
44921      * @cfg {String} url
44922      * The URL to use for form actions if one isn't supplied in the action options.
44923      */
44924     /**
44925      * @cfg {Boolean} fileUpload
44926      * Set to true if this form is a file upload.
44927      */
44928      
44929     /**
44930      * @cfg {Object} baseParams
44931      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44932      */
44933      /**
44934      
44935     /**
44936      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44937      */
44938     timeout: 30,
44939
44940     // private
44941     activeAction : null,
44942
44943     /**
44944      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44945      * or setValues() data instead of when the form was first created.
44946      */
44947     trackResetOnLoad : false,
44948     
44949     
44950     /**
44951      * childForms - used for multi-tab forms
44952      * @type {Array}
44953      */
44954     childForms : false,
44955     
44956     /**
44957      * allItems - full list of fields.
44958      * @type {Array}
44959      */
44960     allItems : false,
44961     
44962     /**
44963      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44964      * element by passing it or its id or mask the form itself by passing in true.
44965      * @type Mixed
44966      */
44967     waitMsgTarget : false,
44968
44969     // private
44970     initEl : function(el){
44971         this.el = Roo.get(el);
44972         this.id = this.el.id || Roo.id();
44973         this.el.on('submit', this.onSubmit, this);
44974         this.el.addClass('x-form');
44975     },
44976
44977     // private
44978     onSubmit : function(e){
44979         e.stopEvent();
44980     },
44981
44982     /**
44983      * Returns true if client-side validation on the form is successful.
44984      * @return Boolean
44985      */
44986     isValid : function(){
44987         var valid = true;
44988         this.items.each(function(f){
44989            if(!f.validate()){
44990                valid = false;
44991            }
44992         });
44993         return valid;
44994     },
44995
44996     /**
44997      * Returns true if any fields in this form have changed since their original load.
44998      * @return Boolean
44999      */
45000     isDirty : function(){
45001         var dirty = false;
45002         this.items.each(function(f){
45003            if(f.isDirty()){
45004                dirty = true;
45005                return false;
45006            }
45007         });
45008         return dirty;
45009     },
45010
45011     /**
45012      * Performs a predefined action (submit or load) or custom actions you define on this form.
45013      * @param {String} actionName The name of the action type
45014      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45015      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45016      * accept other config options):
45017      * <pre>
45018 Property          Type             Description
45019 ----------------  ---------------  ----------------------------------------------------------------------------------
45020 url               String           The url for the action (defaults to the form's url)
45021 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45022 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45023 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45024                                    validate the form on the client (defaults to false)
45025      * </pre>
45026      * @return {BasicForm} this
45027      */
45028     doAction : function(action, options){
45029         if(typeof action == 'string'){
45030             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45031         }
45032         if(this.fireEvent('beforeaction', this, action) !== false){
45033             this.beforeAction(action);
45034             action.run.defer(100, action);
45035         }
45036         return this;
45037     },
45038
45039     /**
45040      * Shortcut to do a submit action.
45041      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45042      * @return {BasicForm} this
45043      */
45044     submit : function(options){
45045         this.doAction('submit', options);
45046         return this;
45047     },
45048
45049     /**
45050      * Shortcut to do a load action.
45051      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45052      * @return {BasicForm} this
45053      */
45054     load : function(options){
45055         this.doAction('load', options);
45056         return this;
45057     },
45058
45059     /**
45060      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45061      * @param {Record} record The record to edit
45062      * @return {BasicForm} this
45063      */
45064     updateRecord : function(record){
45065         record.beginEdit();
45066         var fs = record.fields;
45067         fs.each(function(f){
45068             var field = this.findField(f.name);
45069             if(field){
45070                 record.set(f.name, field.getValue());
45071             }
45072         }, this);
45073         record.endEdit();
45074         return this;
45075     },
45076
45077     /**
45078      * Loads an Roo.data.Record into this form.
45079      * @param {Record} record The record to load
45080      * @return {BasicForm} this
45081      */
45082     loadRecord : function(record){
45083         this.setValues(record.data);
45084         return this;
45085     },
45086
45087     // private
45088     beforeAction : function(action){
45089         var o = action.options;
45090         
45091        
45092         if(this.waitMsgTarget === true){
45093             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45094         }else if(this.waitMsgTarget){
45095             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45096             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45097         }else {
45098             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45099         }
45100          
45101     },
45102
45103     // private
45104     afterAction : function(action, success){
45105         this.activeAction = null;
45106         var o = action.options;
45107         
45108         if(this.waitMsgTarget === true){
45109             this.el.unmask();
45110         }else if(this.waitMsgTarget){
45111             this.waitMsgTarget.unmask();
45112         }else{
45113             Roo.MessageBox.updateProgress(1);
45114             Roo.MessageBox.hide();
45115         }
45116          
45117         if(success){
45118             if(o.reset){
45119                 this.reset();
45120             }
45121             Roo.callback(o.success, o.scope, [this, action]);
45122             this.fireEvent('actioncomplete', this, action);
45123             
45124         }else{
45125             
45126             // failure condition..
45127             // we have a scenario where updates need confirming.
45128             // eg. if a locking scenario exists..
45129             // we look for { errors : { needs_confirm : true }} in the response.
45130             if (
45131                 (typeof(action.result) != 'undefined')  &&
45132                 (typeof(action.result.errors) != 'undefined')  &&
45133                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45134            ){
45135                 var _t = this;
45136                 Roo.MessageBox.confirm(
45137                     "Change requires confirmation",
45138                     action.result.errorMsg,
45139                     function(r) {
45140                         if (r != 'yes') {
45141                             return;
45142                         }
45143                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45144                     }
45145                     
45146                 );
45147                 
45148                 
45149                 
45150                 return;
45151             }
45152             
45153             Roo.callback(o.failure, o.scope, [this, action]);
45154             // show an error message if no failed handler is set..
45155             if (!this.hasListener('actionfailed')) {
45156                 Roo.MessageBox.alert("Error",
45157                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45158                         action.result.errorMsg :
45159                         "Saving Failed, please check your entries or try again"
45160                 );
45161             }
45162             
45163             this.fireEvent('actionfailed', this, action);
45164         }
45165         
45166     },
45167
45168     /**
45169      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45170      * @param {String} id The value to search for
45171      * @return Field
45172      */
45173     findField : function(id){
45174         var field = this.items.get(id);
45175         if(!field){
45176             this.items.each(function(f){
45177                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45178                     field = f;
45179                     return false;
45180                 }
45181             });
45182         }
45183         return field || null;
45184     },
45185
45186     /**
45187      * Add a secondary form to this one, 
45188      * Used to provide tabbed forms. One form is primary, with hidden values 
45189      * which mirror the elements from the other forms.
45190      * 
45191      * @param {Roo.form.Form} form to add.
45192      * 
45193      */
45194     addForm : function(form)
45195     {
45196        
45197         if (this.childForms.indexOf(form) > -1) {
45198             // already added..
45199             return;
45200         }
45201         this.childForms.push(form);
45202         var n = '';
45203         Roo.each(form.allItems, function (fe) {
45204             
45205             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45206             if (this.findField(n)) { // already added..
45207                 return;
45208             }
45209             var add = new Roo.form.Hidden({
45210                 name : n
45211             });
45212             add.render(this.el);
45213             
45214             this.add( add );
45215         }, this);
45216         
45217     },
45218     /**
45219      * Mark fields in this form invalid in bulk.
45220      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45221      * @return {BasicForm} this
45222      */
45223     markInvalid : function(errors){
45224         if(errors instanceof Array){
45225             for(var i = 0, len = errors.length; i < len; i++){
45226                 var fieldError = errors[i];
45227                 var f = this.findField(fieldError.id);
45228                 if(f){
45229                     f.markInvalid(fieldError.msg);
45230                 }
45231             }
45232         }else{
45233             var field, id;
45234             for(id in errors){
45235                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45236                     field.markInvalid(errors[id]);
45237                 }
45238             }
45239         }
45240         Roo.each(this.childForms || [], function (f) {
45241             f.markInvalid(errors);
45242         });
45243         
45244         return this;
45245     },
45246
45247     /**
45248      * Set values for fields in this form in bulk.
45249      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45250      * @return {BasicForm} this
45251      */
45252     setValues : function(values){
45253         if(values instanceof Array){ // array of objects
45254             for(var i = 0, len = values.length; i < len; i++){
45255                 var v = values[i];
45256                 var f = this.findField(v.id);
45257                 if(f){
45258                     f.setValue(v.value);
45259                     if(this.trackResetOnLoad){
45260                         f.originalValue = f.getValue();
45261                     }
45262                 }
45263             }
45264         }else{ // object hash
45265             var field, id;
45266             for(id in values){
45267                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45268                     
45269                     if (field.setFromData && 
45270                         field.valueField && 
45271                         field.displayField &&
45272                         // combos' with local stores can 
45273                         // be queried via setValue()
45274                         // to set their value..
45275                         (field.store && !field.store.isLocal)
45276                         ) {
45277                         // it's a combo
45278                         var sd = { };
45279                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45280                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45281                         field.setFromData(sd);
45282                         
45283                     } else {
45284                         field.setValue(values[id]);
45285                     }
45286                     
45287                     
45288                     if(this.trackResetOnLoad){
45289                         field.originalValue = field.getValue();
45290                     }
45291                 }
45292             }
45293         }
45294          
45295         Roo.each(this.childForms || [], function (f) {
45296             f.setValues(values);
45297         });
45298                 
45299         return this;
45300     },
45301
45302     /**
45303      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45304      * they are returned as an array.
45305      * @param {Boolean} asString
45306      * @return {Object}
45307      */
45308     getValues : function(asString){
45309         if (this.childForms) {
45310             // copy values from the child forms
45311             Roo.each(this.childForms, function (f) {
45312                 this.setValues(f.getValues());
45313             }, this);
45314         }
45315         
45316         
45317         
45318         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45319         if(asString === true){
45320             return fs;
45321         }
45322         return Roo.urlDecode(fs);
45323     },
45324     
45325     /**
45326      * Returns the fields in this form as an object with key/value pairs. 
45327      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45328      * @return {Object}
45329      */
45330     getFieldValues : function(with_hidden)
45331     {
45332         if (this.childForms) {
45333             // copy values from the child forms
45334             // should this call getFieldValues - probably not as we do not currently copy
45335             // hidden fields when we generate..
45336             Roo.each(this.childForms, function (f) {
45337                 this.setValues(f.getValues());
45338             }, this);
45339         }
45340         
45341         var ret = {};
45342         this.items.each(function(f){
45343             if (!f.getName()) {
45344                 return;
45345             }
45346             var v = f.getValue();
45347             if (f.inputType =='radio') {
45348                 if (typeof(ret[f.getName()]) == 'undefined') {
45349                     ret[f.getName()] = ''; // empty..
45350                 }
45351                 
45352                 if (!f.el.dom.checked) {
45353                     return;
45354                     
45355                 }
45356                 v = f.el.dom.value;
45357                 
45358             }
45359             
45360             // not sure if this supported any more..
45361             if ((typeof(v) == 'object') && f.getRawValue) {
45362                 v = f.getRawValue() ; // dates..
45363             }
45364             // combo boxes where name != hiddenName...
45365             if (f.name != f.getName()) {
45366                 ret[f.name] = f.getRawValue();
45367             }
45368             ret[f.getName()] = v;
45369         });
45370         
45371         return ret;
45372     },
45373
45374     /**
45375      * Clears all invalid messages in this form.
45376      * @return {BasicForm} this
45377      */
45378     clearInvalid : function(){
45379         this.items.each(function(f){
45380            f.clearInvalid();
45381         });
45382         
45383         Roo.each(this.childForms || [], function (f) {
45384             f.clearInvalid();
45385         });
45386         
45387         
45388         return this;
45389     },
45390
45391     /**
45392      * Resets this form.
45393      * @return {BasicForm} this
45394      */
45395     reset : function(){
45396         this.items.each(function(f){
45397             f.reset();
45398         });
45399         
45400         Roo.each(this.childForms || [], function (f) {
45401             f.reset();
45402         });
45403        
45404         
45405         return this;
45406     },
45407
45408     /**
45409      * Add Roo.form components to this form.
45410      * @param {Field} field1
45411      * @param {Field} field2 (optional)
45412      * @param {Field} etc (optional)
45413      * @return {BasicForm} this
45414      */
45415     add : function(){
45416         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45417         return this;
45418     },
45419
45420
45421     /**
45422      * Removes a field from the items collection (does NOT remove its markup).
45423      * @param {Field} field
45424      * @return {BasicForm} this
45425      */
45426     remove : function(field){
45427         this.items.remove(field);
45428         return this;
45429     },
45430
45431     /**
45432      * Looks at the fields in this form, checks them for an id attribute,
45433      * and calls applyTo on the existing dom element with that id.
45434      * @return {BasicForm} this
45435      */
45436     render : function(){
45437         this.items.each(function(f){
45438             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45439                 f.applyTo(f.id);
45440             }
45441         });
45442         return this;
45443     },
45444
45445     /**
45446      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45447      * @param {Object} values
45448      * @return {BasicForm} this
45449      */
45450     applyToFields : function(o){
45451         this.items.each(function(f){
45452            Roo.apply(f, o);
45453         });
45454         return this;
45455     },
45456
45457     /**
45458      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45459      * @param {Object} values
45460      * @return {BasicForm} this
45461      */
45462     applyIfToFields : function(o){
45463         this.items.each(function(f){
45464            Roo.applyIf(f, o);
45465         });
45466         return this;
45467     }
45468 });
45469
45470 // back compat
45471 Roo.BasicForm = Roo.form.BasicForm;/*
45472  * Based on:
45473  * Ext JS Library 1.1.1
45474  * Copyright(c) 2006-2007, Ext JS, LLC.
45475  *
45476  * Originally Released Under LGPL - original licence link has changed is not relivant.
45477  *
45478  * Fork - LGPL
45479  * <script type="text/javascript">
45480  */
45481
45482 /**
45483  * @class Roo.form.Form
45484  * @extends Roo.form.BasicForm
45485  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45486  * @constructor
45487  * @param {Object} config Configuration options
45488  */
45489 Roo.form.Form = function(config){
45490     var xitems =  [];
45491     if (config.items) {
45492         xitems = config.items;
45493         delete config.items;
45494     }
45495    
45496     
45497     Roo.form.Form.superclass.constructor.call(this, null, config);
45498     this.url = this.url || this.action;
45499     if(!this.root){
45500         this.root = new Roo.form.Layout(Roo.applyIf({
45501             id: Roo.id()
45502         }, config));
45503     }
45504     this.active = this.root;
45505     /**
45506      * Array of all the buttons that have been added to this form via {@link addButton}
45507      * @type Array
45508      */
45509     this.buttons = [];
45510     this.allItems = [];
45511     this.addEvents({
45512         /**
45513          * @event clientvalidation
45514          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45515          * @param {Form} this
45516          * @param {Boolean} valid true if the form has passed client-side validation
45517          */
45518         clientvalidation: true,
45519         /**
45520          * @event rendered
45521          * Fires when the form is rendered
45522          * @param {Roo.form.Form} form
45523          */
45524         rendered : true
45525     });
45526     
45527     if (this.progressUrl) {
45528             // push a hidden field onto the list of fields..
45529             this.addxtype( {
45530                     xns: Roo.form, 
45531                     xtype : 'Hidden', 
45532                     name : 'UPLOAD_IDENTIFIER' 
45533             });
45534         }
45535         
45536     
45537     Roo.each(xitems, this.addxtype, this);
45538     
45539     
45540     
45541 };
45542
45543 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45544     /**
45545      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45546      */
45547     /**
45548      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45549      */
45550     /**
45551      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45552      */
45553     buttonAlign:'center',
45554
45555     /**
45556      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45557      */
45558     minButtonWidth:75,
45559
45560     /**
45561      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45562      * This property cascades to child containers if not set.
45563      */
45564     labelAlign:'left',
45565
45566     /**
45567      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45568      * fires a looping event with that state. This is required to bind buttons to the valid
45569      * state using the config value formBind:true on the button.
45570      */
45571     monitorValid : false,
45572
45573     /**
45574      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45575      */
45576     monitorPoll : 200,
45577     
45578     /**
45579      * @cfg {String} progressUrl - Url to return progress data 
45580      */
45581     
45582     progressUrl : false,
45583   
45584     /**
45585      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45586      * fields are added and the column is closed. If no fields are passed the column remains open
45587      * until end() is called.
45588      * @param {Object} config The config to pass to the column
45589      * @param {Field} field1 (optional)
45590      * @param {Field} field2 (optional)
45591      * @param {Field} etc (optional)
45592      * @return Column The column container object
45593      */
45594     column : function(c){
45595         var col = new Roo.form.Column(c);
45596         this.start(col);
45597         if(arguments.length > 1){ // duplicate code required because of Opera
45598             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45599             this.end();
45600         }
45601         return col;
45602     },
45603
45604     /**
45605      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45606      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45607      * until end() is called.
45608      * @param {Object} config The config to pass to the fieldset
45609      * @param {Field} field1 (optional)
45610      * @param {Field} field2 (optional)
45611      * @param {Field} etc (optional)
45612      * @return FieldSet The fieldset container object
45613      */
45614     fieldset : function(c){
45615         var fs = new Roo.form.FieldSet(c);
45616         this.start(fs);
45617         if(arguments.length > 1){ // duplicate code required because of Opera
45618             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45619             this.end();
45620         }
45621         return fs;
45622     },
45623
45624     /**
45625      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45626      * fields are added and the container is closed. If no fields are passed the container remains open
45627      * until end() is called.
45628      * @param {Object} config The config to pass to the Layout
45629      * @param {Field} field1 (optional)
45630      * @param {Field} field2 (optional)
45631      * @param {Field} etc (optional)
45632      * @return Layout The container object
45633      */
45634     container : function(c){
45635         var l = new Roo.form.Layout(c);
45636         this.start(l);
45637         if(arguments.length > 1){ // duplicate code required because of Opera
45638             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45639             this.end();
45640         }
45641         return l;
45642     },
45643
45644     /**
45645      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45646      * @param {Object} container A Roo.form.Layout or subclass of Layout
45647      * @return {Form} this
45648      */
45649     start : function(c){
45650         // cascade label info
45651         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45652         this.active.stack.push(c);
45653         c.ownerCt = this.active;
45654         this.active = c;
45655         return this;
45656     },
45657
45658     /**
45659      * Closes the current open container
45660      * @return {Form} this
45661      */
45662     end : function(){
45663         if(this.active == this.root){
45664             return this;
45665         }
45666         this.active = this.active.ownerCt;
45667         return this;
45668     },
45669
45670     /**
45671      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45672      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45673      * as the label of the field.
45674      * @param {Field} field1
45675      * @param {Field} field2 (optional)
45676      * @param {Field} etc. (optional)
45677      * @return {Form} this
45678      */
45679     add : function(){
45680         this.active.stack.push.apply(this.active.stack, arguments);
45681         this.allItems.push.apply(this.allItems,arguments);
45682         var r = [];
45683         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45684             if(a[i].isFormField){
45685                 r.push(a[i]);
45686             }
45687         }
45688         if(r.length > 0){
45689             Roo.form.Form.superclass.add.apply(this, r);
45690         }
45691         return this;
45692     },
45693     
45694
45695     
45696     
45697     
45698      /**
45699      * Find any element that has been added to a form, using it's ID or name
45700      * This can include framesets, columns etc. along with regular fields..
45701      * @param {String} id - id or name to find.
45702      
45703      * @return {Element} e - or false if nothing found.
45704      */
45705     findbyId : function(id)
45706     {
45707         var ret = false;
45708         if (!id) {
45709             return ret;
45710         }
45711         Roo.each(this.allItems, function(f){
45712             if (f.id == id || f.name == id ){
45713                 ret = f;
45714                 return false;
45715             }
45716         });
45717         return ret;
45718     },
45719
45720     
45721     
45722     /**
45723      * Render this form into the passed container. This should only be called once!
45724      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45725      * @return {Form} this
45726      */
45727     render : function(ct)
45728     {
45729         
45730         
45731         
45732         ct = Roo.get(ct);
45733         var o = this.autoCreate || {
45734             tag: 'form',
45735             method : this.method || 'POST',
45736             id : this.id || Roo.id()
45737         };
45738         this.initEl(ct.createChild(o));
45739
45740         this.root.render(this.el);
45741         
45742        
45743              
45744         this.items.each(function(f){
45745             f.render('x-form-el-'+f.id);
45746         });
45747
45748         if(this.buttons.length > 0){
45749             // tables are required to maintain order and for correct IE layout
45750             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45751                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45752                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45753             }}, null, true);
45754             var tr = tb.getElementsByTagName('tr')[0];
45755             for(var i = 0, len = this.buttons.length; i < len; i++) {
45756                 var b = this.buttons[i];
45757                 var td = document.createElement('td');
45758                 td.className = 'x-form-btn-td';
45759                 b.render(tr.appendChild(td));
45760             }
45761         }
45762         if(this.monitorValid){ // initialize after render
45763             this.startMonitoring();
45764         }
45765         this.fireEvent('rendered', this);
45766         return this;
45767     },
45768
45769     /**
45770      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45771      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45772      * object or a valid Roo.DomHelper element config
45773      * @param {Function} handler The function called when the button is clicked
45774      * @param {Object} scope (optional) The scope of the handler function
45775      * @return {Roo.Button}
45776      */
45777     addButton : function(config, handler, scope){
45778         var bc = {
45779             handler: handler,
45780             scope: scope,
45781             minWidth: this.minButtonWidth,
45782             hideParent:true
45783         };
45784         if(typeof config == "string"){
45785             bc.text = config;
45786         }else{
45787             Roo.apply(bc, config);
45788         }
45789         var btn = new Roo.Button(null, bc);
45790         this.buttons.push(btn);
45791         return btn;
45792     },
45793
45794      /**
45795      * Adds a series of form elements (using the xtype property as the factory method.
45796      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45797      * @param {Object} config 
45798      */
45799     
45800     addxtype : function()
45801     {
45802         var ar = Array.prototype.slice.call(arguments, 0);
45803         var ret = false;
45804         for(var i = 0; i < ar.length; i++) {
45805             if (!ar[i]) {
45806                 continue; // skip -- if this happends something invalid got sent, we 
45807                 // should ignore it, as basically that interface element will not show up
45808                 // and that should be pretty obvious!!
45809             }
45810             
45811             if (Roo.form[ar[i].xtype]) {
45812                 ar[i].form = this;
45813                 var fe = Roo.factory(ar[i], Roo.form);
45814                 if (!ret) {
45815                     ret = fe;
45816                 }
45817                 fe.form = this;
45818                 if (fe.store) {
45819                     fe.store.form = this;
45820                 }
45821                 if (fe.isLayout) {  
45822                          
45823                     this.start(fe);
45824                     this.allItems.push(fe);
45825                     if (fe.items && fe.addxtype) {
45826                         fe.addxtype.apply(fe, fe.items);
45827                         delete fe.items;
45828                     }
45829                      this.end();
45830                     continue;
45831                 }
45832                 
45833                 
45834                  
45835                 this.add(fe);
45836               //  console.log('adding ' + ar[i].xtype);
45837             }
45838             if (ar[i].xtype == 'Button') {  
45839                 //console.log('adding button');
45840                 //console.log(ar[i]);
45841                 this.addButton(ar[i]);
45842                 this.allItems.push(fe);
45843                 continue;
45844             }
45845             
45846             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45847                 alert('end is not supported on xtype any more, use items');
45848             //    this.end();
45849             //    //console.log('adding end');
45850             }
45851             
45852         }
45853         return ret;
45854     },
45855     
45856     /**
45857      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45858      * option "monitorValid"
45859      */
45860     startMonitoring : function(){
45861         if(!this.bound){
45862             this.bound = true;
45863             Roo.TaskMgr.start({
45864                 run : this.bindHandler,
45865                 interval : this.monitorPoll || 200,
45866                 scope: this
45867             });
45868         }
45869     },
45870
45871     /**
45872      * Stops monitoring of the valid state of this form
45873      */
45874     stopMonitoring : function(){
45875         this.bound = false;
45876     },
45877
45878     // private
45879     bindHandler : function(){
45880         if(!this.bound){
45881             return false; // stops binding
45882         }
45883         var valid = true;
45884         this.items.each(function(f){
45885             if(!f.isValid(true)){
45886                 valid = false;
45887                 return false;
45888             }
45889         });
45890         for(var i = 0, len = this.buttons.length; i < len; i++){
45891             var btn = this.buttons[i];
45892             if(btn.formBind === true && btn.disabled === valid){
45893                 btn.setDisabled(!valid);
45894             }
45895         }
45896         this.fireEvent('clientvalidation', this, valid);
45897     }
45898     
45899     
45900     
45901     
45902     
45903     
45904     
45905     
45906 });
45907
45908
45909 // back compat
45910 Roo.Form = Roo.form.Form;
45911 /*
45912  * Based on:
45913  * Ext JS Library 1.1.1
45914  * Copyright(c) 2006-2007, Ext JS, LLC.
45915  *
45916  * Originally Released Under LGPL - original licence link has changed is not relivant.
45917  *
45918  * Fork - LGPL
45919  * <script type="text/javascript">
45920  */
45921
45922 // as we use this in bootstrap.
45923 Roo.namespace('Roo.form');
45924  /**
45925  * @class Roo.form.Action
45926  * Internal Class used to handle form actions
45927  * @constructor
45928  * @param {Roo.form.BasicForm} el The form element or its id
45929  * @param {Object} config Configuration options
45930  */
45931
45932  
45933  
45934 // define the action interface
45935 Roo.form.Action = function(form, options){
45936     this.form = form;
45937     this.options = options || {};
45938 };
45939 /**
45940  * Client Validation Failed
45941  * @const 
45942  */
45943 Roo.form.Action.CLIENT_INVALID = 'client';
45944 /**
45945  * Server Validation Failed
45946  * @const 
45947  */
45948 Roo.form.Action.SERVER_INVALID = 'server';
45949  /**
45950  * Connect to Server Failed
45951  * @const 
45952  */
45953 Roo.form.Action.CONNECT_FAILURE = 'connect';
45954 /**
45955  * Reading Data from Server Failed
45956  * @const 
45957  */
45958 Roo.form.Action.LOAD_FAILURE = 'load';
45959
45960 Roo.form.Action.prototype = {
45961     type : 'default',
45962     failureType : undefined,
45963     response : undefined,
45964     result : undefined,
45965
45966     // interface method
45967     run : function(options){
45968
45969     },
45970
45971     // interface method
45972     success : function(response){
45973
45974     },
45975
45976     // interface method
45977     handleResponse : function(response){
45978
45979     },
45980
45981     // default connection failure
45982     failure : function(response){
45983         
45984         this.response = response;
45985         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45986         this.form.afterAction(this, false);
45987     },
45988
45989     processResponse : function(response){
45990         this.response = response;
45991         if(!response.responseText){
45992             return true;
45993         }
45994         this.result = this.handleResponse(response);
45995         return this.result;
45996     },
45997
45998     // utility functions used internally
45999     getUrl : function(appendParams){
46000         var url = this.options.url || this.form.url || this.form.el.dom.action;
46001         if(appendParams){
46002             var p = this.getParams();
46003             if(p){
46004                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46005             }
46006         }
46007         return url;
46008     },
46009
46010     getMethod : function(){
46011         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46012     },
46013
46014     getParams : function(){
46015         var bp = this.form.baseParams;
46016         var p = this.options.params;
46017         if(p){
46018             if(typeof p == "object"){
46019                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46020             }else if(typeof p == 'string' && bp){
46021                 p += '&' + Roo.urlEncode(bp);
46022             }
46023         }else if(bp){
46024             p = Roo.urlEncode(bp);
46025         }
46026         return p;
46027     },
46028
46029     createCallback : function(){
46030         return {
46031             success: this.success,
46032             failure: this.failure,
46033             scope: this,
46034             timeout: (this.form.timeout*1000),
46035             upload: this.form.fileUpload ? this.success : undefined
46036         };
46037     }
46038 };
46039
46040 Roo.form.Action.Submit = function(form, options){
46041     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46042 };
46043
46044 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46045     type : 'submit',
46046
46047     haveProgress : false,
46048     uploadComplete : false,
46049     
46050     // uploadProgress indicator.
46051     uploadProgress : function()
46052     {
46053         if (!this.form.progressUrl) {
46054             return;
46055         }
46056         
46057         if (!this.haveProgress) {
46058             Roo.MessageBox.progress("Uploading", "Uploading");
46059         }
46060         if (this.uploadComplete) {
46061            Roo.MessageBox.hide();
46062            return;
46063         }
46064         
46065         this.haveProgress = true;
46066    
46067         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46068         
46069         var c = new Roo.data.Connection();
46070         c.request({
46071             url : this.form.progressUrl,
46072             params: {
46073                 id : uid
46074             },
46075             method: 'GET',
46076             success : function(req){
46077                //console.log(data);
46078                 var rdata = false;
46079                 var edata;
46080                 try  {
46081                    rdata = Roo.decode(req.responseText)
46082                 } catch (e) {
46083                     Roo.log("Invalid data from server..");
46084                     Roo.log(edata);
46085                     return;
46086                 }
46087                 if (!rdata || !rdata.success) {
46088                     Roo.log(rdata);
46089                     Roo.MessageBox.alert(Roo.encode(rdata));
46090                     return;
46091                 }
46092                 var data = rdata.data;
46093                 
46094                 if (this.uploadComplete) {
46095                    Roo.MessageBox.hide();
46096                    return;
46097                 }
46098                    
46099                 if (data){
46100                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46101                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46102                     );
46103                 }
46104                 this.uploadProgress.defer(2000,this);
46105             },
46106        
46107             failure: function(data) {
46108                 Roo.log('progress url failed ');
46109                 Roo.log(data);
46110             },
46111             scope : this
46112         });
46113            
46114     },
46115     
46116     
46117     run : function()
46118     {
46119         // run get Values on the form, so it syncs any secondary forms.
46120         this.form.getValues();
46121         
46122         var o = this.options;
46123         var method = this.getMethod();
46124         var isPost = method == 'POST';
46125         if(o.clientValidation === false || this.form.isValid()){
46126             
46127             if (this.form.progressUrl) {
46128                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46129                     (new Date() * 1) + '' + Math.random());
46130                     
46131             } 
46132             
46133             
46134             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46135                 form:this.form.el.dom,
46136                 url:this.getUrl(!isPost),
46137                 method: method,
46138                 params:isPost ? this.getParams() : null,
46139                 isUpload: this.form.fileUpload
46140             }));
46141             
46142             this.uploadProgress();
46143
46144         }else if (o.clientValidation !== false){ // client validation failed
46145             this.failureType = Roo.form.Action.CLIENT_INVALID;
46146             this.form.afterAction(this, false);
46147         }
46148     },
46149
46150     success : function(response)
46151     {
46152         this.uploadComplete= true;
46153         if (this.haveProgress) {
46154             Roo.MessageBox.hide();
46155         }
46156         
46157         
46158         var result = this.processResponse(response);
46159         if(result === true || result.success){
46160             this.form.afterAction(this, true);
46161             return;
46162         }
46163         if(result.errors){
46164             this.form.markInvalid(result.errors);
46165             this.failureType = Roo.form.Action.SERVER_INVALID;
46166         }
46167         this.form.afterAction(this, false);
46168     },
46169     failure : function(response)
46170     {
46171         this.uploadComplete= true;
46172         if (this.haveProgress) {
46173             Roo.MessageBox.hide();
46174         }
46175         
46176         this.response = response;
46177         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46178         this.form.afterAction(this, false);
46179     },
46180     
46181     handleResponse : function(response){
46182         if(this.form.errorReader){
46183             var rs = this.form.errorReader.read(response);
46184             var errors = [];
46185             if(rs.records){
46186                 for(var i = 0, len = rs.records.length; i < len; i++) {
46187                     var r = rs.records[i];
46188                     errors[i] = r.data;
46189                 }
46190             }
46191             if(errors.length < 1){
46192                 errors = null;
46193             }
46194             return {
46195                 success : rs.success,
46196                 errors : errors
46197             };
46198         }
46199         var ret = false;
46200         try {
46201             ret = Roo.decode(response.responseText);
46202         } catch (e) {
46203             ret = {
46204                 success: false,
46205                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46206                 errors : []
46207             };
46208         }
46209         return ret;
46210         
46211     }
46212 });
46213
46214
46215 Roo.form.Action.Load = function(form, options){
46216     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46217     this.reader = this.form.reader;
46218 };
46219
46220 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46221     type : 'load',
46222
46223     run : function(){
46224         
46225         Roo.Ajax.request(Roo.apply(
46226                 this.createCallback(), {
46227                     method:this.getMethod(),
46228                     url:this.getUrl(false),
46229                     params:this.getParams()
46230         }));
46231     },
46232
46233     success : function(response){
46234         
46235         var result = this.processResponse(response);
46236         if(result === true || !result.success || !result.data){
46237             this.failureType = Roo.form.Action.LOAD_FAILURE;
46238             this.form.afterAction(this, false);
46239             return;
46240         }
46241         this.form.clearInvalid();
46242         this.form.setValues(result.data);
46243         this.form.afterAction(this, true);
46244     },
46245
46246     handleResponse : function(response){
46247         if(this.form.reader){
46248             var rs = this.form.reader.read(response);
46249             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46250             return {
46251                 success : rs.success,
46252                 data : data
46253             };
46254         }
46255         return Roo.decode(response.responseText);
46256     }
46257 });
46258
46259 Roo.form.Action.ACTION_TYPES = {
46260     'load' : Roo.form.Action.Load,
46261     'submit' : Roo.form.Action.Submit
46262 };/*
46263  * Based on:
46264  * Ext JS Library 1.1.1
46265  * Copyright(c) 2006-2007, Ext JS, LLC.
46266  *
46267  * Originally Released Under LGPL - original licence link has changed is not relivant.
46268  *
46269  * Fork - LGPL
46270  * <script type="text/javascript">
46271  */
46272  
46273 /**
46274  * @class Roo.form.Layout
46275  * @extends Roo.Component
46276  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46277  * @constructor
46278  * @param {Object} config Configuration options
46279  */
46280 Roo.form.Layout = function(config){
46281     var xitems = [];
46282     if (config.items) {
46283         xitems = config.items;
46284         delete config.items;
46285     }
46286     Roo.form.Layout.superclass.constructor.call(this, config);
46287     this.stack = [];
46288     Roo.each(xitems, this.addxtype, this);
46289      
46290 };
46291
46292 Roo.extend(Roo.form.Layout, Roo.Component, {
46293     /**
46294      * @cfg {String/Object} autoCreate
46295      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46296      */
46297     /**
46298      * @cfg {String/Object/Function} style
46299      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46300      * a function which returns such a specification.
46301      */
46302     /**
46303      * @cfg {String} labelAlign
46304      * Valid values are "left," "top" and "right" (defaults to "left")
46305      */
46306     /**
46307      * @cfg {Number} labelWidth
46308      * Fixed width in pixels of all field labels (defaults to undefined)
46309      */
46310     /**
46311      * @cfg {Boolean} clear
46312      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46313      */
46314     clear : true,
46315     /**
46316      * @cfg {String} labelSeparator
46317      * The separator to use after field labels (defaults to ':')
46318      */
46319     labelSeparator : ':',
46320     /**
46321      * @cfg {Boolean} hideLabels
46322      * True to suppress the display of field labels in this layout (defaults to false)
46323      */
46324     hideLabels : false,
46325
46326     // private
46327     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46328     
46329     isLayout : true,
46330     
46331     // private
46332     onRender : function(ct, position){
46333         if(this.el){ // from markup
46334             this.el = Roo.get(this.el);
46335         }else {  // generate
46336             var cfg = this.getAutoCreate();
46337             this.el = ct.createChild(cfg, position);
46338         }
46339         if(this.style){
46340             this.el.applyStyles(this.style);
46341         }
46342         if(this.labelAlign){
46343             this.el.addClass('x-form-label-'+this.labelAlign);
46344         }
46345         if(this.hideLabels){
46346             this.labelStyle = "display:none";
46347             this.elementStyle = "padding-left:0;";
46348         }else{
46349             if(typeof this.labelWidth == 'number'){
46350                 this.labelStyle = "width:"+this.labelWidth+"px;";
46351                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46352             }
46353             if(this.labelAlign == 'top'){
46354                 this.labelStyle = "width:auto;";
46355                 this.elementStyle = "padding-left:0;";
46356             }
46357         }
46358         var stack = this.stack;
46359         var slen = stack.length;
46360         if(slen > 0){
46361             if(!this.fieldTpl){
46362                 var t = new Roo.Template(
46363                     '<div class="x-form-item {5}">',
46364                         '<label for="{0}" style="{2}">{1}{4}</label>',
46365                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46366                         '</div>',
46367                     '</div><div class="x-form-clear-left"></div>'
46368                 );
46369                 t.disableFormats = true;
46370                 t.compile();
46371                 Roo.form.Layout.prototype.fieldTpl = t;
46372             }
46373             for(var i = 0; i < slen; i++) {
46374                 if(stack[i].isFormField){
46375                     this.renderField(stack[i]);
46376                 }else{
46377                     this.renderComponent(stack[i]);
46378                 }
46379             }
46380         }
46381         if(this.clear){
46382             this.el.createChild({cls:'x-form-clear'});
46383         }
46384     },
46385
46386     // private
46387     renderField : function(f){
46388         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46389                f.id, //0
46390                f.fieldLabel, //1
46391                f.labelStyle||this.labelStyle||'', //2
46392                this.elementStyle||'', //3
46393                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46394                f.itemCls||this.itemCls||''  //5
46395        ], true).getPrevSibling());
46396     },
46397
46398     // private
46399     renderComponent : function(c){
46400         c.render(c.isLayout ? this.el : this.el.createChild());    
46401     },
46402     /**
46403      * Adds a object form elements (using the xtype property as the factory method.)
46404      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46405      * @param {Object} config 
46406      */
46407     addxtype : function(o)
46408     {
46409         // create the lement.
46410         o.form = this.form;
46411         var fe = Roo.factory(o, Roo.form);
46412         this.form.allItems.push(fe);
46413         this.stack.push(fe);
46414         
46415         if (fe.isFormField) {
46416             this.form.items.add(fe);
46417         }
46418          
46419         return fe;
46420     }
46421 });
46422
46423 /**
46424  * @class Roo.form.Column
46425  * @extends Roo.form.Layout
46426  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46427  * @constructor
46428  * @param {Object} config Configuration options
46429  */
46430 Roo.form.Column = function(config){
46431     Roo.form.Column.superclass.constructor.call(this, config);
46432 };
46433
46434 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46435     /**
46436      * @cfg {Number/String} width
46437      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46438      */
46439     /**
46440      * @cfg {String/Object} autoCreate
46441      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46442      */
46443
46444     // private
46445     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46446
46447     // private
46448     onRender : function(ct, position){
46449         Roo.form.Column.superclass.onRender.call(this, ct, position);
46450         if(this.width){
46451             this.el.setWidth(this.width);
46452         }
46453     }
46454 });
46455
46456
46457 /**
46458  * @class Roo.form.Row
46459  * @extends Roo.form.Layout
46460  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46461  * @constructor
46462  * @param {Object} config Configuration options
46463  */
46464
46465  
46466 Roo.form.Row = function(config){
46467     Roo.form.Row.superclass.constructor.call(this, config);
46468 };
46469  
46470 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46471       /**
46472      * @cfg {Number/String} width
46473      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46474      */
46475     /**
46476      * @cfg {Number/String} height
46477      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46478      */
46479     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46480     
46481     padWidth : 20,
46482     // private
46483     onRender : function(ct, position){
46484         //console.log('row render');
46485         if(!this.rowTpl){
46486             var t = new Roo.Template(
46487                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46488                     '<label for="{0}" style="{2}">{1}{4}</label>',
46489                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46490                     '</div>',
46491                 '</div>'
46492             );
46493             t.disableFormats = true;
46494             t.compile();
46495             Roo.form.Layout.prototype.rowTpl = t;
46496         }
46497         this.fieldTpl = this.rowTpl;
46498         
46499         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46500         var labelWidth = 100;
46501         
46502         if ((this.labelAlign != 'top')) {
46503             if (typeof this.labelWidth == 'number') {
46504                 labelWidth = this.labelWidth
46505             }
46506             this.padWidth =  20 + labelWidth;
46507             
46508         }
46509         
46510         Roo.form.Column.superclass.onRender.call(this, ct, position);
46511         if(this.width){
46512             this.el.setWidth(this.width);
46513         }
46514         if(this.height){
46515             this.el.setHeight(this.height);
46516         }
46517     },
46518     
46519     // private
46520     renderField : function(f){
46521         f.fieldEl = this.fieldTpl.append(this.el, [
46522                f.id, f.fieldLabel,
46523                f.labelStyle||this.labelStyle||'',
46524                this.elementStyle||'',
46525                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46526                f.itemCls||this.itemCls||'',
46527                f.width ? f.width + this.padWidth : 160 + this.padWidth
46528        ],true);
46529     }
46530 });
46531  
46532
46533 /**
46534  * @class Roo.form.FieldSet
46535  * @extends Roo.form.Layout
46536  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46537  * @constructor
46538  * @param {Object} config Configuration options
46539  */
46540 Roo.form.FieldSet = function(config){
46541     Roo.form.FieldSet.superclass.constructor.call(this, config);
46542 };
46543
46544 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46545     /**
46546      * @cfg {String} legend
46547      * The text to display as the legend for the FieldSet (defaults to '')
46548      */
46549     /**
46550      * @cfg {String/Object} autoCreate
46551      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46552      */
46553
46554     // private
46555     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46556
46557     // private
46558     onRender : function(ct, position){
46559         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46560         if(this.legend){
46561             this.setLegend(this.legend);
46562         }
46563     },
46564
46565     // private
46566     setLegend : function(text){
46567         if(this.rendered){
46568             this.el.child('legend').update(text);
46569         }
46570     }
46571 });/*
46572  * Based on:
46573  * Ext JS Library 1.1.1
46574  * Copyright(c) 2006-2007, Ext JS, LLC.
46575  *
46576  * Originally Released Under LGPL - original licence link has changed is not relivant.
46577  *
46578  * Fork - LGPL
46579  * <script type="text/javascript">
46580  */
46581 /**
46582  * @class Roo.form.VTypes
46583  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46584  * @singleton
46585  */
46586 Roo.form.VTypes = function(){
46587     // closure these in so they are only created once.
46588     var alpha = /^[a-zA-Z_]+$/;
46589     var alphanum = /^[a-zA-Z0-9_]+$/;
46590     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46591     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46592
46593     // All these messages and functions are configurable
46594     return {
46595         /**
46596          * The function used to validate email addresses
46597          * @param {String} value The email address
46598          */
46599         'email' : function(v){
46600             return email.test(v);
46601         },
46602         /**
46603          * The error text to display when the email validation function returns false
46604          * @type String
46605          */
46606         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46607         /**
46608          * The keystroke filter mask to be applied on email input
46609          * @type RegExp
46610          */
46611         'emailMask' : /[a-z0-9_\.\-@]/i,
46612
46613         /**
46614          * The function used to validate URLs
46615          * @param {String} value The URL
46616          */
46617         'url' : function(v){
46618             return url.test(v);
46619         },
46620         /**
46621          * The error text to display when the url validation function returns false
46622          * @type String
46623          */
46624         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46625         
46626         /**
46627          * The function used to validate alpha values
46628          * @param {String} value The value
46629          */
46630         'alpha' : function(v){
46631             return alpha.test(v);
46632         },
46633         /**
46634          * The error text to display when the alpha validation function returns false
46635          * @type String
46636          */
46637         'alphaText' : 'This field should only contain letters and _',
46638         /**
46639          * The keystroke filter mask to be applied on alpha input
46640          * @type RegExp
46641          */
46642         'alphaMask' : /[a-z_]/i,
46643
46644         /**
46645          * The function used to validate alphanumeric values
46646          * @param {String} value The value
46647          */
46648         'alphanum' : function(v){
46649             return alphanum.test(v);
46650         },
46651         /**
46652          * The error text to display when the alphanumeric validation function returns false
46653          * @type String
46654          */
46655         'alphanumText' : 'This field should only contain letters, numbers and _',
46656         /**
46657          * The keystroke filter mask to be applied on alphanumeric input
46658          * @type RegExp
46659          */
46660         'alphanumMask' : /[a-z0-9_]/i
46661     };
46662 }();//<script type="text/javascript">
46663
46664 /**
46665  * @class Roo.form.FCKeditor
46666  * @extends Roo.form.TextArea
46667  * Wrapper around the FCKEditor http://www.fckeditor.net
46668  * @constructor
46669  * Creates a new FCKeditor
46670  * @param {Object} config Configuration options
46671  */
46672 Roo.form.FCKeditor = function(config){
46673     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46674     this.addEvents({
46675          /**
46676          * @event editorinit
46677          * Fired when the editor is initialized - you can add extra handlers here..
46678          * @param {FCKeditor} this
46679          * @param {Object} the FCK object.
46680          */
46681         editorinit : true
46682     });
46683     
46684     
46685 };
46686 Roo.form.FCKeditor.editors = { };
46687 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46688 {
46689     //defaultAutoCreate : {
46690     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46691     //},
46692     // private
46693     /**
46694      * @cfg {Object} fck options - see fck manual for details.
46695      */
46696     fckconfig : false,
46697     
46698     /**
46699      * @cfg {Object} fck toolbar set (Basic or Default)
46700      */
46701     toolbarSet : 'Basic',
46702     /**
46703      * @cfg {Object} fck BasePath
46704      */ 
46705     basePath : '/fckeditor/',
46706     
46707     
46708     frame : false,
46709     
46710     value : '',
46711     
46712    
46713     onRender : function(ct, position)
46714     {
46715         if(!this.el){
46716             this.defaultAutoCreate = {
46717                 tag: "textarea",
46718                 style:"width:300px;height:60px;",
46719                 autocomplete: "new-password"
46720             };
46721         }
46722         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46723         /*
46724         if(this.grow){
46725             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46726             if(this.preventScrollbars){
46727                 this.el.setStyle("overflow", "hidden");
46728             }
46729             this.el.setHeight(this.growMin);
46730         }
46731         */
46732         //console.log('onrender' + this.getId() );
46733         Roo.form.FCKeditor.editors[this.getId()] = this;
46734          
46735
46736         this.replaceTextarea() ;
46737         
46738     },
46739     
46740     getEditor : function() {
46741         return this.fckEditor;
46742     },
46743     /**
46744      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46745      * @param {Mixed} value The value to set
46746      */
46747     
46748     
46749     setValue : function(value)
46750     {
46751         //console.log('setValue: ' + value);
46752         
46753         if(typeof(value) == 'undefined') { // not sure why this is happending...
46754             return;
46755         }
46756         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46757         
46758         //if(!this.el || !this.getEditor()) {
46759         //    this.value = value;
46760             //this.setValue.defer(100,this,[value]);    
46761         //    return;
46762         //} 
46763         
46764         if(!this.getEditor()) {
46765             return;
46766         }
46767         
46768         this.getEditor().SetData(value);
46769         
46770         //
46771
46772     },
46773
46774     /**
46775      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46776      * @return {Mixed} value The field value
46777      */
46778     getValue : function()
46779     {
46780         
46781         if (this.frame && this.frame.dom.style.display == 'none') {
46782             return Roo.form.FCKeditor.superclass.getValue.call(this);
46783         }
46784         
46785         if(!this.el || !this.getEditor()) {
46786            
46787            // this.getValue.defer(100,this); 
46788             return this.value;
46789         }
46790        
46791         
46792         var value=this.getEditor().GetData();
46793         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46794         return Roo.form.FCKeditor.superclass.getValue.call(this);
46795         
46796
46797     },
46798
46799     /**
46800      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46801      * @return {Mixed} value The field value
46802      */
46803     getRawValue : function()
46804     {
46805         if (this.frame && this.frame.dom.style.display == 'none') {
46806             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46807         }
46808         
46809         if(!this.el || !this.getEditor()) {
46810             //this.getRawValue.defer(100,this); 
46811             return this.value;
46812             return;
46813         }
46814         
46815         
46816         
46817         var value=this.getEditor().GetData();
46818         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46819         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46820          
46821     },
46822     
46823     setSize : function(w,h) {
46824         
46825         
46826         
46827         //if (this.frame && this.frame.dom.style.display == 'none') {
46828         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46829         //    return;
46830         //}
46831         //if(!this.el || !this.getEditor()) {
46832         //    this.setSize.defer(100,this, [w,h]); 
46833         //    return;
46834         //}
46835         
46836         
46837         
46838         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46839         
46840         this.frame.dom.setAttribute('width', w);
46841         this.frame.dom.setAttribute('height', h);
46842         this.frame.setSize(w,h);
46843         
46844     },
46845     
46846     toggleSourceEdit : function(value) {
46847         
46848       
46849          
46850         this.el.dom.style.display = value ? '' : 'none';
46851         this.frame.dom.style.display = value ?  'none' : '';
46852         
46853     },
46854     
46855     
46856     focus: function(tag)
46857     {
46858         if (this.frame.dom.style.display == 'none') {
46859             return Roo.form.FCKeditor.superclass.focus.call(this);
46860         }
46861         if(!this.el || !this.getEditor()) {
46862             this.focus.defer(100,this, [tag]); 
46863             return;
46864         }
46865         
46866         
46867         
46868         
46869         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46870         this.getEditor().Focus();
46871         if (tgs.length) {
46872             if (!this.getEditor().Selection.GetSelection()) {
46873                 this.focus.defer(100,this, [tag]); 
46874                 return;
46875             }
46876             
46877             
46878             var r = this.getEditor().EditorDocument.createRange();
46879             r.setStart(tgs[0],0);
46880             r.setEnd(tgs[0],0);
46881             this.getEditor().Selection.GetSelection().removeAllRanges();
46882             this.getEditor().Selection.GetSelection().addRange(r);
46883             this.getEditor().Focus();
46884         }
46885         
46886     },
46887     
46888     
46889     
46890     replaceTextarea : function()
46891     {
46892         if ( document.getElementById( this.getId() + '___Frame' ) )
46893             return ;
46894         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46895         //{
46896             // We must check the elements firstly using the Id and then the name.
46897         var oTextarea = document.getElementById( this.getId() );
46898         
46899         var colElementsByName = document.getElementsByName( this.getId() ) ;
46900          
46901         oTextarea.style.display = 'none' ;
46902
46903         if ( oTextarea.tabIndex ) {            
46904             this.TabIndex = oTextarea.tabIndex ;
46905         }
46906         
46907         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46908         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46909         this.frame = Roo.get(this.getId() + '___Frame')
46910     },
46911     
46912     _getConfigHtml : function()
46913     {
46914         var sConfig = '' ;
46915
46916         for ( var o in this.fckconfig ) {
46917             sConfig += sConfig.length > 0  ? '&amp;' : '';
46918             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46919         }
46920
46921         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46922     },
46923     
46924     
46925     _getIFrameHtml : function()
46926     {
46927         var sFile = 'fckeditor.html' ;
46928         /* no idea what this is about..
46929         try
46930         {
46931             if ( (/fcksource=true/i).test( window.top.location.search ) )
46932                 sFile = 'fckeditor.original.html' ;
46933         }
46934         catch (e) { 
46935         */
46936
46937         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46938         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46939         
46940         
46941         var html = '<iframe id="' + this.getId() +
46942             '___Frame" src="' + sLink +
46943             '" width="' + this.width +
46944             '" height="' + this.height + '"' +
46945             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46946             ' frameborder="0" scrolling="no"></iframe>' ;
46947
46948         return html ;
46949     },
46950     
46951     _insertHtmlBefore : function( html, element )
46952     {
46953         if ( element.insertAdjacentHTML )       {
46954             // IE
46955             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46956         } else { // Gecko
46957             var oRange = document.createRange() ;
46958             oRange.setStartBefore( element ) ;
46959             var oFragment = oRange.createContextualFragment( html );
46960             element.parentNode.insertBefore( oFragment, element ) ;
46961         }
46962     }
46963     
46964     
46965   
46966     
46967     
46968     
46969     
46970
46971 });
46972
46973 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46974
46975 function FCKeditor_OnComplete(editorInstance){
46976     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46977     f.fckEditor = editorInstance;
46978     //console.log("loaded");
46979     f.fireEvent('editorinit', f, editorInstance);
46980
46981   
46982
46983  
46984
46985
46986
46987
46988
46989
46990
46991
46992
46993
46994
46995
46996
46997
46998
46999 //<script type="text/javascript">
47000 /**
47001  * @class Roo.form.GridField
47002  * @extends Roo.form.Field
47003  * Embed a grid (or editable grid into a form)
47004  * STATUS ALPHA
47005  * 
47006  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47007  * it needs 
47008  * xgrid.store = Roo.data.Store
47009  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47010  * xgrid.store.reader = Roo.data.JsonReader 
47011  * 
47012  * 
47013  * @constructor
47014  * Creates a new GridField
47015  * @param {Object} config Configuration options
47016  */
47017 Roo.form.GridField = function(config){
47018     Roo.form.GridField.superclass.constructor.call(this, config);
47019      
47020 };
47021
47022 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47023     /**
47024      * @cfg {Number} width  - used to restrict width of grid..
47025      */
47026     width : 100,
47027     /**
47028      * @cfg {Number} height - used to restrict height of grid..
47029      */
47030     height : 50,
47031      /**
47032      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47033          * 
47034          *}
47035      */
47036     xgrid : false, 
47037     /**
47038      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47039      * {tag: "input", type: "checkbox", autocomplete: "off"})
47040      */
47041    // defaultAutoCreate : { tag: 'div' },
47042     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47043     /**
47044      * @cfg {String} addTitle Text to include for adding a title.
47045      */
47046     addTitle : false,
47047     //
47048     onResize : function(){
47049         Roo.form.Field.superclass.onResize.apply(this, arguments);
47050     },
47051
47052     initEvents : function(){
47053         // Roo.form.Checkbox.superclass.initEvents.call(this);
47054         // has no events...
47055        
47056     },
47057
47058
47059     getResizeEl : function(){
47060         return this.wrap;
47061     },
47062
47063     getPositionEl : function(){
47064         return this.wrap;
47065     },
47066
47067     // private
47068     onRender : function(ct, position){
47069         
47070         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47071         var style = this.style;
47072         delete this.style;
47073         
47074         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47075         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47076         this.viewEl = this.wrap.createChild({ tag: 'div' });
47077         if (style) {
47078             this.viewEl.applyStyles(style);
47079         }
47080         if (this.width) {
47081             this.viewEl.setWidth(this.width);
47082         }
47083         if (this.height) {
47084             this.viewEl.setHeight(this.height);
47085         }
47086         //if(this.inputValue !== undefined){
47087         //this.setValue(this.value);
47088         
47089         
47090         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47091         
47092         
47093         this.grid.render();
47094         this.grid.getDataSource().on('remove', this.refreshValue, this);
47095         this.grid.getDataSource().on('update', this.refreshValue, this);
47096         this.grid.on('afteredit', this.refreshValue, this);
47097  
47098     },
47099      
47100     
47101     /**
47102      * Sets the value of the item. 
47103      * @param {String} either an object  or a string..
47104      */
47105     setValue : function(v){
47106         //this.value = v;
47107         v = v || []; // empty set..
47108         // this does not seem smart - it really only affects memoryproxy grids..
47109         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47110             var ds = this.grid.getDataSource();
47111             // assumes a json reader..
47112             var data = {}
47113             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47114             ds.loadData( data);
47115         }
47116         // clear selection so it does not get stale.
47117         if (this.grid.sm) { 
47118             this.grid.sm.clearSelections();
47119         }
47120         
47121         Roo.form.GridField.superclass.setValue.call(this, v);
47122         this.refreshValue();
47123         // should load data in the grid really....
47124     },
47125     
47126     // private
47127     refreshValue: function() {
47128          var val = [];
47129         this.grid.getDataSource().each(function(r) {
47130             val.push(r.data);
47131         });
47132         this.el.dom.value = Roo.encode(val);
47133     }
47134     
47135      
47136     
47137     
47138 });/*
47139  * Based on:
47140  * Ext JS Library 1.1.1
47141  * Copyright(c) 2006-2007, Ext JS, LLC.
47142  *
47143  * Originally Released Under LGPL - original licence link has changed is not relivant.
47144  *
47145  * Fork - LGPL
47146  * <script type="text/javascript">
47147  */
47148 /**
47149  * @class Roo.form.DisplayField
47150  * @extends Roo.form.Field
47151  * A generic Field to display non-editable data.
47152  * @constructor
47153  * Creates a new Display Field item.
47154  * @param {Object} config Configuration options
47155  */
47156 Roo.form.DisplayField = function(config){
47157     Roo.form.DisplayField.superclass.constructor.call(this, config);
47158     
47159 };
47160
47161 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47162     inputType:      'hidden',
47163     allowBlank:     true,
47164     readOnly:         true,
47165     
47166  
47167     /**
47168      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47169      */
47170     focusClass : undefined,
47171     /**
47172      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47173      */
47174     fieldClass: 'x-form-field',
47175     
47176      /**
47177      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47178      */
47179     valueRenderer: undefined,
47180     
47181     width: 100,
47182     /**
47183      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47184      * {tag: "input", type: "checkbox", autocomplete: "off"})
47185      */
47186      
47187  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47188
47189     onResize : function(){
47190         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47191         
47192     },
47193
47194     initEvents : function(){
47195         // Roo.form.Checkbox.superclass.initEvents.call(this);
47196         // has no events...
47197        
47198     },
47199
47200
47201     getResizeEl : function(){
47202         return this.wrap;
47203     },
47204
47205     getPositionEl : function(){
47206         return this.wrap;
47207     },
47208
47209     // private
47210     onRender : function(ct, position){
47211         
47212         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47213         //if(this.inputValue !== undefined){
47214         this.wrap = this.el.wrap();
47215         
47216         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47217         
47218         if (this.bodyStyle) {
47219             this.viewEl.applyStyles(this.bodyStyle);
47220         }
47221         //this.viewEl.setStyle('padding', '2px');
47222         
47223         this.setValue(this.value);
47224         
47225     },
47226 /*
47227     // private
47228     initValue : Roo.emptyFn,
47229
47230   */
47231
47232         // private
47233     onClick : function(){
47234         
47235     },
47236
47237     /**
47238      * Sets the checked state of the checkbox.
47239      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47240      */
47241     setValue : function(v){
47242         this.value = v;
47243         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47244         // this might be called before we have a dom element..
47245         if (!this.viewEl) {
47246             return;
47247         }
47248         this.viewEl.dom.innerHTML = html;
47249         Roo.form.DisplayField.superclass.setValue.call(this, v);
47250
47251     }
47252 });/*
47253  * 
47254  * Licence- LGPL
47255  * 
47256  */
47257
47258 /**
47259  * @class Roo.form.DayPicker
47260  * @extends Roo.form.Field
47261  * A Day picker show [M] [T] [W] ....
47262  * @constructor
47263  * Creates a new Day Picker
47264  * @param {Object} config Configuration options
47265  */
47266 Roo.form.DayPicker= function(config){
47267     Roo.form.DayPicker.superclass.constructor.call(this, config);
47268      
47269 };
47270
47271 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47272     /**
47273      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47274      */
47275     focusClass : undefined,
47276     /**
47277      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47278      */
47279     fieldClass: "x-form-field",
47280    
47281     /**
47282      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47283      * {tag: "input", type: "checkbox", autocomplete: "off"})
47284      */
47285     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47286     
47287    
47288     actionMode : 'viewEl', 
47289     //
47290     // private
47291  
47292     inputType : 'hidden',
47293     
47294      
47295     inputElement: false, // real input element?
47296     basedOn: false, // ????
47297     
47298     isFormField: true, // not sure where this is needed!!!!
47299
47300     onResize : function(){
47301         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47302         if(!this.boxLabel){
47303             this.el.alignTo(this.wrap, 'c-c');
47304         }
47305     },
47306
47307     initEvents : function(){
47308         Roo.form.Checkbox.superclass.initEvents.call(this);
47309         this.el.on("click", this.onClick,  this);
47310         this.el.on("change", this.onClick,  this);
47311     },
47312
47313
47314     getResizeEl : function(){
47315         return this.wrap;
47316     },
47317
47318     getPositionEl : function(){
47319         return this.wrap;
47320     },
47321
47322     
47323     // private
47324     onRender : function(ct, position){
47325         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47326        
47327         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47328         
47329         var r1 = '<table><tr>';
47330         var r2 = '<tr class="x-form-daypick-icons">';
47331         for (var i=0; i < 7; i++) {
47332             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47333             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47334         }
47335         
47336         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47337         viewEl.select('img').on('click', this.onClick, this);
47338         this.viewEl = viewEl;   
47339         
47340         
47341         // this will not work on Chrome!!!
47342         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47343         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47344         
47345         
47346           
47347
47348     },
47349
47350     // private
47351     initValue : Roo.emptyFn,
47352
47353     /**
47354      * Returns the checked state of the checkbox.
47355      * @return {Boolean} True if checked, else false
47356      */
47357     getValue : function(){
47358         return this.el.dom.value;
47359         
47360     },
47361
47362         // private
47363     onClick : function(e){ 
47364         //this.setChecked(!this.checked);
47365         Roo.get(e.target).toggleClass('x-menu-item-checked');
47366         this.refreshValue();
47367         //if(this.el.dom.checked != this.checked){
47368         //    this.setValue(this.el.dom.checked);
47369        // }
47370     },
47371     
47372     // private
47373     refreshValue : function()
47374     {
47375         var val = '';
47376         this.viewEl.select('img',true).each(function(e,i,n)  {
47377             val += e.is(".x-menu-item-checked") ? String(n) : '';
47378         });
47379         this.setValue(val, true);
47380     },
47381
47382     /**
47383      * Sets the checked state of the checkbox.
47384      * On is always based on a string comparison between inputValue and the param.
47385      * @param {Boolean/String} value - the value to set 
47386      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47387      */
47388     setValue : function(v,suppressEvent){
47389         if (!this.el.dom) {
47390             return;
47391         }
47392         var old = this.el.dom.value ;
47393         this.el.dom.value = v;
47394         if (suppressEvent) {
47395             return ;
47396         }
47397          
47398         // update display..
47399         this.viewEl.select('img',true).each(function(e,i,n)  {
47400             
47401             var on = e.is(".x-menu-item-checked");
47402             var newv = v.indexOf(String(n)) > -1;
47403             if (on != newv) {
47404                 e.toggleClass('x-menu-item-checked');
47405             }
47406             
47407         });
47408         
47409         
47410         this.fireEvent('change', this, v, old);
47411         
47412         
47413     },
47414    
47415     // handle setting of hidden value by some other method!!?!?
47416     setFromHidden: function()
47417     {
47418         if(!this.el){
47419             return;
47420         }
47421         //console.log("SET FROM HIDDEN");
47422         //alert('setFrom hidden');
47423         this.setValue(this.el.dom.value);
47424     },
47425     
47426     onDestroy : function()
47427     {
47428         if(this.viewEl){
47429             Roo.get(this.viewEl).remove();
47430         }
47431          
47432         Roo.form.DayPicker.superclass.onDestroy.call(this);
47433     }
47434
47435 });/*
47436  * RooJS Library 1.1.1
47437  * Copyright(c) 2008-2011  Alan Knowles
47438  *
47439  * License - LGPL
47440  */
47441  
47442
47443 /**
47444  * @class Roo.form.ComboCheck
47445  * @extends Roo.form.ComboBox
47446  * A combobox for multiple select items.
47447  *
47448  * FIXME - could do with a reset button..
47449  * 
47450  * @constructor
47451  * Create a new ComboCheck
47452  * @param {Object} config Configuration options
47453  */
47454 Roo.form.ComboCheck = function(config){
47455     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47456     // should verify some data...
47457     // like
47458     // hiddenName = required..
47459     // displayField = required
47460     // valudField == required
47461     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47462     var _t = this;
47463     Roo.each(req, function(e) {
47464         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47465             throw "Roo.form.ComboCheck : missing value for: " + e;
47466         }
47467     });
47468     
47469     
47470 };
47471
47472 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47473      
47474      
47475     editable : false,
47476      
47477     selectedClass: 'x-menu-item-checked', 
47478     
47479     // private
47480     onRender : function(ct, position){
47481         var _t = this;
47482         
47483         
47484         
47485         if(!this.tpl){
47486             var cls = 'x-combo-list';
47487
47488             
47489             this.tpl =  new Roo.Template({
47490                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47491                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47492                    '<span>{' + this.displayField + '}</span>' +
47493                     '</div>' 
47494                 
47495             });
47496         }
47497  
47498         
47499         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47500         this.view.singleSelect = false;
47501         this.view.multiSelect = true;
47502         this.view.toggleSelect = true;
47503         this.pageTb.add(new Roo.Toolbar.Fill(), {
47504             
47505             text: 'Done',
47506             handler: function()
47507             {
47508                 _t.collapse();
47509             }
47510         });
47511     },
47512     
47513     onViewOver : function(e, t){
47514         // do nothing...
47515         return;
47516         
47517     },
47518     
47519     onViewClick : function(doFocus,index){
47520         return;
47521         
47522     },
47523     select: function () {
47524         //Roo.log("SELECT CALLED");
47525     },
47526      
47527     selectByValue : function(xv, scrollIntoView){
47528         var ar = this.getValueArray();
47529         var sels = [];
47530         
47531         Roo.each(ar, function(v) {
47532             if(v === undefined || v === null){
47533                 return;
47534             }
47535             var r = this.findRecord(this.valueField, v);
47536             if(r){
47537                 sels.push(this.store.indexOf(r))
47538                 
47539             }
47540         },this);
47541         this.view.select(sels);
47542         return false;
47543     },
47544     
47545     
47546     
47547     onSelect : function(record, index){
47548        // Roo.log("onselect Called");
47549        // this is only called by the clear button now..
47550         this.view.clearSelections();
47551         this.setValue('[]');
47552         if (this.value != this.valueBefore) {
47553             this.fireEvent('change', this, this.value, this.valueBefore);
47554             this.valueBefore = this.value;
47555         }
47556     },
47557     getValueArray : function()
47558     {
47559         var ar = [] ;
47560         
47561         try {
47562             //Roo.log(this.value);
47563             if (typeof(this.value) == 'undefined') {
47564                 return [];
47565             }
47566             var ar = Roo.decode(this.value);
47567             return  ar instanceof Array ? ar : []; //?? valid?
47568             
47569         } catch(e) {
47570             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47571             return [];
47572         }
47573          
47574     },
47575     expand : function ()
47576     {
47577         
47578         Roo.form.ComboCheck.superclass.expand.call(this);
47579         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47580         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47581         
47582
47583     },
47584     
47585     collapse : function(){
47586         Roo.form.ComboCheck.superclass.collapse.call(this);
47587         var sl = this.view.getSelectedIndexes();
47588         var st = this.store;
47589         var nv = [];
47590         var tv = [];
47591         var r;
47592         Roo.each(sl, function(i) {
47593             r = st.getAt(i);
47594             nv.push(r.get(this.valueField));
47595         },this);
47596         this.setValue(Roo.encode(nv));
47597         if (this.value != this.valueBefore) {
47598
47599             this.fireEvent('change', this, this.value, this.valueBefore);
47600             this.valueBefore = this.value;
47601         }
47602         
47603     },
47604     
47605     setValue : function(v){
47606         // Roo.log(v);
47607         this.value = v;
47608         
47609         var vals = this.getValueArray();
47610         var tv = [];
47611         Roo.each(vals, function(k) {
47612             var r = this.findRecord(this.valueField, k);
47613             if(r){
47614                 tv.push(r.data[this.displayField]);
47615             }else if(this.valueNotFoundText !== undefined){
47616                 tv.push( this.valueNotFoundText );
47617             }
47618         },this);
47619        // Roo.log(tv);
47620         
47621         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47622         this.hiddenField.value = v;
47623         this.value = v;
47624     }
47625     
47626 });/*
47627  * Based on:
47628  * Ext JS Library 1.1.1
47629  * Copyright(c) 2006-2007, Ext JS, LLC.
47630  *
47631  * Originally Released Under LGPL - original licence link has changed is not relivant.
47632  *
47633  * Fork - LGPL
47634  * <script type="text/javascript">
47635  */
47636  
47637 /**
47638  * @class Roo.form.Signature
47639  * @extends Roo.form.Field
47640  * Signature field.  
47641  * @constructor
47642  * 
47643  * @param {Object} config Configuration options
47644  */
47645
47646 Roo.form.Signature = function(config){
47647     Roo.form.Signature.superclass.constructor.call(this, config);
47648     
47649     this.addEvents({// not in used??
47650          /**
47651          * @event confirm
47652          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47653              * @param {Roo.form.Signature} combo This combo box
47654              */
47655         'confirm' : true,
47656         /**
47657          * @event reset
47658          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47659              * @param {Roo.form.ComboBox} combo This combo box
47660              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47661              */
47662         'reset' : true
47663     });
47664 };
47665
47666 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47667     /**
47668      * @cfg {Object} labels Label to use when rendering a form.
47669      * defaults to 
47670      * labels : { 
47671      *      clear : "Clear",
47672      *      confirm : "Confirm"
47673      *  }
47674      */
47675     labels : { 
47676         clear : "Clear",
47677         confirm : "Confirm"
47678     },
47679     /**
47680      * @cfg {Number} width The signature panel width (defaults to 300)
47681      */
47682     width: 300,
47683     /**
47684      * @cfg {Number} height The signature panel height (defaults to 100)
47685      */
47686     height : 100,
47687     /**
47688      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47689      */
47690     allowBlank : false,
47691     
47692     //private
47693     // {Object} signPanel The signature SVG panel element (defaults to {})
47694     signPanel : {},
47695     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47696     isMouseDown : false,
47697     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47698     isConfirmed : false,
47699     // {String} signatureTmp SVG mapping string (defaults to empty string)
47700     signatureTmp : '',
47701     
47702     
47703     defaultAutoCreate : { // modified by initCompnoent..
47704         tag: "input",
47705         type:"hidden"
47706     },
47707
47708     // private
47709     onRender : function(ct, position){
47710         
47711         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47712         
47713         this.wrap = this.el.wrap({
47714             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47715         });
47716         
47717         this.createToolbar(this);
47718         this.signPanel = this.wrap.createChild({
47719                 tag: 'div',
47720                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47721             }, this.el
47722         );
47723             
47724         this.svgID = Roo.id();
47725         this.svgEl = this.signPanel.createChild({
47726               xmlns : 'http://www.w3.org/2000/svg',
47727               tag : 'svg',
47728               id : this.svgID + "-svg",
47729               width: this.width,
47730               height: this.height,
47731               viewBox: '0 0 '+this.width+' '+this.height,
47732               cn : [
47733                 {
47734                     tag: "rect",
47735                     id: this.svgID + "-svg-r",
47736                     width: this.width,
47737                     height: this.height,
47738                     fill: "#ffa"
47739                 },
47740                 {
47741                     tag: "line",
47742                     id: this.svgID + "-svg-l",
47743                     x1: "0", // start
47744                     y1: (this.height*0.8), // start set the line in 80% of height
47745                     x2: this.width, // end
47746                     y2: (this.height*0.8), // end set the line in 80% of height
47747                     'stroke': "#666",
47748                     'stroke-width': "1",
47749                     'stroke-dasharray': "3",
47750                     'shape-rendering': "crispEdges",
47751                     'pointer-events': "none"
47752                 },
47753                 {
47754                     tag: "path",
47755                     id: this.svgID + "-svg-p",
47756                     'stroke': "navy",
47757                     'stroke-width': "3",
47758                     'fill': "none",
47759                     'pointer-events': 'none'
47760                 }
47761               ]
47762         });
47763         this.createSVG();
47764         this.svgBox = this.svgEl.dom.getScreenCTM();
47765     },
47766     createSVG : function(){ 
47767         var svg = this.signPanel;
47768         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47769         var t = this;
47770
47771         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47772         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47773         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47774         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47775         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47776         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47777         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47778         
47779     },
47780     isTouchEvent : function(e){
47781         return e.type.match(/^touch/);
47782     },
47783     getCoords : function (e) {
47784         var pt    = this.svgEl.dom.createSVGPoint();
47785         pt.x = e.clientX; 
47786         pt.y = e.clientY;
47787         if (this.isTouchEvent(e)) {
47788             pt.x =  e.targetTouches[0].clientX 
47789             pt.y = e.targetTouches[0].clientY;
47790         }
47791         var a = this.svgEl.dom.getScreenCTM();
47792         var b = a.inverse();
47793         var mx = pt.matrixTransform(b);
47794         return mx.x + ',' + mx.y;
47795     },
47796     //mouse event headler 
47797     down : function (e) {
47798         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47799         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47800         
47801         this.isMouseDown = true;
47802         
47803         e.preventDefault();
47804     },
47805     move : function (e) {
47806         if (this.isMouseDown) {
47807             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47808             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47809         }
47810         
47811         e.preventDefault();
47812     },
47813     up : function (e) {
47814         this.isMouseDown = false;
47815         var sp = this.signatureTmp.split(' ');
47816         
47817         if(sp.length > 1){
47818             if(!sp[sp.length-2].match(/^L/)){
47819                 sp.pop();
47820                 sp.pop();
47821                 sp.push("");
47822                 this.signatureTmp = sp.join(" ");
47823             }
47824         }
47825         if(this.getValue() != this.signatureTmp){
47826             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47827             this.isConfirmed = false;
47828         }
47829         e.preventDefault();
47830     },
47831     
47832     /**
47833      * Protected method that will not generally be called directly. It
47834      * is called when the editor creates its toolbar. Override this method if you need to
47835      * add custom toolbar buttons.
47836      * @param {HtmlEditor} editor
47837      */
47838     createToolbar : function(editor){
47839          function btn(id, toggle, handler){
47840             var xid = fid + '-'+ id ;
47841             return {
47842                 id : xid,
47843                 cmd : id,
47844                 cls : 'x-btn-icon x-edit-'+id,
47845                 enableToggle:toggle !== false,
47846                 scope: editor, // was editor...
47847                 handler:handler||editor.relayBtnCmd,
47848                 clickEvent:'mousedown',
47849                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47850                 tabIndex:-1
47851             };
47852         }
47853         
47854         
47855         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47856         this.tb = tb;
47857         this.tb.add(
47858            {
47859                 cls : ' x-signature-btn x-signature-'+id,
47860                 scope: editor, // was editor...
47861                 handler: this.reset,
47862                 clickEvent:'mousedown',
47863                 text: this.labels.clear
47864             },
47865             {
47866                  xtype : 'Fill',
47867                  xns: Roo.Toolbar
47868             }, 
47869             {
47870                 cls : '  x-signature-btn x-signature-'+id,
47871                 scope: editor, // was editor...
47872                 handler: this.confirmHandler,
47873                 clickEvent:'mousedown',
47874                 text: this.labels.confirm
47875             }
47876         );
47877     
47878     },
47879     //public
47880     /**
47881      * when user is clicked confirm then show this image.....
47882      * 
47883      * @return {String} Image Data URI
47884      */
47885     getImageDataURI : function(){
47886         var svg = this.svgEl.dom.parentNode.innerHTML;
47887         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47888         return src; 
47889     },
47890     /**
47891      * 
47892      * @return {Boolean} this.isConfirmed
47893      */
47894     getConfirmed : function(){
47895         return this.isConfirmed;
47896     },
47897     /**
47898      * 
47899      * @return {Number} this.width
47900      */
47901     getWidth : function(){
47902         return this.width;
47903     },
47904     /**
47905      * 
47906      * @return {Number} this.height
47907      */
47908     getHeight : function(){
47909         return this.height;
47910     },
47911     // private
47912     getSignature : function(){
47913         return this.signatureTmp;
47914     },
47915     // private
47916     reset : function(){
47917         this.signatureTmp = '';
47918         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47919         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47920         this.isConfirmed = false;
47921         Roo.form.Signature.superclass.reset.call(this);
47922     },
47923     setSignature : function(s){
47924         this.signatureTmp = s;
47925         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47926         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47927         this.setValue(s);
47928         this.isConfirmed = false;
47929         Roo.form.Signature.superclass.reset.call(this);
47930     }, 
47931     test : function(){
47932 //        Roo.log(this.signPanel.dom.contentWindow.up())
47933     },
47934     //private
47935     setConfirmed : function(){
47936         
47937         
47938         
47939 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47940     },
47941     // private
47942     confirmHandler : function(){
47943         if(!this.getSignature()){
47944             return;
47945         }
47946         
47947         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47948         this.setValue(this.getSignature());
47949         this.isConfirmed = true;
47950         
47951         this.fireEvent('confirm', this);
47952     },
47953     // private
47954     // Subclasses should provide the validation implementation by overriding this
47955     validateValue : function(value){
47956         if(this.allowBlank){
47957             return true;
47958         }
47959         
47960         if(this.isConfirmed){
47961             return true;
47962         }
47963         return false;
47964     }
47965 });/*
47966  * Based on:
47967  * Ext JS Library 1.1.1
47968  * Copyright(c) 2006-2007, Ext JS, LLC.
47969  *
47970  * Originally Released Under LGPL - original licence link has changed is not relivant.
47971  *
47972  * Fork - LGPL
47973  * <script type="text/javascript">
47974  */
47975  
47976
47977 /**
47978  * @class Roo.form.ComboBox
47979  * @extends Roo.form.TriggerField
47980  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47981  * @constructor
47982  * Create a new ComboBox.
47983  * @param {Object} config Configuration options
47984  */
47985 Roo.form.Select = function(config){
47986     Roo.form.Select.superclass.constructor.call(this, config);
47987      
47988 };
47989
47990 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47991     /**
47992      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47993      */
47994     /**
47995      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47996      * rendering into an Roo.Editor, defaults to false)
47997      */
47998     /**
47999      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48000      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48001      */
48002     /**
48003      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48004      */
48005     /**
48006      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48007      * the dropdown list (defaults to undefined, with no header element)
48008      */
48009
48010      /**
48011      * @cfg {String/Roo.Template} tpl The template to use to render the output
48012      */
48013      
48014     // private
48015     defaultAutoCreate : {tag: "select"  },
48016     /**
48017      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48018      */
48019     listWidth: undefined,
48020     /**
48021      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48022      * mode = 'remote' or 'text' if mode = 'local')
48023      */
48024     displayField: undefined,
48025     /**
48026      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48027      * mode = 'remote' or 'value' if mode = 'local'). 
48028      * Note: use of a valueField requires the user make a selection
48029      * in order for a value to be mapped.
48030      */
48031     valueField: undefined,
48032     
48033     
48034     /**
48035      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48036      * field's data value (defaults to the underlying DOM element's name)
48037      */
48038     hiddenName: undefined,
48039     /**
48040      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48041      */
48042     listClass: '',
48043     /**
48044      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48045      */
48046     selectedClass: 'x-combo-selected',
48047     /**
48048      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48049      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48050      * which displays a downward arrow icon).
48051      */
48052     triggerClass : 'x-form-arrow-trigger',
48053     /**
48054      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48055      */
48056     shadow:'sides',
48057     /**
48058      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48059      * anchor positions (defaults to 'tl-bl')
48060      */
48061     listAlign: 'tl-bl?',
48062     /**
48063      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48064      */
48065     maxHeight: 300,
48066     /**
48067      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48068      * query specified by the allQuery config option (defaults to 'query')
48069      */
48070     triggerAction: 'query',
48071     /**
48072      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48073      * (defaults to 4, does not apply if editable = false)
48074      */
48075     minChars : 4,
48076     /**
48077      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48078      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48079      */
48080     typeAhead: false,
48081     /**
48082      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48083      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48084      */
48085     queryDelay: 500,
48086     /**
48087      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48088      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48089      */
48090     pageSize: 0,
48091     /**
48092      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48093      * when editable = true (defaults to false)
48094      */
48095     selectOnFocus:false,
48096     /**
48097      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48098      */
48099     queryParam: 'query',
48100     /**
48101      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48102      * when mode = 'remote' (defaults to 'Loading...')
48103      */
48104     loadingText: 'Loading...',
48105     /**
48106      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48107      */
48108     resizable: false,
48109     /**
48110      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48111      */
48112     handleHeight : 8,
48113     /**
48114      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48115      * traditional select (defaults to true)
48116      */
48117     editable: true,
48118     /**
48119      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48120      */
48121     allQuery: '',
48122     /**
48123      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48124      */
48125     mode: 'remote',
48126     /**
48127      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48128      * listWidth has a higher value)
48129      */
48130     minListWidth : 70,
48131     /**
48132      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48133      * allow the user to set arbitrary text into the field (defaults to false)
48134      */
48135     forceSelection:false,
48136     /**
48137      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48138      * if typeAhead = true (defaults to 250)
48139      */
48140     typeAheadDelay : 250,
48141     /**
48142      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48143      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48144      */
48145     valueNotFoundText : undefined,
48146     
48147     /**
48148      * @cfg {String} defaultValue The value displayed after loading the store.
48149      */
48150     defaultValue: '',
48151     
48152     /**
48153      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48154      */
48155     blockFocus : false,
48156     
48157     /**
48158      * @cfg {Boolean} disableClear Disable showing of clear button.
48159      */
48160     disableClear : false,
48161     /**
48162      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48163      */
48164     alwaysQuery : false,
48165     
48166     //private
48167     addicon : false,
48168     editicon: false,
48169     
48170     // element that contains real text value.. (when hidden is used..)
48171      
48172     // private
48173     onRender : function(ct, position){
48174         Roo.form.Field.prototype.onRender.call(this, ct, position);
48175         
48176         if(this.store){
48177             this.store.on('beforeload', this.onBeforeLoad, this);
48178             this.store.on('load', this.onLoad, this);
48179             this.store.on('loadexception', this.onLoadException, this);
48180             this.store.load({});
48181         }
48182         
48183         
48184         
48185     },
48186
48187     // private
48188     initEvents : function(){
48189         //Roo.form.ComboBox.superclass.initEvents.call(this);
48190  
48191     },
48192
48193     onDestroy : function(){
48194        
48195         if(this.store){
48196             this.store.un('beforeload', this.onBeforeLoad, this);
48197             this.store.un('load', this.onLoad, this);
48198             this.store.un('loadexception', this.onLoadException, this);
48199         }
48200         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48201     },
48202
48203     // private
48204     fireKey : function(e){
48205         if(e.isNavKeyPress() && !this.list.isVisible()){
48206             this.fireEvent("specialkey", this, e);
48207         }
48208     },
48209
48210     // private
48211     onResize: function(w, h){
48212         
48213         return; 
48214     
48215         
48216     },
48217
48218     /**
48219      * Allow or prevent the user from directly editing the field text.  If false is passed,
48220      * the user will only be able to select from the items defined in the dropdown list.  This method
48221      * is the runtime equivalent of setting the 'editable' config option at config time.
48222      * @param {Boolean} value True to allow the user to directly edit the field text
48223      */
48224     setEditable : function(value){
48225          
48226     },
48227
48228     // private
48229     onBeforeLoad : function(){
48230         
48231         Roo.log("Select before load");
48232         return;
48233     
48234         this.innerList.update(this.loadingText ?
48235                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48236         //this.restrictHeight();
48237         this.selectedIndex = -1;
48238     },
48239
48240     // private
48241     onLoad : function(){
48242
48243     
48244         var dom = this.el.dom;
48245         dom.innerHTML = '';
48246          var od = dom.ownerDocument;
48247          
48248         if (this.emptyText) {
48249             var op = od.createElement('option');
48250             op.setAttribute('value', '');
48251             op.innerHTML = String.format('{0}', this.emptyText);
48252             dom.appendChild(op);
48253         }
48254         if(this.store.getCount() > 0){
48255            
48256             var vf = this.valueField;
48257             var df = this.displayField;
48258             this.store.data.each(function(r) {
48259                 // which colmsn to use... testing - cdoe / title..
48260                 var op = od.createElement('option');
48261                 op.setAttribute('value', r.data[vf]);
48262                 op.innerHTML = String.format('{0}', r.data[df]);
48263                 dom.appendChild(op);
48264             });
48265             if (typeof(this.defaultValue != 'undefined')) {
48266                 this.setValue(this.defaultValue);
48267             }
48268             
48269              
48270         }else{
48271             //this.onEmptyResults();
48272         }
48273         //this.el.focus();
48274     },
48275     // private
48276     onLoadException : function()
48277     {
48278         dom.innerHTML = '';
48279             
48280         Roo.log("Select on load exception");
48281         return;
48282     
48283         this.collapse();
48284         Roo.log(this.store.reader.jsonData);
48285         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48286             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48287         }
48288         
48289         
48290     },
48291     // private
48292     onTypeAhead : function(){
48293          
48294     },
48295
48296     // private
48297     onSelect : function(record, index){
48298         Roo.log('on select?');
48299         return;
48300         if(this.fireEvent('beforeselect', this, record, index) !== false){
48301             this.setFromData(index > -1 ? record.data : false);
48302             this.collapse();
48303             this.fireEvent('select', this, record, index);
48304         }
48305     },
48306
48307     /**
48308      * Returns the currently selected field value or empty string if no value is set.
48309      * @return {String} value The selected value
48310      */
48311     getValue : function(){
48312         var dom = this.el.dom;
48313         this.value = dom.options[dom.selectedIndex].value;
48314         return this.value;
48315         
48316     },
48317
48318     /**
48319      * Clears any text/value currently set in the field
48320      */
48321     clearValue : function(){
48322         this.value = '';
48323         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48324         
48325     },
48326
48327     /**
48328      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48329      * will be displayed in the field.  If the value does not match the data value of an existing item,
48330      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48331      * Otherwise the field will be blank (although the value will still be set).
48332      * @param {String} value The value to match
48333      */
48334     setValue : function(v){
48335         var d = this.el.dom;
48336         for (var i =0; i < d.options.length;i++) {
48337             if (v == d.options[i].value) {
48338                 d.selectedIndex = i;
48339                 this.value = v;
48340                 return;
48341             }
48342         }
48343         this.clearValue();
48344     },
48345     /**
48346      * @property {Object} the last set data for the element
48347      */
48348     
48349     lastData : false,
48350     /**
48351      * Sets the value of the field based on a object which is related to the record format for the store.
48352      * @param {Object} value the value to set as. or false on reset?
48353      */
48354     setFromData : function(o){
48355         Roo.log('setfrom data?');
48356          
48357         
48358         
48359     },
48360     // private
48361     reset : function(){
48362         this.clearValue();
48363     },
48364     // private
48365     findRecord : function(prop, value){
48366         
48367         return false;
48368     
48369         var record;
48370         if(this.store.getCount() > 0){
48371             this.store.each(function(r){
48372                 if(r.data[prop] == value){
48373                     record = r;
48374                     return false;
48375                 }
48376                 return true;
48377             });
48378         }
48379         return record;
48380     },
48381     
48382     getName: function()
48383     {
48384         // returns hidden if it's set..
48385         if (!this.rendered) {return ''};
48386         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48387         
48388     },
48389      
48390
48391     
48392
48393     // private
48394     onEmptyResults : function(){
48395         Roo.log('empty results');
48396         //this.collapse();
48397     },
48398
48399     /**
48400      * Returns true if the dropdown list is expanded, else false.
48401      */
48402     isExpanded : function(){
48403         return false;
48404     },
48405
48406     /**
48407      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48408      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48409      * @param {String} value The data value of the item to select
48410      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48411      * selected item if it is not currently in view (defaults to true)
48412      * @return {Boolean} True if the value matched an item in the list, else false
48413      */
48414     selectByValue : function(v, scrollIntoView){
48415         Roo.log('select By Value');
48416         return false;
48417     
48418         if(v !== undefined && v !== null){
48419             var r = this.findRecord(this.valueField || this.displayField, v);
48420             if(r){
48421                 this.select(this.store.indexOf(r), scrollIntoView);
48422                 return true;
48423             }
48424         }
48425         return false;
48426     },
48427
48428     /**
48429      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48430      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48431      * @param {Number} index The zero-based index of the list item to select
48432      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48433      * selected item if it is not currently in view (defaults to true)
48434      */
48435     select : function(index, scrollIntoView){
48436         Roo.log('select ');
48437         return  ;
48438         
48439         this.selectedIndex = index;
48440         this.view.select(index);
48441         if(scrollIntoView !== false){
48442             var el = this.view.getNode(index);
48443             if(el){
48444                 this.innerList.scrollChildIntoView(el, false);
48445             }
48446         }
48447     },
48448
48449       
48450
48451     // private
48452     validateBlur : function(){
48453         
48454         return;
48455         
48456     },
48457
48458     // private
48459     initQuery : function(){
48460         this.doQuery(this.getRawValue());
48461     },
48462
48463     // private
48464     doForce : function(){
48465         if(this.el.dom.value.length > 0){
48466             this.el.dom.value =
48467                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48468              
48469         }
48470     },
48471
48472     /**
48473      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48474      * query allowing the query action to be canceled if needed.
48475      * @param {String} query The SQL query to execute
48476      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48477      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48478      * saved in the current store (defaults to false)
48479      */
48480     doQuery : function(q, forceAll){
48481         
48482         Roo.log('doQuery?');
48483         if(q === undefined || q === null){
48484             q = '';
48485         }
48486         var qe = {
48487             query: q,
48488             forceAll: forceAll,
48489             combo: this,
48490             cancel:false
48491         };
48492         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48493             return false;
48494         }
48495         q = qe.query;
48496         forceAll = qe.forceAll;
48497         if(forceAll === true || (q.length >= this.minChars)){
48498             if(this.lastQuery != q || this.alwaysQuery){
48499                 this.lastQuery = q;
48500                 if(this.mode == 'local'){
48501                     this.selectedIndex = -1;
48502                     if(forceAll){
48503                         this.store.clearFilter();
48504                     }else{
48505                         this.store.filter(this.displayField, q);
48506                     }
48507                     this.onLoad();
48508                 }else{
48509                     this.store.baseParams[this.queryParam] = q;
48510                     this.store.load({
48511                         params: this.getParams(q)
48512                     });
48513                     this.expand();
48514                 }
48515             }else{
48516                 this.selectedIndex = -1;
48517                 this.onLoad();   
48518             }
48519         }
48520     },
48521
48522     // private
48523     getParams : function(q){
48524         var p = {};
48525         //p[this.queryParam] = q;
48526         if(this.pageSize){
48527             p.start = 0;
48528             p.limit = this.pageSize;
48529         }
48530         return p;
48531     },
48532
48533     /**
48534      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48535      */
48536     collapse : function(){
48537         
48538     },
48539
48540     // private
48541     collapseIf : function(e){
48542         
48543     },
48544
48545     /**
48546      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48547      */
48548     expand : function(){
48549         
48550     } ,
48551
48552     // private
48553      
48554
48555     /** 
48556     * @cfg {Boolean} grow 
48557     * @hide 
48558     */
48559     /** 
48560     * @cfg {Number} growMin 
48561     * @hide 
48562     */
48563     /** 
48564     * @cfg {Number} growMax 
48565     * @hide 
48566     */
48567     /**
48568      * @hide
48569      * @method autoSize
48570      */
48571     
48572     setWidth : function()
48573     {
48574         
48575     },
48576     getResizeEl : function(){
48577         return this.el;
48578     }
48579 });//<script type="text/javasscript">
48580  
48581
48582 /**
48583  * @class Roo.DDView
48584  * A DnD enabled version of Roo.View.
48585  * @param {Element/String} container The Element in which to create the View.
48586  * @param {String} tpl The template string used to create the markup for each element of the View
48587  * @param {Object} config The configuration properties. These include all the config options of
48588  * {@link Roo.View} plus some specific to this class.<br>
48589  * <p>
48590  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48591  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48592  * <p>
48593  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48594 .x-view-drag-insert-above {
48595         border-top:1px dotted #3366cc;
48596 }
48597 .x-view-drag-insert-below {
48598         border-bottom:1px dotted #3366cc;
48599 }
48600 </code></pre>
48601  * 
48602  */
48603  
48604 Roo.DDView = function(container, tpl, config) {
48605     Roo.DDView.superclass.constructor.apply(this, arguments);
48606     this.getEl().setStyle("outline", "0px none");
48607     this.getEl().unselectable();
48608     if (this.dragGroup) {
48609                 this.setDraggable(this.dragGroup.split(","));
48610     }
48611     if (this.dropGroup) {
48612                 this.setDroppable(this.dropGroup.split(","));
48613     }
48614     if (this.deletable) {
48615         this.setDeletable();
48616     }
48617     this.isDirtyFlag = false;
48618         this.addEvents({
48619                 "drop" : true
48620         });
48621 };
48622
48623 Roo.extend(Roo.DDView, Roo.View, {
48624 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48625 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48626 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48627 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48628
48629         isFormField: true,
48630
48631         reset: Roo.emptyFn,
48632         
48633         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48634
48635         validate: function() {
48636                 return true;
48637         },
48638         
48639         destroy: function() {
48640                 this.purgeListeners();
48641                 this.getEl.removeAllListeners();
48642                 this.getEl().remove();
48643                 if (this.dragZone) {
48644                         if (this.dragZone.destroy) {
48645                                 this.dragZone.destroy();
48646                         }
48647                 }
48648                 if (this.dropZone) {
48649                         if (this.dropZone.destroy) {
48650                                 this.dropZone.destroy();
48651                         }
48652                 }
48653         },
48654
48655 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48656         getName: function() {
48657                 return this.name;
48658         },
48659
48660 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48661         setValue: function(v) {
48662                 if (!this.store) {
48663                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48664                 }
48665                 var data = {};
48666                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48667                 this.store.proxy = new Roo.data.MemoryProxy(data);
48668                 this.store.load();
48669         },
48670
48671 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48672         getValue: function() {
48673                 var result = '(';
48674                 this.store.each(function(rec) {
48675                         result += rec.id + ',';
48676                 });
48677                 return result.substr(0, result.length - 1) + ')';
48678         },
48679         
48680         getIds: function() {
48681                 var i = 0, result = new Array(this.store.getCount());
48682                 this.store.each(function(rec) {
48683                         result[i++] = rec.id;
48684                 });
48685                 return result;
48686         },
48687         
48688         isDirty: function() {
48689                 return this.isDirtyFlag;
48690         },
48691
48692 /**
48693  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48694  *      whole Element becomes the target, and this causes the drop gesture to append.
48695  */
48696     getTargetFromEvent : function(e) {
48697                 var target = e.getTarget();
48698                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48699                 target = target.parentNode;
48700                 }
48701                 if (!target) {
48702                         target = this.el.dom.lastChild || this.el.dom;
48703                 }
48704                 return target;
48705     },
48706
48707 /**
48708  *      Create the drag data which consists of an object which has the property "ddel" as
48709  *      the drag proxy element. 
48710  */
48711     getDragData : function(e) {
48712         var target = this.findItemFromChild(e.getTarget());
48713                 if(target) {
48714                         this.handleSelection(e);
48715                         var selNodes = this.getSelectedNodes();
48716             var dragData = {
48717                 source: this,
48718                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48719                 nodes: selNodes,
48720                 records: []
48721                         };
48722                         var selectedIndices = this.getSelectedIndexes();
48723                         for (var i = 0; i < selectedIndices.length; i++) {
48724                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48725                         }
48726                         if (selNodes.length == 1) {
48727                                 dragData.ddel = target.cloneNode(true); // the div element
48728                         } else {
48729                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48730                                 div.className = 'multi-proxy';
48731                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48732                                         div.appendChild(selNodes[i].cloneNode(true));
48733                                 }
48734                                 dragData.ddel = div;
48735                         }
48736             //console.log(dragData)
48737             //console.log(dragData.ddel.innerHTML)
48738                         return dragData;
48739                 }
48740         //console.log('nodragData')
48741                 return false;
48742     },
48743     
48744 /**     Specify to which ddGroup items in this DDView may be dragged. */
48745     setDraggable: function(ddGroup) {
48746         if (ddGroup instanceof Array) {
48747                 Roo.each(ddGroup, this.setDraggable, this);
48748                 return;
48749         }
48750         if (this.dragZone) {
48751                 this.dragZone.addToGroup(ddGroup);
48752         } else {
48753                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48754                                 containerScroll: true,
48755                                 ddGroup: ddGroup 
48756
48757                         });
48758 //                      Draggability implies selection. DragZone's mousedown selects the element.
48759                         if (!this.multiSelect) { this.singleSelect = true; }
48760
48761 //                      Wire the DragZone's handlers up to methods in *this*
48762                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48763                 }
48764     },
48765
48766 /**     Specify from which ddGroup this DDView accepts drops. */
48767     setDroppable: function(ddGroup) {
48768         if (ddGroup instanceof Array) {
48769                 Roo.each(ddGroup, this.setDroppable, this);
48770                 return;
48771         }
48772         if (this.dropZone) {
48773                 this.dropZone.addToGroup(ddGroup);
48774         } else {
48775                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48776                                 containerScroll: true,
48777                                 ddGroup: ddGroup
48778                         });
48779
48780 //                      Wire the DropZone's handlers up to methods in *this*
48781                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48782                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48783                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48784                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48785                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48786                 }
48787     },
48788
48789 /**     Decide whether to drop above or below a View node. */
48790     getDropPoint : function(e, n, dd){
48791         if (n == this.el.dom) { return "above"; }
48792                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48793                 var c = t + (b - t) / 2;
48794                 var y = Roo.lib.Event.getPageY(e);
48795                 if(y <= c) {
48796                         return "above";
48797                 }else{
48798                         return "below";
48799                 }
48800     },
48801
48802     onNodeEnter : function(n, dd, e, data){
48803                 return false;
48804     },
48805     
48806     onNodeOver : function(n, dd, e, data){
48807                 var pt = this.getDropPoint(e, n, dd);
48808                 // set the insert point style on the target node
48809                 var dragElClass = this.dropNotAllowed;
48810                 if (pt) {
48811                         var targetElClass;
48812                         if (pt == "above"){
48813                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48814                                 targetElClass = "x-view-drag-insert-above";
48815                         } else {
48816                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48817                                 targetElClass = "x-view-drag-insert-below";
48818                         }
48819                         if (this.lastInsertClass != targetElClass){
48820                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48821                                 this.lastInsertClass = targetElClass;
48822                         }
48823                 }
48824                 return dragElClass;
48825         },
48826
48827     onNodeOut : function(n, dd, e, data){
48828                 this.removeDropIndicators(n);
48829     },
48830
48831     onNodeDrop : function(n, dd, e, data){
48832         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48833                 return false;
48834         }
48835         var pt = this.getDropPoint(e, n, dd);
48836                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48837                 if (pt == "below") { insertAt++; }
48838                 for (var i = 0; i < data.records.length; i++) {
48839                         var r = data.records[i];
48840                         var dup = this.store.getById(r.id);
48841                         if (dup && (dd != this.dragZone)) {
48842                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48843                         } else {
48844                                 if (data.copy) {
48845                                         this.store.insert(insertAt++, r.copy());
48846                                 } else {
48847                                         data.source.isDirtyFlag = true;
48848                                         r.store.remove(r);
48849                                         this.store.insert(insertAt++, r);
48850                                 }
48851                                 this.isDirtyFlag = true;
48852                         }
48853                 }
48854                 this.dragZone.cachedTarget = null;
48855                 return true;
48856     },
48857
48858     removeDropIndicators : function(n){
48859                 if(n){
48860                         Roo.fly(n).removeClass([
48861                                 "x-view-drag-insert-above",
48862                                 "x-view-drag-insert-below"]);
48863                         this.lastInsertClass = "_noclass";
48864                 }
48865     },
48866
48867 /**
48868  *      Utility method. Add a delete option to the DDView's context menu.
48869  *      @param {String} imageUrl The URL of the "delete" icon image.
48870  */
48871         setDeletable: function(imageUrl) {
48872                 if (!this.singleSelect && !this.multiSelect) {
48873                         this.singleSelect = true;
48874                 }
48875                 var c = this.getContextMenu();
48876                 this.contextMenu.on("itemclick", function(item) {
48877                         switch (item.id) {
48878                                 case "delete":
48879                                         this.remove(this.getSelectedIndexes());
48880                                         break;
48881                         }
48882                 }, this);
48883                 this.contextMenu.add({
48884                         icon: imageUrl,
48885                         id: "delete",
48886                         text: 'Delete'
48887                 });
48888         },
48889         
48890 /**     Return the context menu for this DDView. */
48891         getContextMenu: function() {
48892                 if (!this.contextMenu) {
48893 //                      Create the View's context menu
48894                         this.contextMenu = new Roo.menu.Menu({
48895                                 id: this.id + "-contextmenu"
48896                         });
48897                         this.el.on("contextmenu", this.showContextMenu, this);
48898                 }
48899                 return this.contextMenu;
48900         },
48901         
48902         disableContextMenu: function() {
48903                 if (this.contextMenu) {
48904                         this.el.un("contextmenu", this.showContextMenu, this);
48905                 }
48906         },
48907
48908         showContextMenu: function(e, item) {
48909         item = this.findItemFromChild(e.getTarget());
48910                 if (item) {
48911                         e.stopEvent();
48912                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48913                         this.contextMenu.showAt(e.getXY());
48914             }
48915     },
48916
48917 /**
48918  *      Remove {@link Roo.data.Record}s at the specified indices.
48919  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48920  */
48921     remove: function(selectedIndices) {
48922                 selectedIndices = [].concat(selectedIndices);
48923                 for (var i = 0; i < selectedIndices.length; i++) {
48924                         var rec = this.store.getAt(selectedIndices[i]);
48925                         this.store.remove(rec);
48926                 }
48927     },
48928
48929 /**
48930  *      Double click fires the event, but also, if this is draggable, and there is only one other
48931  *      related DropZone, it transfers the selected node.
48932  */
48933     onDblClick : function(e){
48934         var item = this.findItemFromChild(e.getTarget());
48935         if(item){
48936             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48937                 return false;
48938             }
48939             if (this.dragGroup) {
48940                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48941                     while (targets.indexOf(this.dropZone) > -1) {
48942                             targets.remove(this.dropZone);
48943                                 }
48944                     if (targets.length == 1) {
48945                                         this.dragZone.cachedTarget = null;
48946                         var el = Roo.get(targets[0].getEl());
48947                         var box = el.getBox(true);
48948                         targets[0].onNodeDrop(el.dom, {
48949                                 target: el.dom,
48950                                 xy: [box.x, box.y + box.height - 1]
48951                         }, null, this.getDragData(e));
48952                     }
48953                 }
48954         }
48955     },
48956     
48957     handleSelection: function(e) {
48958                 this.dragZone.cachedTarget = null;
48959         var item = this.findItemFromChild(e.getTarget());
48960         if (!item) {
48961                 this.clearSelections(true);
48962                 return;
48963         }
48964                 if (item && (this.multiSelect || this.singleSelect)){
48965                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48966                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48967                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48968                                 this.unselect(item);
48969                         } else {
48970                                 this.select(item, this.multiSelect && e.ctrlKey);
48971                                 this.lastSelection = item;
48972                         }
48973                 }
48974     },
48975
48976     onItemClick : function(item, index, e){
48977                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48978                         return false;
48979                 }
48980                 return true;
48981     },
48982
48983     unselect : function(nodeInfo, suppressEvent){
48984                 var node = this.getNode(nodeInfo);
48985                 if(node && this.isSelected(node)){
48986                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48987                                 Roo.fly(node).removeClass(this.selectedClass);
48988                                 this.selections.remove(node);
48989                                 if(!suppressEvent){
48990                                         this.fireEvent("selectionchange", this, this.selections);
48991                                 }
48992                         }
48993                 }
48994     }
48995 });
48996 /*
48997  * Based on:
48998  * Ext JS Library 1.1.1
48999  * Copyright(c) 2006-2007, Ext JS, LLC.
49000  *
49001  * Originally Released Under LGPL - original licence link has changed is not relivant.
49002  *
49003  * Fork - LGPL
49004  * <script type="text/javascript">
49005  */
49006  
49007 /**
49008  * @class Roo.LayoutManager
49009  * @extends Roo.util.Observable
49010  * Base class for layout managers.
49011  */
49012 Roo.LayoutManager = function(container, config){
49013     Roo.LayoutManager.superclass.constructor.call(this);
49014     this.el = Roo.get(container);
49015     // ie scrollbar fix
49016     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49017         document.body.scroll = "no";
49018     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49019         this.el.position('relative');
49020     }
49021     this.id = this.el.id;
49022     this.el.addClass("x-layout-container");
49023     /** false to disable window resize monitoring @type Boolean */
49024     this.monitorWindowResize = true;
49025     this.regions = {};
49026     this.addEvents({
49027         /**
49028          * @event layout
49029          * Fires when a layout is performed. 
49030          * @param {Roo.LayoutManager} this
49031          */
49032         "layout" : true,
49033         /**
49034          * @event regionresized
49035          * Fires when the user resizes a region. 
49036          * @param {Roo.LayoutRegion} region The resized region
49037          * @param {Number} newSize The new size (width for east/west, height for north/south)
49038          */
49039         "regionresized" : true,
49040         /**
49041          * @event regioncollapsed
49042          * Fires when a region is collapsed. 
49043          * @param {Roo.LayoutRegion} region The collapsed region
49044          */
49045         "regioncollapsed" : true,
49046         /**
49047          * @event regionexpanded
49048          * Fires when a region is expanded.  
49049          * @param {Roo.LayoutRegion} region The expanded region
49050          */
49051         "regionexpanded" : true
49052     });
49053     this.updating = false;
49054     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49055 };
49056
49057 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49058     /**
49059      * Returns true if this layout is currently being updated
49060      * @return {Boolean}
49061      */
49062     isUpdating : function(){
49063         return this.updating; 
49064     },
49065     
49066     /**
49067      * Suspend the LayoutManager from doing auto-layouts while
49068      * making multiple add or remove calls
49069      */
49070     beginUpdate : function(){
49071         this.updating = true;    
49072     },
49073     
49074     /**
49075      * Restore auto-layouts and optionally disable the manager from performing a layout
49076      * @param {Boolean} noLayout true to disable a layout update 
49077      */
49078     endUpdate : function(noLayout){
49079         this.updating = false;
49080         if(!noLayout){
49081             this.layout();
49082         }    
49083     },
49084     
49085     layout: function(){
49086         
49087     },
49088     
49089     onRegionResized : function(region, newSize){
49090         this.fireEvent("regionresized", region, newSize);
49091         this.layout();
49092     },
49093     
49094     onRegionCollapsed : function(region){
49095         this.fireEvent("regioncollapsed", region);
49096     },
49097     
49098     onRegionExpanded : function(region){
49099         this.fireEvent("regionexpanded", region);
49100     },
49101         
49102     /**
49103      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49104      * performs box-model adjustments.
49105      * @return {Object} The size as an object {width: (the width), height: (the height)}
49106      */
49107     getViewSize : function(){
49108         var size;
49109         if(this.el.dom != document.body){
49110             size = this.el.getSize();
49111         }else{
49112             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49113         }
49114         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49115         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49116         return size;
49117     },
49118     
49119     /**
49120      * Returns the Element this layout is bound to.
49121      * @return {Roo.Element}
49122      */
49123     getEl : function(){
49124         return this.el;
49125     },
49126     
49127     /**
49128      * Returns the specified region.
49129      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49130      * @return {Roo.LayoutRegion}
49131      */
49132     getRegion : function(target){
49133         return this.regions[target.toLowerCase()];
49134     },
49135     
49136     onWindowResize : function(){
49137         if(this.monitorWindowResize){
49138             this.layout();
49139         }
49140     }
49141 });/*
49142  * Based on:
49143  * Ext JS Library 1.1.1
49144  * Copyright(c) 2006-2007, Ext JS, LLC.
49145  *
49146  * Originally Released Under LGPL - original licence link has changed is not relivant.
49147  *
49148  * Fork - LGPL
49149  * <script type="text/javascript">
49150  */
49151 /**
49152  * @class Roo.BorderLayout
49153  * @extends Roo.LayoutManager
49154  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49155  * please see: <br><br>
49156  * <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>
49157  * <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>
49158  * Example:
49159  <pre><code>
49160  var layout = new Roo.BorderLayout(document.body, {
49161     north: {
49162         initialSize: 25,
49163         titlebar: false
49164     },
49165     west: {
49166         split:true,
49167         initialSize: 200,
49168         minSize: 175,
49169         maxSize: 400,
49170         titlebar: true,
49171         collapsible: true
49172     },
49173     east: {
49174         split:true,
49175         initialSize: 202,
49176         minSize: 175,
49177         maxSize: 400,
49178         titlebar: true,
49179         collapsible: true
49180     },
49181     south: {
49182         split:true,
49183         initialSize: 100,
49184         minSize: 100,
49185         maxSize: 200,
49186         titlebar: true,
49187         collapsible: true
49188     },
49189     center: {
49190         titlebar: true,
49191         autoScroll:true,
49192         resizeTabs: true,
49193         minTabWidth: 50,
49194         preferredTabWidth: 150
49195     }
49196 });
49197
49198 // shorthand
49199 var CP = Roo.ContentPanel;
49200
49201 layout.beginUpdate();
49202 layout.add("north", new CP("north", "North"));
49203 layout.add("south", new CP("south", {title: "South", closable: true}));
49204 layout.add("west", new CP("west", {title: "West"}));
49205 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49206 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49207 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49208 layout.getRegion("center").showPanel("center1");
49209 layout.endUpdate();
49210 </code></pre>
49211
49212 <b>The container the layout is rendered into can be either the body element or any other element.
49213 If it is not the body element, the container needs to either be an absolute positioned element,
49214 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49215 the container size if it is not the body element.</b>
49216
49217 * @constructor
49218 * Create a new BorderLayout
49219 * @param {String/HTMLElement/Element} container The container this layout is bound to
49220 * @param {Object} config Configuration options
49221  */
49222 Roo.BorderLayout = function(container, config){
49223     config = config || {};
49224     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49225     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49226     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49227         var target = this.factory.validRegions[i];
49228         if(config[target]){
49229             this.addRegion(target, config[target]);
49230         }
49231     }
49232 };
49233
49234 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49235     /**
49236      * Creates and adds a new region if it doesn't already exist.
49237      * @param {String} target The target region key (north, south, east, west or center).
49238      * @param {Object} config The regions config object
49239      * @return {BorderLayoutRegion} The new region
49240      */
49241     addRegion : function(target, config){
49242         if(!this.regions[target]){
49243             var r = this.factory.create(target, this, config);
49244             this.bindRegion(target, r);
49245         }
49246         return this.regions[target];
49247     },
49248
49249     // private (kinda)
49250     bindRegion : function(name, r){
49251         this.regions[name] = r;
49252         r.on("visibilitychange", this.layout, this);
49253         r.on("paneladded", this.layout, this);
49254         r.on("panelremoved", this.layout, this);
49255         r.on("invalidated", this.layout, this);
49256         r.on("resized", this.onRegionResized, this);
49257         r.on("collapsed", this.onRegionCollapsed, this);
49258         r.on("expanded", this.onRegionExpanded, this);
49259     },
49260
49261     /**
49262      * Performs a layout update.
49263      */
49264     layout : function(){
49265         if(this.updating) return;
49266         var size = this.getViewSize();
49267         var w = size.width;
49268         var h = size.height;
49269         var centerW = w;
49270         var centerH = h;
49271         var centerY = 0;
49272         var centerX = 0;
49273         //var x = 0, y = 0;
49274
49275         var rs = this.regions;
49276         var north = rs["north"];
49277         var south = rs["south"]; 
49278         var west = rs["west"];
49279         var east = rs["east"];
49280         var center = rs["center"];
49281         //if(this.hideOnLayout){ // not supported anymore
49282             //c.el.setStyle("display", "none");
49283         //}
49284         if(north && north.isVisible()){
49285             var b = north.getBox();
49286             var m = north.getMargins();
49287             b.width = w - (m.left+m.right);
49288             b.x = m.left;
49289             b.y = m.top;
49290             centerY = b.height + b.y + m.bottom;
49291             centerH -= centerY;
49292             north.updateBox(this.safeBox(b));
49293         }
49294         if(south && south.isVisible()){
49295             var b = south.getBox();
49296             var m = south.getMargins();
49297             b.width = w - (m.left+m.right);
49298             b.x = m.left;
49299             var totalHeight = (b.height + m.top + m.bottom);
49300             b.y = h - totalHeight + m.top;
49301             centerH -= totalHeight;
49302             south.updateBox(this.safeBox(b));
49303         }
49304         if(west && west.isVisible()){
49305             var b = west.getBox();
49306             var m = west.getMargins();
49307             b.height = centerH - (m.top+m.bottom);
49308             b.x = m.left;
49309             b.y = centerY + m.top;
49310             var totalWidth = (b.width + m.left + m.right);
49311             centerX += totalWidth;
49312             centerW -= totalWidth;
49313             west.updateBox(this.safeBox(b));
49314         }
49315         if(east && east.isVisible()){
49316             var b = east.getBox();
49317             var m = east.getMargins();
49318             b.height = centerH - (m.top+m.bottom);
49319             var totalWidth = (b.width + m.left + m.right);
49320             b.x = w - totalWidth + m.left;
49321             b.y = centerY + m.top;
49322             centerW -= totalWidth;
49323             east.updateBox(this.safeBox(b));
49324         }
49325         if(center){
49326             var m = center.getMargins();
49327             var centerBox = {
49328                 x: centerX + m.left,
49329                 y: centerY + m.top,
49330                 width: centerW - (m.left+m.right),
49331                 height: centerH - (m.top+m.bottom)
49332             };
49333             //if(this.hideOnLayout){
49334                 //center.el.setStyle("display", "block");
49335             //}
49336             center.updateBox(this.safeBox(centerBox));
49337         }
49338         this.el.repaint();
49339         this.fireEvent("layout", this);
49340     },
49341
49342     // private
49343     safeBox : function(box){
49344         box.width = Math.max(0, box.width);
49345         box.height = Math.max(0, box.height);
49346         return box;
49347     },
49348
49349     /**
49350      * Adds a ContentPanel (or subclass) to this layout.
49351      * @param {String} target The target region key (north, south, east, west or center).
49352      * @param {Roo.ContentPanel} panel The panel to add
49353      * @return {Roo.ContentPanel} The added panel
49354      */
49355     add : function(target, panel){
49356          
49357         target = target.toLowerCase();
49358         return this.regions[target].add(panel);
49359     },
49360
49361     /**
49362      * Remove a ContentPanel (or subclass) to this layout.
49363      * @param {String} target The target region key (north, south, east, west or center).
49364      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49365      * @return {Roo.ContentPanel} The removed panel
49366      */
49367     remove : function(target, panel){
49368         target = target.toLowerCase();
49369         return this.regions[target].remove(panel);
49370     },
49371
49372     /**
49373      * Searches all regions for a panel with the specified id
49374      * @param {String} panelId
49375      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49376      */
49377     findPanel : function(panelId){
49378         var rs = this.regions;
49379         for(var target in rs){
49380             if(typeof rs[target] != "function"){
49381                 var p = rs[target].getPanel(panelId);
49382                 if(p){
49383                     return p;
49384                 }
49385             }
49386         }
49387         return null;
49388     },
49389
49390     /**
49391      * Searches all regions for a panel with the specified id and activates (shows) it.
49392      * @param {String/ContentPanel} panelId The panels id or the panel itself
49393      * @return {Roo.ContentPanel} The shown panel or null
49394      */
49395     showPanel : function(panelId) {
49396       var rs = this.regions;
49397       for(var target in rs){
49398          var r = rs[target];
49399          if(typeof r != "function"){
49400             if(r.hasPanel(panelId)){
49401                return r.showPanel(panelId);
49402             }
49403          }
49404       }
49405       return null;
49406    },
49407
49408    /**
49409      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49410      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49411      */
49412     restoreState : function(provider){
49413         if(!provider){
49414             provider = Roo.state.Manager;
49415         }
49416         var sm = new Roo.LayoutStateManager();
49417         sm.init(this, provider);
49418     },
49419
49420     /**
49421      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49422      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49423      * a valid ContentPanel config object.  Example:
49424      * <pre><code>
49425 // Create the main layout
49426 var layout = new Roo.BorderLayout('main-ct', {
49427     west: {
49428         split:true,
49429         minSize: 175,
49430         titlebar: true
49431     },
49432     center: {
49433         title:'Components'
49434     }
49435 }, 'main-ct');
49436
49437 // Create and add multiple ContentPanels at once via configs
49438 layout.batchAdd({
49439    west: {
49440        id: 'source-files',
49441        autoCreate:true,
49442        title:'Ext Source Files',
49443        autoScroll:true,
49444        fitToFrame:true
49445    },
49446    center : {
49447        el: cview,
49448        autoScroll:true,
49449        fitToFrame:true,
49450        toolbar: tb,
49451        resizeEl:'cbody'
49452    }
49453 });
49454 </code></pre>
49455      * @param {Object} regions An object containing ContentPanel configs by region name
49456      */
49457     batchAdd : function(regions){
49458         this.beginUpdate();
49459         for(var rname in regions){
49460             var lr = this.regions[rname];
49461             if(lr){
49462                 this.addTypedPanels(lr, regions[rname]);
49463             }
49464         }
49465         this.endUpdate();
49466     },
49467
49468     // private
49469     addTypedPanels : function(lr, ps){
49470         if(typeof ps == 'string'){
49471             lr.add(new Roo.ContentPanel(ps));
49472         }
49473         else if(ps instanceof Array){
49474             for(var i =0, len = ps.length; i < len; i++){
49475                 this.addTypedPanels(lr, ps[i]);
49476             }
49477         }
49478         else if(!ps.events){ // raw config?
49479             var el = ps.el;
49480             delete ps.el; // prevent conflict
49481             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49482         }
49483         else {  // panel object assumed!
49484             lr.add(ps);
49485         }
49486     },
49487     /**
49488      * Adds a xtype elements to the layout.
49489      * <pre><code>
49490
49491 layout.addxtype({
49492        xtype : 'ContentPanel',
49493        region: 'west',
49494        items: [ .... ]
49495    }
49496 );
49497
49498 layout.addxtype({
49499         xtype : 'NestedLayoutPanel',
49500         region: 'west',
49501         layout: {
49502            center: { },
49503            west: { }   
49504         },
49505         items : [ ... list of content panels or nested layout panels.. ]
49506    }
49507 );
49508 </code></pre>
49509      * @param {Object} cfg Xtype definition of item to add.
49510      */
49511     addxtype : function(cfg)
49512     {
49513         // basically accepts a pannel...
49514         // can accept a layout region..!?!?
49515         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49516         
49517         if (!cfg.xtype.match(/Panel$/)) {
49518             return false;
49519         }
49520         var ret = false;
49521         
49522         if (typeof(cfg.region) == 'undefined') {
49523             Roo.log("Failed to add Panel, region was not set");
49524             Roo.log(cfg);
49525             return false;
49526         }
49527         var region = cfg.region;
49528         delete cfg.region;
49529         
49530           
49531         var xitems = [];
49532         if (cfg.items) {
49533             xitems = cfg.items;
49534             delete cfg.items;
49535         }
49536         var nb = false;
49537         
49538         switch(cfg.xtype) 
49539         {
49540             case 'ContentPanel':  // ContentPanel (el, cfg)
49541             case 'ScrollPanel':  // ContentPanel (el, cfg)
49542             case 'ViewPanel': 
49543                 if(cfg.autoCreate) {
49544                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49545                 } else {
49546                     var el = this.el.createChild();
49547                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49548                 }
49549                 
49550                 this.add(region, ret);
49551                 break;
49552             
49553             
49554             case 'TreePanel': // our new panel!
49555                 cfg.el = this.el.createChild();
49556                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49557                 this.add(region, ret);
49558                 break;
49559             
49560             case 'NestedLayoutPanel': 
49561                 // create a new Layout (which is  a Border Layout...
49562                 var el = this.el.createChild();
49563                 var clayout = cfg.layout;
49564                 delete cfg.layout;
49565                 clayout.items   = clayout.items  || [];
49566                 // replace this exitems with the clayout ones..
49567                 xitems = clayout.items;
49568                  
49569                 
49570                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49571                     cfg.background = false;
49572                 }
49573                 var layout = new Roo.BorderLayout(el, clayout);
49574                 
49575                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49576                 //console.log('adding nested layout panel '  + cfg.toSource());
49577                 this.add(region, ret);
49578                 nb = {}; /// find first...
49579                 break;
49580                 
49581             case 'GridPanel': 
49582             
49583                 // needs grid and region
49584                 
49585                 //var el = this.getRegion(region).el.createChild();
49586                 var el = this.el.createChild();
49587                 // create the grid first...
49588                 
49589                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49590                 delete cfg.grid;
49591                 if (region == 'center' && this.active ) {
49592                     cfg.background = false;
49593                 }
49594                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49595                 
49596                 this.add(region, ret);
49597                 if (cfg.background) {
49598                     ret.on('activate', function(gp) {
49599                         if (!gp.grid.rendered) {
49600                             gp.grid.render();
49601                         }
49602                     });
49603                 } else {
49604                     grid.render();
49605                 }
49606                 break;
49607            
49608            
49609            
49610                 
49611                 
49612                 
49613             default:
49614                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49615                     
49616                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49617                     this.add(region, ret);
49618                 } else {
49619                 
49620                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49621                     return null;
49622                 }
49623                 
49624              // GridPanel (grid, cfg)
49625             
49626         }
49627         this.beginUpdate();
49628         // add children..
49629         var region = '';
49630         var abn = {};
49631         Roo.each(xitems, function(i)  {
49632             region = nb && i.region ? i.region : false;
49633             
49634             var add = ret.addxtype(i);
49635            
49636             if (region) {
49637                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49638                 if (!i.background) {
49639                     abn[region] = nb[region] ;
49640                 }
49641             }
49642             
49643         });
49644         this.endUpdate();
49645
49646         // make the last non-background panel active..
49647         //if (nb) { Roo.log(abn); }
49648         if (nb) {
49649             
49650             for(var r in abn) {
49651                 region = this.getRegion(r);
49652                 if (region) {
49653                     // tried using nb[r], but it does not work..
49654                      
49655                     region.showPanel(abn[r]);
49656                    
49657                 }
49658             }
49659         }
49660         return ret;
49661         
49662     }
49663 });
49664
49665 /**
49666  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49667  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49668  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49669  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49670  * <pre><code>
49671 // shorthand
49672 var CP = Roo.ContentPanel;
49673
49674 var layout = Roo.BorderLayout.create({
49675     north: {
49676         initialSize: 25,
49677         titlebar: false,
49678         panels: [new CP("north", "North")]
49679     },
49680     west: {
49681         split:true,
49682         initialSize: 200,
49683         minSize: 175,
49684         maxSize: 400,
49685         titlebar: true,
49686         collapsible: true,
49687         panels: [new CP("west", {title: "West"})]
49688     },
49689     east: {
49690         split:true,
49691         initialSize: 202,
49692         minSize: 175,
49693         maxSize: 400,
49694         titlebar: true,
49695         collapsible: true,
49696         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49697     },
49698     south: {
49699         split:true,
49700         initialSize: 100,
49701         minSize: 100,
49702         maxSize: 200,
49703         titlebar: true,
49704         collapsible: true,
49705         panels: [new CP("south", {title: "South", closable: true})]
49706     },
49707     center: {
49708         titlebar: true,
49709         autoScroll:true,
49710         resizeTabs: true,
49711         minTabWidth: 50,
49712         preferredTabWidth: 150,
49713         panels: [
49714             new CP("center1", {title: "Close Me", closable: true}),
49715             new CP("center2", {title: "Center Panel", closable: false})
49716         ]
49717     }
49718 }, document.body);
49719
49720 layout.getRegion("center").showPanel("center1");
49721 </code></pre>
49722  * @param config
49723  * @param targetEl
49724  */
49725 Roo.BorderLayout.create = function(config, targetEl){
49726     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49727     layout.beginUpdate();
49728     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49729     for(var j = 0, jlen = regions.length; j < jlen; j++){
49730         var lr = regions[j];
49731         if(layout.regions[lr] && config[lr].panels){
49732             var r = layout.regions[lr];
49733             var ps = config[lr].panels;
49734             layout.addTypedPanels(r, ps);
49735         }
49736     }
49737     layout.endUpdate();
49738     return layout;
49739 };
49740
49741 // private
49742 Roo.BorderLayout.RegionFactory = {
49743     // private
49744     validRegions : ["north","south","east","west","center"],
49745
49746     // private
49747     create : function(target, mgr, config){
49748         target = target.toLowerCase();
49749         if(config.lightweight || config.basic){
49750             return new Roo.BasicLayoutRegion(mgr, config, target);
49751         }
49752         switch(target){
49753             case "north":
49754                 return new Roo.NorthLayoutRegion(mgr, config);
49755             case "south":
49756                 return new Roo.SouthLayoutRegion(mgr, config);
49757             case "east":
49758                 return new Roo.EastLayoutRegion(mgr, config);
49759             case "west":
49760                 return new Roo.WestLayoutRegion(mgr, config);
49761             case "center":
49762                 return new Roo.CenterLayoutRegion(mgr, config);
49763         }
49764         throw 'Layout region "'+target+'" not supported.';
49765     }
49766 };/*
49767  * Based on:
49768  * Ext JS Library 1.1.1
49769  * Copyright(c) 2006-2007, Ext JS, LLC.
49770  *
49771  * Originally Released Under LGPL - original licence link has changed is not relivant.
49772  *
49773  * Fork - LGPL
49774  * <script type="text/javascript">
49775  */
49776  
49777 /**
49778  * @class Roo.BasicLayoutRegion
49779  * @extends Roo.util.Observable
49780  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49781  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49782  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49783  */
49784 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49785     this.mgr = mgr;
49786     this.position  = pos;
49787     this.events = {
49788         /**
49789          * @scope Roo.BasicLayoutRegion
49790          */
49791         
49792         /**
49793          * @event beforeremove
49794          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49795          * @param {Roo.LayoutRegion} this
49796          * @param {Roo.ContentPanel} panel The panel
49797          * @param {Object} e The cancel event object
49798          */
49799         "beforeremove" : true,
49800         /**
49801          * @event invalidated
49802          * Fires when the layout for this region is changed.
49803          * @param {Roo.LayoutRegion} this
49804          */
49805         "invalidated" : true,
49806         /**
49807          * @event visibilitychange
49808          * Fires when this region is shown or hidden 
49809          * @param {Roo.LayoutRegion} this
49810          * @param {Boolean} visibility true or false
49811          */
49812         "visibilitychange" : true,
49813         /**
49814          * @event paneladded
49815          * Fires when a panel is added. 
49816          * @param {Roo.LayoutRegion} this
49817          * @param {Roo.ContentPanel} panel The panel
49818          */
49819         "paneladded" : true,
49820         /**
49821          * @event panelremoved
49822          * Fires when a panel is removed. 
49823          * @param {Roo.LayoutRegion} this
49824          * @param {Roo.ContentPanel} panel The panel
49825          */
49826         "panelremoved" : true,
49827         /**
49828          * @event collapsed
49829          * Fires when this region is collapsed.
49830          * @param {Roo.LayoutRegion} this
49831          */
49832         "collapsed" : true,
49833         /**
49834          * @event expanded
49835          * Fires when this region is expanded.
49836          * @param {Roo.LayoutRegion} this
49837          */
49838         "expanded" : true,
49839         /**
49840          * @event slideshow
49841          * Fires when this region is slid into view.
49842          * @param {Roo.LayoutRegion} this
49843          */
49844         "slideshow" : true,
49845         /**
49846          * @event slidehide
49847          * Fires when this region slides out of view. 
49848          * @param {Roo.LayoutRegion} this
49849          */
49850         "slidehide" : true,
49851         /**
49852          * @event panelactivated
49853          * Fires when a panel is activated. 
49854          * @param {Roo.LayoutRegion} this
49855          * @param {Roo.ContentPanel} panel The activated panel
49856          */
49857         "panelactivated" : true,
49858         /**
49859          * @event resized
49860          * Fires when the user resizes this region. 
49861          * @param {Roo.LayoutRegion} this
49862          * @param {Number} newSize The new size (width for east/west, height for north/south)
49863          */
49864         "resized" : true
49865     };
49866     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49867     this.panels = new Roo.util.MixedCollection();
49868     this.panels.getKey = this.getPanelId.createDelegate(this);
49869     this.box = null;
49870     this.activePanel = null;
49871     // ensure listeners are added...
49872     
49873     if (config.listeners || config.events) {
49874         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49875             listeners : config.listeners || {},
49876             events : config.events || {}
49877         });
49878     }
49879     
49880     if(skipConfig !== true){
49881         this.applyConfig(config);
49882     }
49883 };
49884
49885 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49886     getPanelId : function(p){
49887         return p.getId();
49888     },
49889     
49890     applyConfig : function(config){
49891         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49892         this.config = config;
49893         
49894     },
49895     
49896     /**
49897      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49898      * the width, for horizontal (north, south) the height.
49899      * @param {Number} newSize The new width or height
49900      */
49901     resizeTo : function(newSize){
49902         var el = this.el ? this.el :
49903                  (this.activePanel ? this.activePanel.getEl() : null);
49904         if(el){
49905             switch(this.position){
49906                 case "east":
49907                 case "west":
49908                     el.setWidth(newSize);
49909                     this.fireEvent("resized", this, newSize);
49910                 break;
49911                 case "north":
49912                 case "south":
49913                     el.setHeight(newSize);
49914                     this.fireEvent("resized", this, newSize);
49915                 break;                
49916             }
49917         }
49918     },
49919     
49920     getBox : function(){
49921         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49922     },
49923     
49924     getMargins : function(){
49925         return this.margins;
49926     },
49927     
49928     updateBox : function(box){
49929         this.box = box;
49930         var el = this.activePanel.getEl();
49931         el.dom.style.left = box.x + "px";
49932         el.dom.style.top = box.y + "px";
49933         this.activePanel.setSize(box.width, box.height);
49934     },
49935     
49936     /**
49937      * Returns the container element for this region.
49938      * @return {Roo.Element}
49939      */
49940     getEl : function(){
49941         return this.activePanel;
49942     },
49943     
49944     /**
49945      * Returns true if this region is currently visible.
49946      * @return {Boolean}
49947      */
49948     isVisible : function(){
49949         return this.activePanel ? true : false;
49950     },
49951     
49952     setActivePanel : function(panel){
49953         panel = this.getPanel(panel);
49954         if(this.activePanel && this.activePanel != panel){
49955             this.activePanel.setActiveState(false);
49956             this.activePanel.getEl().setLeftTop(-10000,-10000);
49957         }
49958         this.activePanel = panel;
49959         panel.setActiveState(true);
49960         if(this.box){
49961             panel.setSize(this.box.width, this.box.height);
49962         }
49963         this.fireEvent("panelactivated", this, panel);
49964         this.fireEvent("invalidated");
49965     },
49966     
49967     /**
49968      * Show the specified panel.
49969      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49970      * @return {Roo.ContentPanel} The shown panel or null
49971      */
49972     showPanel : function(panel){
49973         if(panel = this.getPanel(panel)){
49974             this.setActivePanel(panel);
49975         }
49976         return panel;
49977     },
49978     
49979     /**
49980      * Get the active panel for this region.
49981      * @return {Roo.ContentPanel} The active panel or null
49982      */
49983     getActivePanel : function(){
49984         return this.activePanel;
49985     },
49986     
49987     /**
49988      * Add the passed ContentPanel(s)
49989      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49990      * @return {Roo.ContentPanel} The panel added (if only one was added)
49991      */
49992     add : function(panel){
49993         if(arguments.length > 1){
49994             for(var i = 0, len = arguments.length; i < len; i++) {
49995                 this.add(arguments[i]);
49996             }
49997             return null;
49998         }
49999         if(this.hasPanel(panel)){
50000             this.showPanel(panel);
50001             return panel;
50002         }
50003         var el = panel.getEl();
50004         if(el.dom.parentNode != this.mgr.el.dom){
50005             this.mgr.el.dom.appendChild(el.dom);
50006         }
50007         if(panel.setRegion){
50008             panel.setRegion(this);
50009         }
50010         this.panels.add(panel);
50011         el.setStyle("position", "absolute");
50012         if(!panel.background){
50013             this.setActivePanel(panel);
50014             if(this.config.initialSize && this.panels.getCount()==1){
50015                 this.resizeTo(this.config.initialSize);
50016             }
50017         }
50018         this.fireEvent("paneladded", this, panel);
50019         return panel;
50020     },
50021     
50022     /**
50023      * Returns true if the panel is in this region.
50024      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50025      * @return {Boolean}
50026      */
50027     hasPanel : function(panel){
50028         if(typeof panel == "object"){ // must be panel obj
50029             panel = panel.getId();
50030         }
50031         return this.getPanel(panel) ? true : false;
50032     },
50033     
50034     /**
50035      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50036      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50037      * @param {Boolean} preservePanel Overrides the config preservePanel option
50038      * @return {Roo.ContentPanel} The panel that was removed
50039      */
50040     remove : function(panel, preservePanel){
50041         panel = this.getPanel(panel);
50042         if(!panel){
50043             return null;
50044         }
50045         var e = {};
50046         this.fireEvent("beforeremove", this, panel, e);
50047         if(e.cancel === true){
50048             return null;
50049         }
50050         var panelId = panel.getId();
50051         this.panels.removeKey(panelId);
50052         return panel;
50053     },
50054     
50055     /**
50056      * Returns the panel specified or null if it's not in this region.
50057      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50058      * @return {Roo.ContentPanel}
50059      */
50060     getPanel : function(id){
50061         if(typeof id == "object"){ // must be panel obj
50062             return id;
50063         }
50064         return this.panels.get(id);
50065     },
50066     
50067     /**
50068      * Returns this regions position (north/south/east/west/center).
50069      * @return {String} 
50070      */
50071     getPosition: function(){
50072         return this.position;    
50073     }
50074 });/*
50075  * Based on:
50076  * Ext JS Library 1.1.1
50077  * Copyright(c) 2006-2007, Ext JS, LLC.
50078  *
50079  * Originally Released Under LGPL - original licence link has changed is not relivant.
50080  *
50081  * Fork - LGPL
50082  * <script type="text/javascript">
50083  */
50084  
50085 /**
50086  * @class Roo.LayoutRegion
50087  * @extends Roo.BasicLayoutRegion
50088  * This class represents a region in a layout manager.
50089  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50090  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50091  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50092  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50093  * @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})
50094  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50095  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50096  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50097  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50098  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50099  * @cfg {String}    title           The title for the region (overrides panel titles)
50100  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50101  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50102  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50103  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50104  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50105  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50106  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50107  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50108  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50109  * @cfg {Boolean}   showPin         True to show a pin button
50110  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50111  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50112  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50113  * @cfg {Number}    width           For East/West panels
50114  * @cfg {Number}    height          For North/South panels
50115  * @cfg {Boolean}   split           To show the splitter
50116  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50117  */
50118 Roo.LayoutRegion = function(mgr, config, pos){
50119     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50120     var dh = Roo.DomHelper;
50121     /** This region's container element 
50122     * @type Roo.Element */
50123     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50124     /** This region's title element 
50125     * @type Roo.Element */
50126
50127     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50128         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50129         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50130     ]}, true);
50131     this.titleEl.enableDisplayMode();
50132     /** This region's title text element 
50133     * @type HTMLElement */
50134     this.titleTextEl = this.titleEl.dom.firstChild;
50135     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50136     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50137     this.closeBtn.enableDisplayMode();
50138     this.closeBtn.on("click", this.closeClicked, this);
50139     this.closeBtn.hide();
50140
50141     this.createBody(config);
50142     this.visible = true;
50143     this.collapsed = false;
50144
50145     if(config.hideWhenEmpty){
50146         this.hide();
50147         this.on("paneladded", this.validateVisibility, this);
50148         this.on("panelremoved", this.validateVisibility, this);
50149     }
50150     this.applyConfig(config);
50151 };
50152
50153 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50154
50155     createBody : function(){
50156         /** This region's body element 
50157         * @type Roo.Element */
50158         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50159     },
50160
50161     applyConfig : function(c){
50162         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50163             var dh = Roo.DomHelper;
50164             if(c.titlebar !== false){
50165                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50166                 this.collapseBtn.on("click", this.collapse, this);
50167                 this.collapseBtn.enableDisplayMode();
50168
50169                 if(c.showPin === true || this.showPin){
50170                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50171                     this.stickBtn.enableDisplayMode();
50172                     this.stickBtn.on("click", this.expand, this);
50173                     this.stickBtn.hide();
50174                 }
50175             }
50176             /** This region's collapsed element
50177             * @type Roo.Element */
50178             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50179                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50180             ]}, true);
50181             if(c.floatable !== false){
50182                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50183                this.collapsedEl.on("click", this.collapseClick, this);
50184             }
50185
50186             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50187                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50188                    id: "message", unselectable: "on", style:{"float":"left"}});
50189                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50190              }
50191             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50192             this.expandBtn.on("click", this.expand, this);
50193         }
50194         if(this.collapseBtn){
50195             this.collapseBtn.setVisible(c.collapsible == true);
50196         }
50197         this.cmargins = c.cmargins || this.cmargins ||
50198                          (this.position == "west" || this.position == "east" ?
50199                              {top: 0, left: 2, right:2, bottom: 0} :
50200                              {top: 2, left: 0, right:0, bottom: 2});
50201         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50202         this.bottomTabs = c.tabPosition != "top";
50203         this.autoScroll = c.autoScroll || false;
50204         if(this.autoScroll){
50205             this.bodyEl.setStyle("overflow", "auto");
50206         }else{
50207             this.bodyEl.setStyle("overflow", "hidden");
50208         }
50209         //if(c.titlebar !== false){
50210             if((!c.titlebar && !c.title) || c.titlebar === false){
50211                 this.titleEl.hide();
50212             }else{
50213                 this.titleEl.show();
50214                 if(c.title){
50215                     this.titleTextEl.innerHTML = c.title;
50216                 }
50217             }
50218         //}
50219         this.duration = c.duration || .30;
50220         this.slideDuration = c.slideDuration || .45;
50221         this.config = c;
50222         if(c.collapsed){
50223             this.collapse(true);
50224         }
50225         if(c.hidden){
50226             this.hide();
50227         }
50228     },
50229     /**
50230      * Returns true if this region is currently visible.
50231      * @return {Boolean}
50232      */
50233     isVisible : function(){
50234         return this.visible;
50235     },
50236
50237     /**
50238      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50239      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50240      */
50241     setCollapsedTitle : function(title){
50242         title = title || "&#160;";
50243         if(this.collapsedTitleTextEl){
50244             this.collapsedTitleTextEl.innerHTML = title;
50245         }
50246     },
50247
50248     getBox : function(){
50249         var b;
50250         if(!this.collapsed){
50251             b = this.el.getBox(false, true);
50252         }else{
50253             b = this.collapsedEl.getBox(false, true);
50254         }
50255         return b;
50256     },
50257
50258     getMargins : function(){
50259         return this.collapsed ? this.cmargins : this.margins;
50260     },
50261
50262     highlight : function(){
50263         this.el.addClass("x-layout-panel-dragover");
50264     },
50265
50266     unhighlight : function(){
50267         this.el.removeClass("x-layout-panel-dragover");
50268     },
50269
50270     updateBox : function(box){
50271         this.box = box;
50272         if(!this.collapsed){
50273             this.el.dom.style.left = box.x + "px";
50274             this.el.dom.style.top = box.y + "px";
50275             this.updateBody(box.width, box.height);
50276         }else{
50277             this.collapsedEl.dom.style.left = box.x + "px";
50278             this.collapsedEl.dom.style.top = box.y + "px";
50279             this.collapsedEl.setSize(box.width, box.height);
50280         }
50281         if(this.tabs){
50282             this.tabs.autoSizeTabs();
50283         }
50284     },
50285
50286     updateBody : function(w, h){
50287         if(w !== null){
50288             this.el.setWidth(w);
50289             w -= this.el.getBorderWidth("rl");
50290             if(this.config.adjustments){
50291                 w += this.config.adjustments[0];
50292             }
50293         }
50294         if(h !== null){
50295             this.el.setHeight(h);
50296             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50297             h -= this.el.getBorderWidth("tb");
50298             if(this.config.adjustments){
50299                 h += this.config.adjustments[1];
50300             }
50301             this.bodyEl.setHeight(h);
50302             if(this.tabs){
50303                 h = this.tabs.syncHeight(h);
50304             }
50305         }
50306         if(this.panelSize){
50307             w = w !== null ? w : this.panelSize.width;
50308             h = h !== null ? h : this.panelSize.height;
50309         }
50310         if(this.activePanel){
50311             var el = this.activePanel.getEl();
50312             w = w !== null ? w : el.getWidth();
50313             h = h !== null ? h : el.getHeight();
50314             this.panelSize = {width: w, height: h};
50315             this.activePanel.setSize(w, h);
50316         }
50317         if(Roo.isIE && this.tabs){
50318             this.tabs.el.repaint();
50319         }
50320     },
50321
50322     /**
50323      * Returns the container element for this region.
50324      * @return {Roo.Element}
50325      */
50326     getEl : function(){
50327         return this.el;
50328     },
50329
50330     /**
50331      * Hides this region.
50332      */
50333     hide : function(){
50334         if(!this.collapsed){
50335             this.el.dom.style.left = "-2000px";
50336             this.el.hide();
50337         }else{
50338             this.collapsedEl.dom.style.left = "-2000px";
50339             this.collapsedEl.hide();
50340         }
50341         this.visible = false;
50342         this.fireEvent("visibilitychange", this, false);
50343     },
50344
50345     /**
50346      * Shows this region if it was previously hidden.
50347      */
50348     show : function(){
50349         if(!this.collapsed){
50350             this.el.show();
50351         }else{
50352             this.collapsedEl.show();
50353         }
50354         this.visible = true;
50355         this.fireEvent("visibilitychange", this, true);
50356     },
50357
50358     closeClicked : function(){
50359         if(this.activePanel){
50360             this.remove(this.activePanel);
50361         }
50362     },
50363
50364     collapseClick : function(e){
50365         if(this.isSlid){
50366            e.stopPropagation();
50367            this.slideIn();
50368         }else{
50369            e.stopPropagation();
50370            this.slideOut();
50371         }
50372     },
50373
50374     /**
50375      * Collapses this region.
50376      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50377      */
50378     collapse : function(skipAnim){
50379         if(this.collapsed) return;
50380         this.collapsed = true;
50381         if(this.split){
50382             this.split.el.hide();
50383         }
50384         if(this.config.animate && skipAnim !== true){
50385             this.fireEvent("invalidated", this);
50386             this.animateCollapse();
50387         }else{
50388             this.el.setLocation(-20000,-20000);
50389             this.el.hide();
50390             this.collapsedEl.show();
50391             this.fireEvent("collapsed", this);
50392             this.fireEvent("invalidated", this);
50393         }
50394     },
50395
50396     animateCollapse : function(){
50397         // overridden
50398     },
50399
50400     /**
50401      * Expands this region if it was previously collapsed.
50402      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50403      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50404      */
50405     expand : function(e, skipAnim){
50406         if(e) e.stopPropagation();
50407         if(!this.collapsed || this.el.hasActiveFx()) return;
50408         if(this.isSlid){
50409             this.afterSlideIn();
50410             skipAnim = true;
50411         }
50412         this.collapsed = false;
50413         if(this.config.animate && skipAnim !== true){
50414             this.animateExpand();
50415         }else{
50416             this.el.show();
50417             if(this.split){
50418                 this.split.el.show();
50419             }
50420             this.collapsedEl.setLocation(-2000,-2000);
50421             this.collapsedEl.hide();
50422             this.fireEvent("invalidated", this);
50423             this.fireEvent("expanded", this);
50424         }
50425     },
50426
50427     animateExpand : function(){
50428         // overridden
50429     },
50430
50431     initTabs : function()
50432     {
50433         this.bodyEl.setStyle("overflow", "hidden");
50434         var ts = new Roo.TabPanel(
50435                 this.bodyEl.dom,
50436                 {
50437                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50438                     disableTooltips: this.config.disableTabTips,
50439                     toolbar : this.config.toolbar
50440                 }
50441         );
50442         if(this.config.hideTabs){
50443             ts.stripWrap.setDisplayed(false);
50444         }
50445         this.tabs = ts;
50446         ts.resizeTabs = this.config.resizeTabs === true;
50447         ts.minTabWidth = this.config.minTabWidth || 40;
50448         ts.maxTabWidth = this.config.maxTabWidth || 250;
50449         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50450         ts.monitorResize = false;
50451         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50452         ts.bodyEl.addClass('x-layout-tabs-body');
50453         this.panels.each(this.initPanelAsTab, this);
50454     },
50455
50456     initPanelAsTab : function(panel){
50457         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50458                     this.config.closeOnTab && panel.isClosable());
50459         if(panel.tabTip !== undefined){
50460             ti.setTooltip(panel.tabTip);
50461         }
50462         ti.on("activate", function(){
50463               this.setActivePanel(panel);
50464         }, this);
50465         if(this.config.closeOnTab){
50466             ti.on("beforeclose", function(t, e){
50467                 e.cancel = true;
50468                 this.remove(panel);
50469             }, this);
50470         }
50471         return ti;
50472     },
50473
50474     updatePanelTitle : function(panel, title){
50475         if(this.activePanel == panel){
50476             this.updateTitle(title);
50477         }
50478         if(this.tabs){
50479             var ti = this.tabs.getTab(panel.getEl().id);
50480             ti.setText(title);
50481             if(panel.tabTip !== undefined){
50482                 ti.setTooltip(panel.tabTip);
50483             }
50484         }
50485     },
50486
50487     updateTitle : function(title){
50488         if(this.titleTextEl && !this.config.title){
50489             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50490         }
50491     },
50492
50493     setActivePanel : function(panel){
50494         panel = this.getPanel(panel);
50495         if(this.activePanel && this.activePanel != panel){
50496             this.activePanel.setActiveState(false);
50497         }
50498         this.activePanel = panel;
50499         panel.setActiveState(true);
50500         if(this.panelSize){
50501             panel.setSize(this.panelSize.width, this.panelSize.height);
50502         }
50503         if(this.closeBtn){
50504             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50505         }
50506         this.updateTitle(panel.getTitle());
50507         if(this.tabs){
50508             this.fireEvent("invalidated", this);
50509         }
50510         this.fireEvent("panelactivated", this, panel);
50511     },
50512
50513     /**
50514      * Shows the specified panel.
50515      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50516      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50517      */
50518     showPanel : function(panel)
50519     {
50520         panel = this.getPanel(panel);
50521         if(panel){
50522             if(this.tabs){
50523                 var tab = this.tabs.getTab(panel.getEl().id);
50524                 if(tab.isHidden()){
50525                     this.tabs.unhideTab(tab.id);
50526                 }
50527                 tab.activate();
50528             }else{
50529                 this.setActivePanel(panel);
50530             }
50531         }
50532         return panel;
50533     },
50534
50535     /**
50536      * Get the active panel for this region.
50537      * @return {Roo.ContentPanel} The active panel or null
50538      */
50539     getActivePanel : function(){
50540         return this.activePanel;
50541     },
50542
50543     validateVisibility : function(){
50544         if(this.panels.getCount() < 1){
50545             this.updateTitle("&#160;");
50546             this.closeBtn.hide();
50547             this.hide();
50548         }else{
50549             if(!this.isVisible()){
50550                 this.show();
50551             }
50552         }
50553     },
50554
50555     /**
50556      * Adds the passed ContentPanel(s) to this region.
50557      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50558      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50559      */
50560     add : function(panel){
50561         if(arguments.length > 1){
50562             for(var i = 0, len = arguments.length; i < len; i++) {
50563                 this.add(arguments[i]);
50564             }
50565             return null;
50566         }
50567         if(this.hasPanel(panel)){
50568             this.showPanel(panel);
50569             return panel;
50570         }
50571         panel.setRegion(this);
50572         this.panels.add(panel);
50573         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50574             this.bodyEl.dom.appendChild(panel.getEl().dom);
50575             if(panel.background !== true){
50576                 this.setActivePanel(panel);
50577             }
50578             this.fireEvent("paneladded", this, panel);
50579             return panel;
50580         }
50581         if(!this.tabs){
50582             this.initTabs();
50583         }else{
50584             this.initPanelAsTab(panel);
50585         }
50586         if(panel.background !== true){
50587             this.tabs.activate(panel.getEl().id);
50588         }
50589         this.fireEvent("paneladded", this, panel);
50590         return panel;
50591     },
50592
50593     /**
50594      * Hides the tab for the specified panel.
50595      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50596      */
50597     hidePanel : function(panel){
50598         if(this.tabs && (panel = this.getPanel(panel))){
50599             this.tabs.hideTab(panel.getEl().id);
50600         }
50601     },
50602
50603     /**
50604      * Unhides the tab for a previously hidden panel.
50605      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50606      */
50607     unhidePanel : function(panel){
50608         if(this.tabs && (panel = this.getPanel(panel))){
50609             this.tabs.unhideTab(panel.getEl().id);
50610         }
50611     },
50612
50613     clearPanels : function(){
50614         while(this.panels.getCount() > 0){
50615              this.remove(this.panels.first());
50616         }
50617     },
50618
50619     /**
50620      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50621      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50622      * @param {Boolean} preservePanel Overrides the config preservePanel option
50623      * @return {Roo.ContentPanel} The panel that was removed
50624      */
50625     remove : function(panel, preservePanel){
50626         panel = this.getPanel(panel);
50627         if(!panel){
50628             return null;
50629         }
50630         var e = {};
50631         this.fireEvent("beforeremove", this, panel, e);
50632         if(e.cancel === true){
50633             return null;
50634         }
50635         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50636         var panelId = panel.getId();
50637         this.panels.removeKey(panelId);
50638         if(preservePanel){
50639             document.body.appendChild(panel.getEl().dom);
50640         }
50641         if(this.tabs){
50642             this.tabs.removeTab(panel.getEl().id);
50643         }else if (!preservePanel){
50644             this.bodyEl.dom.removeChild(panel.getEl().dom);
50645         }
50646         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50647             var p = this.panels.first();
50648             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50649             tempEl.appendChild(p.getEl().dom);
50650             this.bodyEl.update("");
50651             this.bodyEl.dom.appendChild(p.getEl().dom);
50652             tempEl = null;
50653             this.updateTitle(p.getTitle());
50654             this.tabs = null;
50655             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50656             this.setActivePanel(p);
50657         }
50658         panel.setRegion(null);
50659         if(this.activePanel == panel){
50660             this.activePanel = null;
50661         }
50662         if(this.config.autoDestroy !== false && preservePanel !== true){
50663             try{panel.destroy();}catch(e){}
50664         }
50665         this.fireEvent("panelremoved", this, panel);
50666         return panel;
50667     },
50668
50669     /**
50670      * Returns the TabPanel component used by this region
50671      * @return {Roo.TabPanel}
50672      */
50673     getTabs : function(){
50674         return this.tabs;
50675     },
50676
50677     createTool : function(parentEl, className){
50678         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50679             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50680         btn.addClassOnOver("x-layout-tools-button-over");
50681         return btn;
50682     }
50683 });/*
50684  * Based on:
50685  * Ext JS Library 1.1.1
50686  * Copyright(c) 2006-2007, Ext JS, LLC.
50687  *
50688  * Originally Released Under LGPL - original licence link has changed is not relivant.
50689  *
50690  * Fork - LGPL
50691  * <script type="text/javascript">
50692  */
50693  
50694
50695
50696 /**
50697  * @class Roo.SplitLayoutRegion
50698  * @extends Roo.LayoutRegion
50699  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50700  */
50701 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50702     this.cursor = cursor;
50703     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50704 };
50705
50706 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50707     splitTip : "Drag to resize.",
50708     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50709     useSplitTips : false,
50710
50711     applyConfig : function(config){
50712         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50713         if(config.split){
50714             if(!this.split){
50715                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50716                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50717                 /** The SplitBar for this region 
50718                 * @type Roo.SplitBar */
50719                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50720                 this.split.on("moved", this.onSplitMove, this);
50721                 this.split.useShim = config.useShim === true;
50722                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50723                 if(this.useSplitTips){
50724                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50725                 }
50726                 if(config.collapsible){
50727                     this.split.el.on("dblclick", this.collapse,  this);
50728                 }
50729             }
50730             if(typeof config.minSize != "undefined"){
50731                 this.split.minSize = config.minSize;
50732             }
50733             if(typeof config.maxSize != "undefined"){
50734                 this.split.maxSize = config.maxSize;
50735             }
50736             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50737                 this.hideSplitter();
50738             }
50739         }
50740     },
50741
50742     getHMaxSize : function(){
50743          var cmax = this.config.maxSize || 10000;
50744          var center = this.mgr.getRegion("center");
50745          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50746     },
50747
50748     getVMaxSize : function(){
50749          var cmax = this.config.maxSize || 10000;
50750          var center = this.mgr.getRegion("center");
50751          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50752     },
50753
50754     onSplitMove : function(split, newSize){
50755         this.fireEvent("resized", this, newSize);
50756     },
50757     
50758     /** 
50759      * Returns the {@link Roo.SplitBar} for this region.
50760      * @return {Roo.SplitBar}
50761      */
50762     getSplitBar : function(){
50763         return this.split;
50764     },
50765     
50766     hide : function(){
50767         this.hideSplitter();
50768         Roo.SplitLayoutRegion.superclass.hide.call(this);
50769     },
50770
50771     hideSplitter : function(){
50772         if(this.split){
50773             this.split.el.setLocation(-2000,-2000);
50774             this.split.el.hide();
50775         }
50776     },
50777
50778     show : function(){
50779         if(this.split){
50780             this.split.el.show();
50781         }
50782         Roo.SplitLayoutRegion.superclass.show.call(this);
50783     },
50784     
50785     beforeSlide: function(){
50786         if(Roo.isGecko){// firefox overflow auto bug workaround
50787             this.bodyEl.clip();
50788             if(this.tabs) this.tabs.bodyEl.clip();
50789             if(this.activePanel){
50790                 this.activePanel.getEl().clip();
50791                 
50792                 if(this.activePanel.beforeSlide){
50793                     this.activePanel.beforeSlide();
50794                 }
50795             }
50796         }
50797     },
50798     
50799     afterSlide : function(){
50800         if(Roo.isGecko){// firefox overflow auto bug workaround
50801             this.bodyEl.unclip();
50802             if(this.tabs) this.tabs.bodyEl.unclip();
50803             if(this.activePanel){
50804                 this.activePanel.getEl().unclip();
50805                 if(this.activePanel.afterSlide){
50806                     this.activePanel.afterSlide();
50807                 }
50808             }
50809         }
50810     },
50811
50812     initAutoHide : function(){
50813         if(this.autoHide !== false){
50814             if(!this.autoHideHd){
50815                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50816                 this.autoHideHd = {
50817                     "mouseout": function(e){
50818                         if(!e.within(this.el, true)){
50819                             st.delay(500);
50820                         }
50821                     },
50822                     "mouseover" : function(e){
50823                         st.cancel();
50824                     },
50825                     scope : this
50826                 };
50827             }
50828             this.el.on(this.autoHideHd);
50829         }
50830     },
50831
50832     clearAutoHide : function(){
50833         if(this.autoHide !== false){
50834             this.el.un("mouseout", this.autoHideHd.mouseout);
50835             this.el.un("mouseover", this.autoHideHd.mouseover);
50836         }
50837     },
50838
50839     clearMonitor : function(){
50840         Roo.get(document).un("click", this.slideInIf, this);
50841     },
50842
50843     // these names are backwards but not changed for compat
50844     slideOut : function(){
50845         if(this.isSlid || this.el.hasActiveFx()){
50846             return;
50847         }
50848         this.isSlid = true;
50849         if(this.collapseBtn){
50850             this.collapseBtn.hide();
50851         }
50852         this.closeBtnState = this.closeBtn.getStyle('display');
50853         this.closeBtn.hide();
50854         if(this.stickBtn){
50855             this.stickBtn.show();
50856         }
50857         this.el.show();
50858         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50859         this.beforeSlide();
50860         this.el.setStyle("z-index", 10001);
50861         this.el.slideIn(this.getSlideAnchor(), {
50862             callback: function(){
50863                 this.afterSlide();
50864                 this.initAutoHide();
50865                 Roo.get(document).on("click", this.slideInIf, this);
50866                 this.fireEvent("slideshow", this);
50867             },
50868             scope: this,
50869             block: true
50870         });
50871     },
50872
50873     afterSlideIn : function(){
50874         this.clearAutoHide();
50875         this.isSlid = false;
50876         this.clearMonitor();
50877         this.el.setStyle("z-index", "");
50878         if(this.collapseBtn){
50879             this.collapseBtn.show();
50880         }
50881         this.closeBtn.setStyle('display', this.closeBtnState);
50882         if(this.stickBtn){
50883             this.stickBtn.hide();
50884         }
50885         this.fireEvent("slidehide", this);
50886     },
50887
50888     slideIn : function(cb){
50889         if(!this.isSlid || this.el.hasActiveFx()){
50890             Roo.callback(cb);
50891             return;
50892         }
50893         this.isSlid = false;
50894         this.beforeSlide();
50895         this.el.slideOut(this.getSlideAnchor(), {
50896             callback: function(){
50897                 this.el.setLeftTop(-10000, -10000);
50898                 this.afterSlide();
50899                 this.afterSlideIn();
50900                 Roo.callback(cb);
50901             },
50902             scope: this,
50903             block: true
50904         });
50905     },
50906     
50907     slideInIf : function(e){
50908         if(!e.within(this.el)){
50909             this.slideIn();
50910         }
50911     },
50912
50913     animateCollapse : function(){
50914         this.beforeSlide();
50915         this.el.setStyle("z-index", 20000);
50916         var anchor = this.getSlideAnchor();
50917         this.el.slideOut(anchor, {
50918             callback : function(){
50919                 this.el.setStyle("z-index", "");
50920                 this.collapsedEl.slideIn(anchor, {duration:.3});
50921                 this.afterSlide();
50922                 this.el.setLocation(-10000,-10000);
50923                 this.el.hide();
50924                 this.fireEvent("collapsed", this);
50925             },
50926             scope: this,
50927             block: true
50928         });
50929     },
50930
50931     animateExpand : function(){
50932         this.beforeSlide();
50933         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50934         this.el.setStyle("z-index", 20000);
50935         this.collapsedEl.hide({
50936             duration:.1
50937         });
50938         this.el.slideIn(this.getSlideAnchor(), {
50939             callback : function(){
50940                 this.el.setStyle("z-index", "");
50941                 this.afterSlide();
50942                 if(this.split){
50943                     this.split.el.show();
50944                 }
50945                 this.fireEvent("invalidated", this);
50946                 this.fireEvent("expanded", this);
50947             },
50948             scope: this,
50949             block: true
50950         });
50951     },
50952
50953     anchors : {
50954         "west" : "left",
50955         "east" : "right",
50956         "north" : "top",
50957         "south" : "bottom"
50958     },
50959
50960     sanchors : {
50961         "west" : "l",
50962         "east" : "r",
50963         "north" : "t",
50964         "south" : "b"
50965     },
50966
50967     canchors : {
50968         "west" : "tl-tr",
50969         "east" : "tr-tl",
50970         "north" : "tl-bl",
50971         "south" : "bl-tl"
50972     },
50973
50974     getAnchor : function(){
50975         return this.anchors[this.position];
50976     },
50977
50978     getCollapseAnchor : function(){
50979         return this.canchors[this.position];
50980     },
50981
50982     getSlideAnchor : function(){
50983         return this.sanchors[this.position];
50984     },
50985
50986     getAlignAdj : function(){
50987         var cm = this.cmargins;
50988         switch(this.position){
50989             case "west":
50990                 return [0, 0];
50991             break;
50992             case "east":
50993                 return [0, 0];
50994             break;
50995             case "north":
50996                 return [0, 0];
50997             break;
50998             case "south":
50999                 return [0, 0];
51000             break;
51001         }
51002     },
51003
51004     getExpandAdj : function(){
51005         var c = this.collapsedEl, cm = this.cmargins;
51006         switch(this.position){
51007             case "west":
51008                 return [-(cm.right+c.getWidth()+cm.left), 0];
51009             break;
51010             case "east":
51011                 return [cm.right+c.getWidth()+cm.left, 0];
51012             break;
51013             case "north":
51014                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51015             break;
51016             case "south":
51017                 return [0, cm.top+cm.bottom+c.getHeight()];
51018             break;
51019         }
51020     }
51021 });/*
51022  * Based on:
51023  * Ext JS Library 1.1.1
51024  * Copyright(c) 2006-2007, Ext JS, LLC.
51025  *
51026  * Originally Released Under LGPL - original licence link has changed is not relivant.
51027  *
51028  * Fork - LGPL
51029  * <script type="text/javascript">
51030  */
51031 /*
51032  * These classes are private internal classes
51033  */
51034 Roo.CenterLayoutRegion = function(mgr, config){
51035     Roo.LayoutRegion.call(this, mgr, config, "center");
51036     this.visible = true;
51037     this.minWidth = config.minWidth || 20;
51038     this.minHeight = config.minHeight || 20;
51039 };
51040
51041 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51042     hide : function(){
51043         // center panel can't be hidden
51044     },
51045     
51046     show : function(){
51047         // center panel can't be hidden
51048     },
51049     
51050     getMinWidth: function(){
51051         return this.minWidth;
51052     },
51053     
51054     getMinHeight: function(){
51055         return this.minHeight;
51056     }
51057 });
51058
51059
51060 Roo.NorthLayoutRegion = function(mgr, config){
51061     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51062     if(this.split){
51063         this.split.placement = Roo.SplitBar.TOP;
51064         this.split.orientation = Roo.SplitBar.VERTICAL;
51065         this.split.el.addClass("x-layout-split-v");
51066     }
51067     var size = config.initialSize || config.height;
51068     if(typeof size != "undefined"){
51069         this.el.setHeight(size);
51070     }
51071 };
51072 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51073     orientation: Roo.SplitBar.VERTICAL,
51074     getBox : function(){
51075         if(this.collapsed){
51076             return this.collapsedEl.getBox();
51077         }
51078         var box = this.el.getBox();
51079         if(this.split){
51080             box.height += this.split.el.getHeight();
51081         }
51082         return box;
51083     },
51084     
51085     updateBox : function(box){
51086         if(this.split && !this.collapsed){
51087             box.height -= this.split.el.getHeight();
51088             this.split.el.setLeft(box.x);
51089             this.split.el.setTop(box.y+box.height);
51090             this.split.el.setWidth(box.width);
51091         }
51092         if(this.collapsed){
51093             this.updateBody(box.width, null);
51094         }
51095         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51096     }
51097 });
51098
51099 Roo.SouthLayoutRegion = function(mgr, config){
51100     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51101     if(this.split){
51102         this.split.placement = Roo.SplitBar.BOTTOM;
51103         this.split.orientation = Roo.SplitBar.VERTICAL;
51104         this.split.el.addClass("x-layout-split-v");
51105     }
51106     var size = config.initialSize || config.height;
51107     if(typeof size != "undefined"){
51108         this.el.setHeight(size);
51109     }
51110 };
51111 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51112     orientation: Roo.SplitBar.VERTICAL,
51113     getBox : function(){
51114         if(this.collapsed){
51115             return this.collapsedEl.getBox();
51116         }
51117         var box = this.el.getBox();
51118         if(this.split){
51119             var sh = this.split.el.getHeight();
51120             box.height += sh;
51121             box.y -= sh;
51122         }
51123         return box;
51124     },
51125     
51126     updateBox : function(box){
51127         if(this.split && !this.collapsed){
51128             var sh = this.split.el.getHeight();
51129             box.height -= sh;
51130             box.y += sh;
51131             this.split.el.setLeft(box.x);
51132             this.split.el.setTop(box.y-sh);
51133             this.split.el.setWidth(box.width);
51134         }
51135         if(this.collapsed){
51136             this.updateBody(box.width, null);
51137         }
51138         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51139     }
51140 });
51141
51142 Roo.EastLayoutRegion = function(mgr, config){
51143     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51144     if(this.split){
51145         this.split.placement = Roo.SplitBar.RIGHT;
51146         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51147         this.split.el.addClass("x-layout-split-h");
51148     }
51149     var size = config.initialSize || config.width;
51150     if(typeof size != "undefined"){
51151         this.el.setWidth(size);
51152     }
51153 };
51154 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51155     orientation: Roo.SplitBar.HORIZONTAL,
51156     getBox : function(){
51157         if(this.collapsed){
51158             return this.collapsedEl.getBox();
51159         }
51160         var box = this.el.getBox();
51161         if(this.split){
51162             var sw = this.split.el.getWidth();
51163             box.width += sw;
51164             box.x -= sw;
51165         }
51166         return box;
51167     },
51168
51169     updateBox : function(box){
51170         if(this.split && !this.collapsed){
51171             var sw = this.split.el.getWidth();
51172             box.width -= sw;
51173             this.split.el.setLeft(box.x);
51174             this.split.el.setTop(box.y);
51175             this.split.el.setHeight(box.height);
51176             box.x += sw;
51177         }
51178         if(this.collapsed){
51179             this.updateBody(null, box.height);
51180         }
51181         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51182     }
51183 });
51184
51185 Roo.WestLayoutRegion = function(mgr, config){
51186     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51187     if(this.split){
51188         this.split.placement = Roo.SplitBar.LEFT;
51189         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51190         this.split.el.addClass("x-layout-split-h");
51191     }
51192     var size = config.initialSize || config.width;
51193     if(typeof size != "undefined"){
51194         this.el.setWidth(size);
51195     }
51196 };
51197 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51198     orientation: Roo.SplitBar.HORIZONTAL,
51199     getBox : function(){
51200         if(this.collapsed){
51201             return this.collapsedEl.getBox();
51202         }
51203         var box = this.el.getBox();
51204         if(this.split){
51205             box.width += this.split.el.getWidth();
51206         }
51207         return box;
51208     },
51209     
51210     updateBox : function(box){
51211         if(this.split && !this.collapsed){
51212             var sw = this.split.el.getWidth();
51213             box.width -= sw;
51214             this.split.el.setLeft(box.x+box.width);
51215             this.split.el.setTop(box.y);
51216             this.split.el.setHeight(box.height);
51217         }
51218         if(this.collapsed){
51219             this.updateBody(null, box.height);
51220         }
51221         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51222     }
51223 });
51224 /*
51225  * Based on:
51226  * Ext JS Library 1.1.1
51227  * Copyright(c) 2006-2007, Ext JS, LLC.
51228  *
51229  * Originally Released Under LGPL - original licence link has changed is not relivant.
51230  *
51231  * Fork - LGPL
51232  * <script type="text/javascript">
51233  */
51234  
51235  
51236 /*
51237  * Private internal class for reading and applying state
51238  */
51239 Roo.LayoutStateManager = function(layout){
51240      // default empty state
51241      this.state = {
51242         north: {},
51243         south: {},
51244         east: {},
51245         west: {}       
51246     };
51247 };
51248
51249 Roo.LayoutStateManager.prototype = {
51250     init : function(layout, provider){
51251         this.provider = provider;
51252         var state = provider.get(layout.id+"-layout-state");
51253         if(state){
51254             var wasUpdating = layout.isUpdating();
51255             if(!wasUpdating){
51256                 layout.beginUpdate();
51257             }
51258             for(var key in state){
51259                 if(typeof state[key] != "function"){
51260                     var rstate = state[key];
51261                     var r = layout.getRegion(key);
51262                     if(r && rstate){
51263                         if(rstate.size){
51264                             r.resizeTo(rstate.size);
51265                         }
51266                         if(rstate.collapsed == true){
51267                             r.collapse(true);
51268                         }else{
51269                             r.expand(null, true);
51270                         }
51271                     }
51272                 }
51273             }
51274             if(!wasUpdating){
51275                 layout.endUpdate();
51276             }
51277             this.state = state; 
51278         }
51279         this.layout = layout;
51280         layout.on("regionresized", this.onRegionResized, this);
51281         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51282         layout.on("regionexpanded", this.onRegionExpanded, this);
51283     },
51284     
51285     storeState : function(){
51286         this.provider.set(this.layout.id+"-layout-state", this.state);
51287     },
51288     
51289     onRegionResized : function(region, newSize){
51290         this.state[region.getPosition()].size = newSize;
51291         this.storeState();
51292     },
51293     
51294     onRegionCollapsed : function(region){
51295         this.state[region.getPosition()].collapsed = true;
51296         this.storeState();
51297     },
51298     
51299     onRegionExpanded : function(region){
51300         this.state[region.getPosition()].collapsed = false;
51301         this.storeState();
51302     }
51303 };/*
51304  * Based on:
51305  * Ext JS Library 1.1.1
51306  * Copyright(c) 2006-2007, Ext JS, LLC.
51307  *
51308  * Originally Released Under LGPL - original licence link has changed is not relivant.
51309  *
51310  * Fork - LGPL
51311  * <script type="text/javascript">
51312  */
51313 /**
51314  * @class Roo.ContentPanel
51315  * @extends Roo.util.Observable
51316  * A basic ContentPanel element.
51317  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51318  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51319  * @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
51320  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51321  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51322  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51323  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51324  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51325  * @cfg {String} title          The title for this panel
51326  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51327  * @cfg {String} url            Calls {@link #setUrl} with this value
51328  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51329  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51330  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51331  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51332
51333  * @constructor
51334  * Create a new ContentPanel.
51335  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51336  * @param {String/Object} config A string to set only the title or a config object
51337  * @param {String} content (optional) Set the HTML content for this panel
51338  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51339  */
51340 Roo.ContentPanel = function(el, config, content){
51341     
51342      
51343     /*
51344     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51345         config = el;
51346         el = Roo.id();
51347     }
51348     if (config && config.parentLayout) { 
51349         el = config.parentLayout.el.createChild(); 
51350     }
51351     */
51352     if(el.autoCreate){ // xtype is available if this is called from factory
51353         config = el;
51354         el = Roo.id();
51355     }
51356     this.el = Roo.get(el);
51357     if(!this.el && config && config.autoCreate){
51358         if(typeof config.autoCreate == "object"){
51359             if(!config.autoCreate.id){
51360                 config.autoCreate.id = config.id||el;
51361             }
51362             this.el = Roo.DomHelper.append(document.body,
51363                         config.autoCreate, true);
51364         }else{
51365             this.el = Roo.DomHelper.append(document.body,
51366                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51367         }
51368     }
51369     this.closable = false;
51370     this.loaded = false;
51371     this.active = false;
51372     if(typeof config == "string"){
51373         this.title = config;
51374     }else{
51375         Roo.apply(this, config);
51376     }
51377     
51378     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51379         this.wrapEl = this.el.wrap();
51380         this.toolbar.container = this.el.insertSibling(false, 'before');
51381         this.toolbar = new Roo.Toolbar(this.toolbar);
51382     }
51383     
51384     // xtype created footer. - not sure if will work as we normally have to render first..
51385     if (this.footer && !this.footer.el && this.footer.xtype) {
51386         if (!this.wrapEl) {
51387             this.wrapEl = this.el.wrap();
51388         }
51389     
51390         this.footer.container = this.wrapEl.createChild();
51391          
51392         this.footer = Roo.factory(this.footer, Roo);
51393         
51394     }
51395     
51396     if(this.resizeEl){
51397         this.resizeEl = Roo.get(this.resizeEl, true);
51398     }else{
51399         this.resizeEl = this.el;
51400     }
51401     // handle view.xtype
51402     
51403  
51404     
51405     
51406     this.addEvents({
51407         /**
51408          * @event activate
51409          * Fires when this panel is activated. 
51410          * @param {Roo.ContentPanel} this
51411          */
51412         "activate" : true,
51413         /**
51414          * @event deactivate
51415          * Fires when this panel is activated. 
51416          * @param {Roo.ContentPanel} this
51417          */
51418         "deactivate" : true,
51419
51420         /**
51421          * @event resize
51422          * Fires when this panel is resized if fitToFrame is true.
51423          * @param {Roo.ContentPanel} this
51424          * @param {Number} width The width after any component adjustments
51425          * @param {Number} height The height after any component adjustments
51426          */
51427         "resize" : true,
51428         
51429          /**
51430          * @event render
51431          * Fires when this tab is created
51432          * @param {Roo.ContentPanel} this
51433          */
51434         "render" : true
51435         
51436         
51437         
51438     });
51439     
51440
51441     
51442     
51443     if(this.autoScroll){
51444         this.resizeEl.setStyle("overflow", "auto");
51445     } else {
51446         // fix randome scrolling
51447         this.el.on('scroll', function() {
51448             Roo.log('fix random scolling');
51449             this.scrollTo('top',0); 
51450         });
51451     }
51452     content = content || this.content;
51453     if(content){
51454         this.setContent(content);
51455     }
51456     if(config && config.url){
51457         this.setUrl(this.url, this.params, this.loadOnce);
51458     }
51459     
51460     
51461     
51462     Roo.ContentPanel.superclass.constructor.call(this);
51463     
51464     if (this.view && typeof(this.view.xtype) != 'undefined') {
51465         this.view.el = this.el.appendChild(document.createElement("div"));
51466         this.view = Roo.factory(this.view); 
51467         this.view.render  &&  this.view.render(false, '');  
51468     }
51469     
51470     
51471     this.fireEvent('render', this);
51472 };
51473
51474 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51475     tabTip:'',
51476     setRegion : function(region){
51477         this.region = region;
51478         if(region){
51479            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51480         }else{
51481            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51482         } 
51483     },
51484     
51485     /**
51486      * Returns the toolbar for this Panel if one was configured. 
51487      * @return {Roo.Toolbar} 
51488      */
51489     getToolbar : function(){
51490         return this.toolbar;
51491     },
51492     
51493     setActiveState : function(active){
51494         this.active = active;
51495         if(!active){
51496             this.fireEvent("deactivate", this);
51497         }else{
51498             this.fireEvent("activate", this);
51499         }
51500     },
51501     /**
51502      * Updates this panel's element
51503      * @param {String} content The new content
51504      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51505     */
51506     setContent : function(content, loadScripts){
51507         this.el.update(content, loadScripts);
51508     },
51509
51510     ignoreResize : function(w, h){
51511         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51512             return true;
51513         }else{
51514             this.lastSize = {width: w, height: h};
51515             return false;
51516         }
51517     },
51518     /**
51519      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51520      * @return {Roo.UpdateManager} The UpdateManager
51521      */
51522     getUpdateManager : function(){
51523         return this.el.getUpdateManager();
51524     },
51525      /**
51526      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51527      * @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:
51528 <pre><code>
51529 panel.load({
51530     url: "your-url.php",
51531     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51532     callback: yourFunction,
51533     scope: yourObject, //(optional scope)
51534     discardUrl: false,
51535     nocache: false,
51536     text: "Loading...",
51537     timeout: 30,
51538     scripts: false
51539 });
51540 </code></pre>
51541      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51542      * 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.
51543      * @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}
51544      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51545      * @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.
51546      * @return {Roo.ContentPanel} this
51547      */
51548     load : function(){
51549         var um = this.el.getUpdateManager();
51550         um.update.apply(um, arguments);
51551         return this;
51552     },
51553
51554
51555     /**
51556      * 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.
51557      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51558      * @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)
51559      * @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)
51560      * @return {Roo.UpdateManager} The UpdateManager
51561      */
51562     setUrl : function(url, params, loadOnce){
51563         if(this.refreshDelegate){
51564             this.removeListener("activate", this.refreshDelegate);
51565         }
51566         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51567         this.on("activate", this.refreshDelegate);
51568         return this.el.getUpdateManager();
51569     },
51570     
51571     _handleRefresh : function(url, params, loadOnce){
51572         if(!loadOnce || !this.loaded){
51573             var updater = this.el.getUpdateManager();
51574             updater.update(url, params, this._setLoaded.createDelegate(this));
51575         }
51576     },
51577     
51578     _setLoaded : function(){
51579         this.loaded = true;
51580     }, 
51581     
51582     /**
51583      * Returns this panel's id
51584      * @return {String} 
51585      */
51586     getId : function(){
51587         return this.el.id;
51588     },
51589     
51590     /** 
51591      * Returns this panel's element - used by regiosn to add.
51592      * @return {Roo.Element} 
51593      */
51594     getEl : function(){
51595         return this.wrapEl || this.el;
51596     },
51597     
51598     adjustForComponents : function(width, height)
51599     {
51600         //Roo.log('adjustForComponents ');
51601         if(this.resizeEl != this.el){
51602             width -= this.el.getFrameWidth('lr');
51603             height -= this.el.getFrameWidth('tb');
51604         }
51605         if(this.toolbar){
51606             var te = this.toolbar.getEl();
51607             height -= te.getHeight();
51608             te.setWidth(width);
51609         }
51610         if(this.footer){
51611             var te = this.footer.getEl();
51612             Roo.log("footer:" + te.getHeight());
51613             
51614             height -= te.getHeight();
51615             te.setWidth(width);
51616         }
51617         
51618         
51619         if(this.adjustments){
51620             width += this.adjustments[0];
51621             height += this.adjustments[1];
51622         }
51623         return {"width": width, "height": height};
51624     },
51625     
51626     setSize : function(width, height){
51627         if(this.fitToFrame && !this.ignoreResize(width, height)){
51628             if(this.fitContainer && this.resizeEl != this.el){
51629                 this.el.setSize(width, height);
51630             }
51631             var size = this.adjustForComponents(width, height);
51632             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51633             this.fireEvent('resize', this, size.width, size.height);
51634         }
51635     },
51636     
51637     /**
51638      * Returns this panel's title
51639      * @return {String} 
51640      */
51641     getTitle : function(){
51642         return this.title;
51643     },
51644     
51645     /**
51646      * Set this panel's title
51647      * @param {String} title
51648      */
51649     setTitle : function(title){
51650         this.title = title;
51651         if(this.region){
51652             this.region.updatePanelTitle(this, title);
51653         }
51654     },
51655     
51656     /**
51657      * Returns true is this panel was configured to be closable
51658      * @return {Boolean} 
51659      */
51660     isClosable : function(){
51661         return this.closable;
51662     },
51663     
51664     beforeSlide : function(){
51665         this.el.clip();
51666         this.resizeEl.clip();
51667     },
51668     
51669     afterSlide : function(){
51670         this.el.unclip();
51671         this.resizeEl.unclip();
51672     },
51673     
51674     /**
51675      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51676      *   Will fail silently if the {@link #setUrl} method has not been called.
51677      *   This does not activate the panel, just updates its content.
51678      */
51679     refresh : function(){
51680         if(this.refreshDelegate){
51681            this.loaded = false;
51682            this.refreshDelegate();
51683         }
51684     },
51685     
51686     /**
51687      * Destroys this panel
51688      */
51689     destroy : function(){
51690         this.el.removeAllListeners();
51691         var tempEl = document.createElement("span");
51692         tempEl.appendChild(this.el.dom);
51693         tempEl.innerHTML = "";
51694         this.el.remove();
51695         this.el = null;
51696     },
51697     
51698     /**
51699      * form - if the content panel contains a form - this is a reference to it.
51700      * @type {Roo.form.Form}
51701      */
51702     form : false,
51703     /**
51704      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51705      *    This contains a reference to it.
51706      * @type {Roo.View}
51707      */
51708     view : false,
51709     
51710       /**
51711      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51712      * <pre><code>
51713
51714 layout.addxtype({
51715        xtype : 'Form',
51716        items: [ .... ]
51717    }
51718 );
51719
51720 </code></pre>
51721      * @param {Object} cfg Xtype definition of item to add.
51722      */
51723     
51724     addxtype : function(cfg) {
51725         // add form..
51726         if (cfg.xtype.match(/^Form$/)) {
51727             
51728             var el;
51729             //if (this.footer) {
51730             //    el = this.footer.container.insertSibling(false, 'before');
51731             //} else {
51732                 el = this.el.createChild();
51733             //}
51734
51735             this.form = new  Roo.form.Form(cfg);
51736             
51737             
51738             if ( this.form.allItems.length) this.form.render(el.dom);
51739             return this.form;
51740         }
51741         // should only have one of theses..
51742         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51743             // views.. should not be just added - used named prop 'view''
51744             
51745             cfg.el = this.el.appendChild(document.createElement("div"));
51746             // factory?
51747             
51748             var ret = new Roo.factory(cfg);
51749              
51750              ret.render && ret.render(false, ''); // render blank..
51751             this.view = ret;
51752             return ret;
51753         }
51754         return false;
51755     }
51756 });
51757
51758 /**
51759  * @class Roo.GridPanel
51760  * @extends Roo.ContentPanel
51761  * @constructor
51762  * Create a new GridPanel.
51763  * @param {Roo.grid.Grid} grid The grid for this panel
51764  * @param {String/Object} config A string to set only the panel's title, or a config object
51765  */
51766 Roo.GridPanel = function(grid, config){
51767     
51768   
51769     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51770         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51771         
51772     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51773     
51774     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51775     
51776     if(this.toolbar){
51777         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51778     }
51779     // xtype created footer. - not sure if will work as we normally have to render first..
51780     if (this.footer && !this.footer.el && this.footer.xtype) {
51781         
51782         this.footer.container = this.grid.getView().getFooterPanel(true);
51783         this.footer.dataSource = this.grid.dataSource;
51784         this.footer = Roo.factory(this.footer, Roo);
51785         
51786     }
51787     
51788     grid.monitorWindowResize = false; // turn off autosizing
51789     grid.autoHeight = false;
51790     grid.autoWidth = false;
51791     this.grid = grid;
51792     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51793 };
51794
51795 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51796     getId : function(){
51797         return this.grid.id;
51798     },
51799     
51800     /**
51801      * Returns the grid for this panel
51802      * @return {Roo.grid.Grid} 
51803      */
51804     getGrid : function(){
51805         return this.grid;    
51806     },
51807     
51808     setSize : function(width, height){
51809         if(!this.ignoreResize(width, height)){
51810             var grid = this.grid;
51811             var size = this.adjustForComponents(width, height);
51812             grid.getGridEl().setSize(size.width, size.height);
51813             grid.autoSize();
51814         }
51815     },
51816     
51817     beforeSlide : function(){
51818         this.grid.getView().scroller.clip();
51819     },
51820     
51821     afterSlide : function(){
51822         this.grid.getView().scroller.unclip();
51823     },
51824     
51825     destroy : function(){
51826         this.grid.destroy();
51827         delete this.grid;
51828         Roo.GridPanel.superclass.destroy.call(this); 
51829     }
51830 });
51831
51832
51833 /**
51834  * @class Roo.NestedLayoutPanel
51835  * @extends Roo.ContentPanel
51836  * @constructor
51837  * Create a new NestedLayoutPanel.
51838  * 
51839  * 
51840  * @param {Roo.BorderLayout} layout The layout for this panel
51841  * @param {String/Object} config A string to set only the title or a config object
51842  */
51843 Roo.NestedLayoutPanel = function(layout, config)
51844 {
51845     // construct with only one argument..
51846     /* FIXME - implement nicer consturctors
51847     if (layout.layout) {
51848         config = layout;
51849         layout = config.layout;
51850         delete config.layout;
51851     }
51852     if (layout.xtype && !layout.getEl) {
51853         // then layout needs constructing..
51854         layout = Roo.factory(layout, Roo);
51855     }
51856     */
51857     
51858     
51859     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51860     
51861     layout.monitorWindowResize = false; // turn off autosizing
51862     this.layout = layout;
51863     this.layout.getEl().addClass("x-layout-nested-layout");
51864     
51865     
51866     
51867     
51868 };
51869
51870 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51871
51872     setSize : function(width, height){
51873         if(!this.ignoreResize(width, height)){
51874             var size = this.adjustForComponents(width, height);
51875             var el = this.layout.getEl();
51876             el.setSize(size.width, size.height);
51877             var touch = el.dom.offsetWidth;
51878             this.layout.layout();
51879             // ie requires a double layout on the first pass
51880             if(Roo.isIE && !this.initialized){
51881                 this.initialized = true;
51882                 this.layout.layout();
51883             }
51884         }
51885     },
51886     
51887     // activate all subpanels if not currently active..
51888     
51889     setActiveState : function(active){
51890         this.active = active;
51891         if(!active){
51892             this.fireEvent("deactivate", this);
51893             return;
51894         }
51895         
51896         this.fireEvent("activate", this);
51897         // not sure if this should happen before or after..
51898         if (!this.layout) {
51899             return; // should not happen..
51900         }
51901         var reg = false;
51902         for (var r in this.layout.regions) {
51903             reg = this.layout.getRegion(r);
51904             if (reg.getActivePanel()) {
51905                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51906                 reg.setActivePanel(reg.getActivePanel());
51907                 continue;
51908             }
51909             if (!reg.panels.length) {
51910                 continue;
51911             }
51912             reg.showPanel(reg.getPanel(0));
51913         }
51914         
51915         
51916         
51917         
51918     },
51919     
51920     /**
51921      * Returns the nested BorderLayout for this panel
51922      * @return {Roo.BorderLayout} 
51923      */
51924     getLayout : function(){
51925         return this.layout;
51926     },
51927     
51928      /**
51929      * Adds a xtype elements to the layout of the nested panel
51930      * <pre><code>
51931
51932 panel.addxtype({
51933        xtype : 'ContentPanel',
51934        region: 'west',
51935        items: [ .... ]
51936    }
51937 );
51938
51939 panel.addxtype({
51940         xtype : 'NestedLayoutPanel',
51941         region: 'west',
51942         layout: {
51943            center: { },
51944            west: { }   
51945         },
51946         items : [ ... list of content panels or nested layout panels.. ]
51947    }
51948 );
51949 </code></pre>
51950      * @param {Object} cfg Xtype definition of item to add.
51951      */
51952     addxtype : function(cfg) {
51953         return this.layout.addxtype(cfg);
51954     
51955     }
51956 });
51957
51958 Roo.ScrollPanel = function(el, config, content){
51959     config = config || {};
51960     config.fitToFrame = true;
51961     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51962     
51963     this.el.dom.style.overflow = "hidden";
51964     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51965     this.el.removeClass("x-layout-inactive-content");
51966     this.el.on("mousewheel", this.onWheel, this);
51967
51968     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51969     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51970     up.unselectable(); down.unselectable();
51971     up.on("click", this.scrollUp, this);
51972     down.on("click", this.scrollDown, this);
51973     up.addClassOnOver("x-scroller-btn-over");
51974     down.addClassOnOver("x-scroller-btn-over");
51975     up.addClassOnClick("x-scroller-btn-click");
51976     down.addClassOnClick("x-scroller-btn-click");
51977     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51978
51979     this.resizeEl = this.el;
51980     this.el = wrap; this.up = up; this.down = down;
51981 };
51982
51983 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51984     increment : 100,
51985     wheelIncrement : 5,
51986     scrollUp : function(){
51987         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51988     },
51989
51990     scrollDown : function(){
51991         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51992     },
51993
51994     afterScroll : function(){
51995         var el = this.resizeEl;
51996         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51997         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51998         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51999     },
52000
52001     setSize : function(){
52002         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52003         this.afterScroll();
52004     },
52005
52006     onWheel : function(e){
52007         var d = e.getWheelDelta();
52008         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52009         this.afterScroll();
52010         e.stopEvent();
52011     },
52012
52013     setContent : function(content, loadScripts){
52014         this.resizeEl.update(content, loadScripts);
52015     }
52016
52017 });
52018
52019
52020
52021
52022
52023
52024
52025
52026
52027 /**
52028  * @class Roo.TreePanel
52029  * @extends Roo.ContentPanel
52030  * @constructor
52031  * Create a new TreePanel. - defaults to fit/scoll contents.
52032  * @param {String/Object} config A string to set only the panel's title, or a config object
52033  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52034  */
52035 Roo.TreePanel = function(config){
52036     var el = config.el;
52037     var tree = config.tree;
52038     delete config.tree; 
52039     delete config.el; // hopefull!
52040     
52041     // wrapper for IE7 strict & safari scroll issue
52042     
52043     var treeEl = el.createChild();
52044     config.resizeEl = treeEl;
52045     
52046     
52047     
52048     Roo.TreePanel.superclass.constructor.call(this, el, config);
52049  
52050  
52051     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52052     //console.log(tree);
52053     this.on('activate', function()
52054     {
52055         if (this.tree.rendered) {
52056             return;
52057         }
52058         //console.log('render tree');
52059         this.tree.render();
52060     });
52061     // this should not be needed.. - it's actually the 'el' that resizes?
52062     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52063     
52064     //this.on('resize',  function (cp, w, h) {
52065     //        this.tree.innerCt.setWidth(w);
52066     //        this.tree.innerCt.setHeight(h);
52067     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52068     //});
52069
52070         
52071     
52072 };
52073
52074 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52075     fitToFrame : true,
52076     autoScroll : true
52077 });
52078
52079
52080
52081
52082
52083
52084
52085
52086
52087
52088
52089 /*
52090  * Based on:
52091  * Ext JS Library 1.1.1
52092  * Copyright(c) 2006-2007, Ext JS, LLC.
52093  *
52094  * Originally Released Under LGPL - original licence link has changed is not relivant.
52095  *
52096  * Fork - LGPL
52097  * <script type="text/javascript">
52098  */
52099  
52100
52101 /**
52102  * @class Roo.ReaderLayout
52103  * @extends Roo.BorderLayout
52104  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52105  * center region containing two nested regions (a top one for a list view and one for item preview below),
52106  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52107  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52108  * expedites the setup of the overall layout and regions for this common application style.
52109  * Example:
52110  <pre><code>
52111 var reader = new Roo.ReaderLayout();
52112 var CP = Roo.ContentPanel;  // shortcut for adding
52113
52114 reader.beginUpdate();
52115 reader.add("north", new CP("north", "North"));
52116 reader.add("west", new CP("west", {title: "West"}));
52117 reader.add("east", new CP("east", {title: "East"}));
52118
52119 reader.regions.listView.add(new CP("listView", "List"));
52120 reader.regions.preview.add(new CP("preview", "Preview"));
52121 reader.endUpdate();
52122 </code></pre>
52123 * @constructor
52124 * Create a new ReaderLayout
52125 * @param {Object} config Configuration options
52126 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52127 * document.body if omitted)
52128 */
52129 Roo.ReaderLayout = function(config, renderTo){
52130     var c = config || {size:{}};
52131     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52132         north: c.north !== false ? Roo.apply({
52133             split:false,
52134             initialSize: 32,
52135             titlebar: false
52136         }, c.north) : false,
52137         west: c.west !== false ? Roo.apply({
52138             split:true,
52139             initialSize: 200,
52140             minSize: 175,
52141             maxSize: 400,
52142             titlebar: true,
52143             collapsible: true,
52144             animate: true,
52145             margins:{left:5,right:0,bottom:5,top:5},
52146             cmargins:{left:5,right:5,bottom:5,top:5}
52147         }, c.west) : false,
52148         east: c.east !== false ? Roo.apply({
52149             split:true,
52150             initialSize: 200,
52151             minSize: 175,
52152             maxSize: 400,
52153             titlebar: true,
52154             collapsible: true,
52155             animate: true,
52156             margins:{left:0,right:5,bottom:5,top:5},
52157             cmargins:{left:5,right:5,bottom:5,top:5}
52158         }, c.east) : false,
52159         center: Roo.apply({
52160             tabPosition: 'top',
52161             autoScroll:false,
52162             closeOnTab: true,
52163             titlebar:false,
52164             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52165         }, c.center)
52166     });
52167
52168     this.el.addClass('x-reader');
52169
52170     this.beginUpdate();
52171
52172     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52173         south: c.preview !== false ? Roo.apply({
52174             split:true,
52175             initialSize: 200,
52176             minSize: 100,
52177             autoScroll:true,
52178             collapsible:true,
52179             titlebar: true,
52180             cmargins:{top:5,left:0, right:0, bottom:0}
52181         }, c.preview) : false,
52182         center: Roo.apply({
52183             autoScroll:false,
52184             titlebar:false,
52185             minHeight:200
52186         }, c.listView)
52187     });
52188     this.add('center', new Roo.NestedLayoutPanel(inner,
52189             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52190
52191     this.endUpdate();
52192
52193     this.regions.preview = inner.getRegion('south');
52194     this.regions.listView = inner.getRegion('center');
52195 };
52196
52197 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52198  * Based on:
52199  * Ext JS Library 1.1.1
52200  * Copyright(c) 2006-2007, Ext JS, LLC.
52201  *
52202  * Originally Released Under LGPL - original licence link has changed is not relivant.
52203  *
52204  * Fork - LGPL
52205  * <script type="text/javascript">
52206  */
52207  
52208 /**
52209  * @class Roo.grid.Grid
52210  * @extends Roo.util.Observable
52211  * This class represents the primary interface of a component based grid control.
52212  * <br><br>Usage:<pre><code>
52213  var grid = new Roo.grid.Grid("my-container-id", {
52214      ds: myDataStore,
52215      cm: myColModel,
52216      selModel: mySelectionModel,
52217      autoSizeColumns: true,
52218      monitorWindowResize: false,
52219      trackMouseOver: true
52220  });
52221  // set any options
52222  grid.render();
52223  * </code></pre>
52224  * <b>Common Problems:</b><br/>
52225  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52226  * element will correct this<br/>
52227  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52228  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52229  * are unpredictable.<br/>
52230  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52231  * grid to calculate dimensions/offsets.<br/>
52232   * @constructor
52233  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52234  * The container MUST have some type of size defined for the grid to fill. The container will be
52235  * automatically set to position relative if it isn't already.
52236  * @param {Object} config A config object that sets properties on this grid.
52237  */
52238 Roo.grid.Grid = function(container, config){
52239         // initialize the container
52240         this.container = Roo.get(container);
52241         this.container.update("");
52242         this.container.setStyle("overflow", "hidden");
52243     this.container.addClass('x-grid-container');
52244
52245     this.id = this.container.id;
52246
52247     Roo.apply(this, config);
52248     // check and correct shorthanded configs
52249     if(this.ds){
52250         this.dataSource = this.ds;
52251         delete this.ds;
52252     }
52253     if(this.cm){
52254         this.colModel = this.cm;
52255         delete this.cm;
52256     }
52257     if(this.sm){
52258         this.selModel = this.sm;
52259         delete this.sm;
52260     }
52261
52262     if (this.selModel) {
52263         this.selModel = Roo.factory(this.selModel, Roo.grid);
52264         this.sm = this.selModel;
52265         this.sm.xmodule = this.xmodule || false;
52266     }
52267     if (typeof(this.colModel.config) == 'undefined') {
52268         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52269         this.cm = this.colModel;
52270         this.cm.xmodule = this.xmodule || false;
52271     }
52272     if (this.dataSource) {
52273         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52274         this.ds = this.dataSource;
52275         this.ds.xmodule = this.xmodule || false;
52276          
52277     }
52278     
52279     
52280     
52281     if(this.width){
52282         this.container.setWidth(this.width);
52283     }
52284
52285     if(this.height){
52286         this.container.setHeight(this.height);
52287     }
52288     /** @private */
52289         this.addEvents({
52290         // raw events
52291         /**
52292          * @event click
52293          * The raw click event for the entire grid.
52294          * @param {Roo.EventObject} e
52295          */
52296         "click" : true,
52297         /**
52298          * @event dblclick
52299          * The raw dblclick event for the entire grid.
52300          * @param {Roo.EventObject} e
52301          */
52302         "dblclick" : true,
52303         /**
52304          * @event contextmenu
52305          * The raw contextmenu event for the entire grid.
52306          * @param {Roo.EventObject} e
52307          */
52308         "contextmenu" : true,
52309         /**
52310          * @event mousedown
52311          * The raw mousedown event for the entire grid.
52312          * @param {Roo.EventObject} e
52313          */
52314         "mousedown" : true,
52315         /**
52316          * @event mouseup
52317          * The raw mouseup event for the entire grid.
52318          * @param {Roo.EventObject} e
52319          */
52320         "mouseup" : true,
52321         /**
52322          * @event mouseover
52323          * The raw mouseover event for the entire grid.
52324          * @param {Roo.EventObject} e
52325          */
52326         "mouseover" : true,
52327         /**
52328          * @event mouseout
52329          * The raw mouseout event for the entire grid.
52330          * @param {Roo.EventObject} e
52331          */
52332         "mouseout" : true,
52333         /**
52334          * @event keypress
52335          * The raw keypress event for the entire grid.
52336          * @param {Roo.EventObject} e
52337          */
52338         "keypress" : true,
52339         /**
52340          * @event keydown
52341          * The raw keydown event for the entire grid.
52342          * @param {Roo.EventObject} e
52343          */
52344         "keydown" : true,
52345
52346         // custom events
52347
52348         /**
52349          * @event cellclick
52350          * Fires when a cell is clicked
52351          * @param {Grid} this
52352          * @param {Number} rowIndex
52353          * @param {Number} columnIndex
52354          * @param {Roo.EventObject} e
52355          */
52356         "cellclick" : true,
52357         /**
52358          * @event celldblclick
52359          * Fires when a cell is double clicked
52360          * @param {Grid} this
52361          * @param {Number} rowIndex
52362          * @param {Number} columnIndex
52363          * @param {Roo.EventObject} e
52364          */
52365         "celldblclick" : true,
52366         /**
52367          * @event rowclick
52368          * Fires when a row is clicked
52369          * @param {Grid} this
52370          * @param {Number} rowIndex
52371          * @param {Roo.EventObject} e
52372          */
52373         "rowclick" : true,
52374         /**
52375          * @event rowdblclick
52376          * Fires when a row is double clicked
52377          * @param {Grid} this
52378          * @param {Number} rowIndex
52379          * @param {Roo.EventObject} e
52380          */
52381         "rowdblclick" : true,
52382         /**
52383          * @event headerclick
52384          * Fires when a header is clicked
52385          * @param {Grid} this
52386          * @param {Number} columnIndex
52387          * @param {Roo.EventObject} e
52388          */
52389         "headerclick" : true,
52390         /**
52391          * @event headerdblclick
52392          * Fires when a header cell is double clicked
52393          * @param {Grid} this
52394          * @param {Number} columnIndex
52395          * @param {Roo.EventObject} e
52396          */
52397         "headerdblclick" : true,
52398         /**
52399          * @event rowcontextmenu
52400          * Fires when a row is right clicked
52401          * @param {Grid} this
52402          * @param {Number} rowIndex
52403          * @param {Roo.EventObject} e
52404          */
52405         "rowcontextmenu" : true,
52406         /**
52407          * @event cellcontextmenu
52408          * Fires when a cell is right clicked
52409          * @param {Grid} this
52410          * @param {Number} rowIndex
52411          * @param {Number} cellIndex
52412          * @param {Roo.EventObject} e
52413          */
52414          "cellcontextmenu" : true,
52415         /**
52416          * @event headercontextmenu
52417          * Fires when a header is right clicked
52418          * @param {Grid} this
52419          * @param {Number} columnIndex
52420          * @param {Roo.EventObject} e
52421          */
52422         "headercontextmenu" : true,
52423         /**
52424          * @event bodyscroll
52425          * Fires when the body element is scrolled
52426          * @param {Number} scrollLeft
52427          * @param {Number} scrollTop
52428          */
52429         "bodyscroll" : true,
52430         /**
52431          * @event columnresize
52432          * Fires when the user resizes a column
52433          * @param {Number} columnIndex
52434          * @param {Number} newSize
52435          */
52436         "columnresize" : true,
52437         /**
52438          * @event columnmove
52439          * Fires when the user moves a column
52440          * @param {Number} oldIndex
52441          * @param {Number} newIndex
52442          */
52443         "columnmove" : true,
52444         /**
52445          * @event startdrag
52446          * Fires when row(s) start being dragged
52447          * @param {Grid} this
52448          * @param {Roo.GridDD} dd The drag drop object
52449          * @param {event} e The raw browser event
52450          */
52451         "startdrag" : true,
52452         /**
52453          * @event enddrag
52454          * Fires when a drag operation is complete
52455          * @param {Grid} this
52456          * @param {Roo.GridDD} dd The drag drop object
52457          * @param {event} e The raw browser event
52458          */
52459         "enddrag" : true,
52460         /**
52461          * @event dragdrop
52462          * Fires when dragged row(s) are dropped on a valid DD target
52463          * @param {Grid} this
52464          * @param {Roo.GridDD} dd The drag drop object
52465          * @param {String} targetId The target drag drop object
52466          * @param {event} e The raw browser event
52467          */
52468         "dragdrop" : true,
52469         /**
52470          * @event dragover
52471          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52472          * @param {Grid} this
52473          * @param {Roo.GridDD} dd The drag drop object
52474          * @param {String} targetId The target drag drop object
52475          * @param {event} e The raw browser event
52476          */
52477         "dragover" : true,
52478         /**
52479          * @event dragenter
52480          *  Fires when the dragged row(s) first cross another DD target while being dragged
52481          * @param {Grid} this
52482          * @param {Roo.GridDD} dd The drag drop object
52483          * @param {String} targetId The target drag drop object
52484          * @param {event} e The raw browser event
52485          */
52486         "dragenter" : true,
52487         /**
52488          * @event dragout
52489          * Fires when the dragged row(s) leave another DD target while being dragged
52490          * @param {Grid} this
52491          * @param {Roo.GridDD} dd The drag drop object
52492          * @param {String} targetId The target drag drop object
52493          * @param {event} e The raw browser event
52494          */
52495         "dragout" : true,
52496         /**
52497          * @event rowclass
52498          * Fires when a row is rendered, so you can change add a style to it.
52499          * @param {GridView} gridview   The grid view
52500          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52501          */
52502         'rowclass' : true,
52503
52504         /**
52505          * @event render
52506          * Fires when the grid is rendered
52507          * @param {Grid} grid
52508          */
52509         'render' : true
52510     });
52511
52512     Roo.grid.Grid.superclass.constructor.call(this);
52513 };
52514 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52515     
52516     /**
52517      * @cfg {String} ddGroup - drag drop group.
52518      */
52519
52520     /**
52521      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52522      */
52523     minColumnWidth : 25,
52524
52525     /**
52526      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52527      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52528      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52529      */
52530     autoSizeColumns : false,
52531
52532     /**
52533      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52534      */
52535     autoSizeHeaders : true,
52536
52537     /**
52538      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52539      */
52540     monitorWindowResize : true,
52541
52542     /**
52543      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52544      * rows measured to get a columns size. Default is 0 (all rows).
52545      */
52546     maxRowsToMeasure : 0,
52547
52548     /**
52549      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52550      */
52551     trackMouseOver : true,
52552
52553     /**
52554     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52555     */
52556     
52557     /**
52558     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52559     */
52560     enableDragDrop : false,
52561     
52562     /**
52563     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52564     */
52565     enableColumnMove : true,
52566     
52567     /**
52568     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52569     */
52570     enableColumnHide : true,
52571     
52572     /**
52573     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52574     */
52575     enableRowHeightSync : false,
52576     
52577     /**
52578     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52579     */
52580     stripeRows : true,
52581     
52582     /**
52583     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52584     */
52585     autoHeight : false,
52586
52587     /**
52588      * @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.
52589      */
52590     autoExpandColumn : false,
52591
52592     /**
52593     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52594     * Default is 50.
52595     */
52596     autoExpandMin : 50,
52597
52598     /**
52599     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52600     */
52601     autoExpandMax : 1000,
52602
52603     /**
52604     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52605     */
52606     view : null,
52607
52608     /**
52609     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52610     */
52611     loadMask : false,
52612     /**
52613     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52614     */
52615     dropTarget: false,
52616     
52617    
52618     
52619     // private
52620     rendered : false,
52621
52622     /**
52623     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52624     * of a fixed width. Default is false.
52625     */
52626     /**
52627     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52628     */
52629     /**
52630      * Called once after all setup has been completed and the grid is ready to be rendered.
52631      * @return {Roo.grid.Grid} this
52632      */
52633     render : function()
52634     {
52635         var c = this.container;
52636         // try to detect autoHeight/width mode
52637         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52638             this.autoHeight = true;
52639         }
52640         var view = this.getView();
52641         view.init(this);
52642
52643         c.on("click", this.onClick, this);
52644         c.on("dblclick", this.onDblClick, this);
52645         c.on("contextmenu", this.onContextMenu, this);
52646         c.on("keydown", this.onKeyDown, this);
52647         if (Roo.isTouch) {
52648             c.on("touchstart", this.onTouchStart, this);
52649         }
52650
52651         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52652
52653         this.getSelectionModel().init(this);
52654
52655         view.render();
52656
52657         if(this.loadMask){
52658             this.loadMask = new Roo.LoadMask(this.container,
52659                     Roo.apply({store:this.dataSource}, this.loadMask));
52660         }
52661         
52662         
52663         if (this.toolbar && this.toolbar.xtype) {
52664             this.toolbar.container = this.getView().getHeaderPanel(true);
52665             this.toolbar = new Roo.Toolbar(this.toolbar);
52666         }
52667         if (this.footer && this.footer.xtype) {
52668             this.footer.dataSource = this.getDataSource();
52669             this.footer.container = this.getView().getFooterPanel(true);
52670             this.footer = Roo.factory(this.footer, Roo);
52671         }
52672         if (this.dropTarget && this.dropTarget.xtype) {
52673             delete this.dropTarget.xtype;
52674             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52675         }
52676         
52677         
52678         this.rendered = true;
52679         this.fireEvent('render', this);
52680         return this;
52681     },
52682
52683         /**
52684          * Reconfigures the grid to use a different Store and Column Model.
52685          * The View will be bound to the new objects and refreshed.
52686          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52687          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52688          */
52689     reconfigure : function(dataSource, colModel){
52690         if(this.loadMask){
52691             this.loadMask.destroy();
52692             this.loadMask = new Roo.LoadMask(this.container,
52693                     Roo.apply({store:dataSource}, this.loadMask));
52694         }
52695         this.view.bind(dataSource, colModel);
52696         this.dataSource = dataSource;
52697         this.colModel = colModel;
52698         this.view.refresh(true);
52699     },
52700
52701     // private
52702     onKeyDown : function(e){
52703         this.fireEvent("keydown", e);
52704     },
52705
52706     /**
52707      * Destroy this grid.
52708      * @param {Boolean} removeEl True to remove the element
52709      */
52710     destroy : function(removeEl, keepListeners){
52711         if(this.loadMask){
52712             this.loadMask.destroy();
52713         }
52714         var c = this.container;
52715         c.removeAllListeners();
52716         this.view.destroy();
52717         this.colModel.purgeListeners();
52718         if(!keepListeners){
52719             this.purgeListeners();
52720         }
52721         c.update("");
52722         if(removeEl === true){
52723             c.remove();
52724         }
52725     },
52726
52727     // private
52728     processEvent : function(name, e){
52729         // does this fire select???
52730         //Roo.log('grid:processEvent '  + name);
52731         
52732         if (name != 'touchstart' ) {
52733             this.fireEvent(name, e);    
52734         }
52735         
52736         var t = e.getTarget();
52737         var v = this.view;
52738         var header = v.findHeaderIndex(t);
52739         if(header !== false){
52740             var ename = name == 'touchstart' ? 'click' : name;
52741              
52742             this.fireEvent("header" + ename, this, header, e);
52743         }else{
52744             var row = v.findRowIndex(t);
52745             var cell = v.findCellIndex(t);
52746             if (name == 'touchstart') {
52747                 // first touch is always a click.
52748                 // hopefull this happens after selection is updated.?
52749                 name = false;
52750                 
52751                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52752                     var cs = this.selModel.getSelectedCell();
52753                     if (row == cs[0] && cell == cs[1]){
52754                         name = 'dblclick';
52755                     }
52756                 }
52757                 if (typeof(this.selModel.getSelections) != 'undefined') {
52758                     var cs = this.selModel.getSelections();
52759                     var ds = this.dataSource;
52760                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52761                         name = 'dblclick';
52762                     }
52763                 }
52764                 if (!name) {
52765                     return;
52766                 }
52767             }
52768             
52769             
52770             if(row !== false){
52771                 this.fireEvent("row" + name, this, row, e);
52772                 if(cell !== false){
52773                     this.fireEvent("cell" + name, this, row, cell, e);
52774                 }
52775             }
52776         }
52777     },
52778
52779     // private
52780     onClick : function(e){
52781         this.processEvent("click", e);
52782     },
52783    // private
52784     onTouchStart : function(e){
52785         this.processEvent("touchstart", e);
52786     },
52787
52788     // private
52789     onContextMenu : function(e, t){
52790         this.processEvent("contextmenu", e);
52791     },
52792
52793     // private
52794     onDblClick : function(e){
52795         this.processEvent("dblclick", e);
52796     },
52797
52798     // private
52799     walkCells : function(row, col, step, fn, scope){
52800         var cm = this.colModel, clen = cm.getColumnCount();
52801         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52802         if(step < 0){
52803             if(col < 0){
52804                 row--;
52805                 first = false;
52806             }
52807             while(row >= 0){
52808                 if(!first){
52809                     col = clen-1;
52810                 }
52811                 first = false;
52812                 while(col >= 0){
52813                     if(fn.call(scope || this, row, col, cm) === true){
52814                         return [row, col];
52815                     }
52816                     col--;
52817                 }
52818                 row--;
52819             }
52820         } else {
52821             if(col >= clen){
52822                 row++;
52823                 first = false;
52824             }
52825             while(row < rlen){
52826                 if(!first){
52827                     col = 0;
52828                 }
52829                 first = false;
52830                 while(col < clen){
52831                     if(fn.call(scope || this, row, col, cm) === true){
52832                         return [row, col];
52833                     }
52834                     col++;
52835                 }
52836                 row++;
52837             }
52838         }
52839         return null;
52840     },
52841
52842     // private
52843     getSelections : function(){
52844         return this.selModel.getSelections();
52845     },
52846
52847     /**
52848      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52849      * but if manual update is required this method will initiate it.
52850      */
52851     autoSize : function(){
52852         if(this.rendered){
52853             this.view.layout();
52854             if(this.view.adjustForScroll){
52855                 this.view.adjustForScroll();
52856             }
52857         }
52858     },
52859
52860     /**
52861      * Returns the grid's underlying element.
52862      * @return {Element} The element
52863      */
52864     getGridEl : function(){
52865         return this.container;
52866     },
52867
52868     // private for compatibility, overridden by editor grid
52869     stopEditing : function(){},
52870
52871     /**
52872      * Returns the grid's SelectionModel.
52873      * @return {SelectionModel}
52874      */
52875     getSelectionModel : function(){
52876         if(!this.selModel){
52877             this.selModel = new Roo.grid.RowSelectionModel();
52878         }
52879         return this.selModel;
52880     },
52881
52882     /**
52883      * Returns the grid's DataSource.
52884      * @return {DataSource}
52885      */
52886     getDataSource : function(){
52887         return this.dataSource;
52888     },
52889
52890     /**
52891      * Returns the grid's ColumnModel.
52892      * @return {ColumnModel}
52893      */
52894     getColumnModel : function(){
52895         return this.colModel;
52896     },
52897
52898     /**
52899      * Returns the grid's GridView object.
52900      * @return {GridView}
52901      */
52902     getView : function(){
52903         if(!this.view){
52904             this.view = new Roo.grid.GridView(this.viewConfig);
52905         }
52906         return this.view;
52907     },
52908     /**
52909      * Called to get grid's drag proxy text, by default returns this.ddText.
52910      * @return {String}
52911      */
52912     getDragDropText : function(){
52913         var count = this.selModel.getCount();
52914         return String.format(this.ddText, count, count == 1 ? '' : 's');
52915     }
52916 });
52917 /**
52918  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52919  * %0 is replaced with the number of selected rows.
52920  * @type String
52921  */
52922 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52923  * Based on:
52924  * Ext JS Library 1.1.1
52925  * Copyright(c) 2006-2007, Ext JS, LLC.
52926  *
52927  * Originally Released Under LGPL - original licence link has changed is not relivant.
52928  *
52929  * Fork - LGPL
52930  * <script type="text/javascript">
52931  */
52932  
52933 Roo.grid.AbstractGridView = function(){
52934         this.grid = null;
52935         
52936         this.events = {
52937             "beforerowremoved" : true,
52938             "beforerowsinserted" : true,
52939             "beforerefresh" : true,
52940             "rowremoved" : true,
52941             "rowsinserted" : true,
52942             "rowupdated" : true,
52943             "refresh" : true
52944         };
52945     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52946 };
52947
52948 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52949     rowClass : "x-grid-row",
52950     cellClass : "x-grid-cell",
52951     tdClass : "x-grid-td",
52952     hdClass : "x-grid-hd",
52953     splitClass : "x-grid-hd-split",
52954     
52955     init: function(grid){
52956         this.grid = grid;
52957                 var cid = this.grid.getGridEl().id;
52958         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52959         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52960         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52961         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52962         },
52963         
52964     getColumnRenderers : function(){
52965         var renderers = [];
52966         var cm = this.grid.colModel;
52967         var colCount = cm.getColumnCount();
52968         for(var i = 0; i < colCount; i++){
52969             renderers[i] = cm.getRenderer(i);
52970         }
52971         return renderers;
52972     },
52973     
52974     getColumnIds : function(){
52975         var ids = [];
52976         var cm = this.grid.colModel;
52977         var colCount = cm.getColumnCount();
52978         for(var i = 0; i < colCount; i++){
52979             ids[i] = cm.getColumnId(i);
52980         }
52981         return ids;
52982     },
52983     
52984     getDataIndexes : function(){
52985         if(!this.indexMap){
52986             this.indexMap = this.buildIndexMap();
52987         }
52988         return this.indexMap.colToData;
52989     },
52990     
52991     getColumnIndexByDataIndex : function(dataIndex){
52992         if(!this.indexMap){
52993             this.indexMap = this.buildIndexMap();
52994         }
52995         return this.indexMap.dataToCol[dataIndex];
52996     },
52997     
52998     /**
52999      * Set a css style for a column dynamically. 
53000      * @param {Number} colIndex The index of the column
53001      * @param {String} name The css property name
53002      * @param {String} value The css value
53003      */
53004     setCSSStyle : function(colIndex, name, value){
53005         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53006         Roo.util.CSS.updateRule(selector, name, value);
53007     },
53008     
53009     generateRules : function(cm){
53010         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53011         Roo.util.CSS.removeStyleSheet(rulesId);
53012         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53013             var cid = cm.getColumnId(i);
53014             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53015                          this.tdSelector, cid, " {\n}\n",
53016                          this.hdSelector, cid, " {\n}\n",
53017                          this.splitSelector, cid, " {\n}\n");
53018         }
53019         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53020     }
53021 });/*
53022  * Based on:
53023  * Ext JS Library 1.1.1
53024  * Copyright(c) 2006-2007, Ext JS, LLC.
53025  *
53026  * Originally Released Under LGPL - original licence link has changed is not relivant.
53027  *
53028  * Fork - LGPL
53029  * <script type="text/javascript">
53030  */
53031
53032 // private
53033 // This is a support class used internally by the Grid components
53034 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53035     this.grid = grid;
53036     this.view = grid.getView();
53037     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53038     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53039     if(hd2){
53040         this.setHandleElId(Roo.id(hd));
53041         this.setOuterHandleElId(Roo.id(hd2));
53042     }
53043     this.scroll = false;
53044 };
53045 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53046     maxDragWidth: 120,
53047     getDragData : function(e){
53048         var t = Roo.lib.Event.getTarget(e);
53049         var h = this.view.findHeaderCell(t);
53050         if(h){
53051             return {ddel: h.firstChild, header:h};
53052         }
53053         return false;
53054     },
53055
53056     onInitDrag : function(e){
53057         this.view.headersDisabled = true;
53058         var clone = this.dragData.ddel.cloneNode(true);
53059         clone.id = Roo.id();
53060         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53061         this.proxy.update(clone);
53062         return true;
53063     },
53064
53065     afterValidDrop : function(){
53066         var v = this.view;
53067         setTimeout(function(){
53068             v.headersDisabled = false;
53069         }, 50);
53070     },
53071
53072     afterInvalidDrop : function(){
53073         var v = this.view;
53074         setTimeout(function(){
53075             v.headersDisabled = false;
53076         }, 50);
53077     }
53078 });
53079 /*
53080  * Based on:
53081  * Ext JS Library 1.1.1
53082  * Copyright(c) 2006-2007, Ext JS, LLC.
53083  *
53084  * Originally Released Under LGPL - original licence link has changed is not relivant.
53085  *
53086  * Fork - LGPL
53087  * <script type="text/javascript">
53088  */
53089 // private
53090 // This is a support class used internally by the Grid components
53091 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53092     this.grid = grid;
53093     this.view = grid.getView();
53094     // split the proxies so they don't interfere with mouse events
53095     this.proxyTop = Roo.DomHelper.append(document.body, {
53096         cls:"col-move-top", html:"&#160;"
53097     }, true);
53098     this.proxyBottom = Roo.DomHelper.append(document.body, {
53099         cls:"col-move-bottom", html:"&#160;"
53100     }, true);
53101     this.proxyTop.hide = this.proxyBottom.hide = function(){
53102         this.setLeftTop(-100,-100);
53103         this.setStyle("visibility", "hidden");
53104     };
53105     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53106     // temporarily disabled
53107     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53108     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53109 };
53110 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53111     proxyOffsets : [-4, -9],
53112     fly: Roo.Element.fly,
53113
53114     getTargetFromEvent : function(e){
53115         var t = Roo.lib.Event.getTarget(e);
53116         var cindex = this.view.findCellIndex(t);
53117         if(cindex !== false){
53118             return this.view.getHeaderCell(cindex);
53119         }
53120         return null;
53121     },
53122
53123     nextVisible : function(h){
53124         var v = this.view, cm = this.grid.colModel;
53125         h = h.nextSibling;
53126         while(h){
53127             if(!cm.isHidden(v.getCellIndex(h))){
53128                 return h;
53129             }
53130             h = h.nextSibling;
53131         }
53132         return null;
53133     },
53134
53135     prevVisible : function(h){
53136         var v = this.view, cm = this.grid.colModel;
53137         h = h.prevSibling;
53138         while(h){
53139             if(!cm.isHidden(v.getCellIndex(h))){
53140                 return h;
53141             }
53142             h = h.prevSibling;
53143         }
53144         return null;
53145     },
53146
53147     positionIndicator : function(h, n, e){
53148         var x = Roo.lib.Event.getPageX(e);
53149         var r = Roo.lib.Dom.getRegion(n.firstChild);
53150         var px, pt, py = r.top + this.proxyOffsets[1];
53151         if((r.right - x) <= (r.right-r.left)/2){
53152             px = r.right+this.view.borderWidth;
53153             pt = "after";
53154         }else{
53155             px = r.left;
53156             pt = "before";
53157         }
53158         var oldIndex = this.view.getCellIndex(h);
53159         var newIndex = this.view.getCellIndex(n);
53160
53161         if(this.grid.colModel.isFixed(newIndex)){
53162             return false;
53163         }
53164
53165         var locked = this.grid.colModel.isLocked(newIndex);
53166
53167         if(pt == "after"){
53168             newIndex++;
53169         }
53170         if(oldIndex < newIndex){
53171             newIndex--;
53172         }
53173         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53174             return false;
53175         }
53176         px +=  this.proxyOffsets[0];
53177         this.proxyTop.setLeftTop(px, py);
53178         this.proxyTop.show();
53179         if(!this.bottomOffset){
53180             this.bottomOffset = this.view.mainHd.getHeight();
53181         }
53182         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53183         this.proxyBottom.show();
53184         return pt;
53185     },
53186
53187     onNodeEnter : function(n, dd, e, data){
53188         if(data.header != n){
53189             this.positionIndicator(data.header, n, e);
53190         }
53191     },
53192
53193     onNodeOver : function(n, dd, e, data){
53194         var result = false;
53195         if(data.header != n){
53196             result = this.positionIndicator(data.header, n, e);
53197         }
53198         if(!result){
53199             this.proxyTop.hide();
53200             this.proxyBottom.hide();
53201         }
53202         return result ? this.dropAllowed : this.dropNotAllowed;
53203     },
53204
53205     onNodeOut : function(n, dd, e, data){
53206         this.proxyTop.hide();
53207         this.proxyBottom.hide();
53208     },
53209
53210     onNodeDrop : function(n, dd, e, data){
53211         var h = data.header;
53212         if(h != n){
53213             var cm = this.grid.colModel;
53214             var x = Roo.lib.Event.getPageX(e);
53215             var r = Roo.lib.Dom.getRegion(n.firstChild);
53216             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53217             var oldIndex = this.view.getCellIndex(h);
53218             var newIndex = this.view.getCellIndex(n);
53219             var locked = cm.isLocked(newIndex);
53220             if(pt == "after"){
53221                 newIndex++;
53222             }
53223             if(oldIndex < newIndex){
53224                 newIndex--;
53225             }
53226             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53227                 return false;
53228             }
53229             cm.setLocked(oldIndex, locked, true);
53230             cm.moveColumn(oldIndex, newIndex);
53231             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53232             return true;
53233         }
53234         return false;
53235     }
53236 });
53237 /*
53238  * Based on:
53239  * Ext JS Library 1.1.1
53240  * Copyright(c) 2006-2007, Ext JS, LLC.
53241  *
53242  * Originally Released Under LGPL - original licence link has changed is not relivant.
53243  *
53244  * Fork - LGPL
53245  * <script type="text/javascript">
53246  */
53247   
53248 /**
53249  * @class Roo.grid.GridView
53250  * @extends Roo.util.Observable
53251  *
53252  * @constructor
53253  * @param {Object} config
53254  */
53255 Roo.grid.GridView = function(config){
53256     Roo.grid.GridView.superclass.constructor.call(this);
53257     this.el = null;
53258
53259     Roo.apply(this, config);
53260 };
53261
53262 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53263
53264     unselectable :  'unselectable="on"',
53265     unselectableCls :  'x-unselectable',
53266     
53267     
53268     rowClass : "x-grid-row",
53269
53270     cellClass : "x-grid-col",
53271
53272     tdClass : "x-grid-td",
53273
53274     hdClass : "x-grid-hd",
53275
53276     splitClass : "x-grid-split",
53277
53278     sortClasses : ["sort-asc", "sort-desc"],
53279
53280     enableMoveAnim : false,
53281
53282     hlColor: "C3DAF9",
53283
53284     dh : Roo.DomHelper,
53285
53286     fly : Roo.Element.fly,
53287
53288     css : Roo.util.CSS,
53289
53290     borderWidth: 1,
53291
53292     splitOffset: 3,
53293
53294     scrollIncrement : 22,
53295
53296     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53297
53298     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53299
53300     bind : function(ds, cm){
53301         if(this.ds){
53302             this.ds.un("load", this.onLoad, this);
53303             this.ds.un("datachanged", this.onDataChange, this);
53304             this.ds.un("add", this.onAdd, this);
53305             this.ds.un("remove", this.onRemove, this);
53306             this.ds.un("update", this.onUpdate, this);
53307             this.ds.un("clear", this.onClear, this);
53308         }
53309         if(ds){
53310             ds.on("load", this.onLoad, this);
53311             ds.on("datachanged", this.onDataChange, this);
53312             ds.on("add", this.onAdd, this);
53313             ds.on("remove", this.onRemove, this);
53314             ds.on("update", this.onUpdate, this);
53315             ds.on("clear", this.onClear, this);
53316         }
53317         this.ds = ds;
53318
53319         if(this.cm){
53320             this.cm.un("widthchange", this.onColWidthChange, this);
53321             this.cm.un("headerchange", this.onHeaderChange, this);
53322             this.cm.un("hiddenchange", this.onHiddenChange, this);
53323             this.cm.un("columnmoved", this.onColumnMove, this);
53324             this.cm.un("columnlockchange", this.onColumnLock, this);
53325         }
53326         if(cm){
53327             this.generateRules(cm);
53328             cm.on("widthchange", this.onColWidthChange, this);
53329             cm.on("headerchange", this.onHeaderChange, this);
53330             cm.on("hiddenchange", this.onHiddenChange, this);
53331             cm.on("columnmoved", this.onColumnMove, this);
53332             cm.on("columnlockchange", this.onColumnLock, this);
53333         }
53334         this.cm = cm;
53335     },
53336
53337     init: function(grid){
53338         Roo.grid.GridView.superclass.init.call(this, grid);
53339
53340         this.bind(grid.dataSource, grid.colModel);
53341
53342         grid.on("headerclick", this.handleHeaderClick, this);
53343
53344         if(grid.trackMouseOver){
53345             grid.on("mouseover", this.onRowOver, this);
53346             grid.on("mouseout", this.onRowOut, this);
53347         }
53348         grid.cancelTextSelection = function(){};
53349         this.gridId = grid.id;
53350
53351         var tpls = this.templates || {};
53352
53353         if(!tpls.master){
53354             tpls.master = new Roo.Template(
53355                '<div class="x-grid" hidefocus="true">',
53356                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53357                   '<div class="x-grid-topbar"></div>',
53358                   '<div class="x-grid-scroller"><div></div></div>',
53359                   '<div class="x-grid-locked">',
53360                       '<div class="x-grid-header">{lockedHeader}</div>',
53361                       '<div class="x-grid-body">{lockedBody}</div>',
53362                   "</div>",
53363                   '<div class="x-grid-viewport">',
53364                       '<div class="x-grid-header">{header}</div>',
53365                       '<div class="x-grid-body">{body}</div>',
53366                   "</div>",
53367                   '<div class="x-grid-bottombar"></div>',
53368                  
53369                   '<div class="x-grid-resize-proxy">&#160;</div>',
53370                "</div>"
53371             );
53372             tpls.master.disableformats = true;
53373         }
53374
53375         if(!tpls.header){
53376             tpls.header = new Roo.Template(
53377                '<table border="0" cellspacing="0" cellpadding="0">',
53378                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53379                "</table>{splits}"
53380             );
53381             tpls.header.disableformats = true;
53382         }
53383         tpls.header.compile();
53384
53385         if(!tpls.hcell){
53386             tpls.hcell = new Roo.Template(
53387                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53388                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53389                 "</div></td>"
53390              );
53391              tpls.hcell.disableFormats = true;
53392         }
53393         tpls.hcell.compile();
53394
53395         if(!tpls.hsplit){
53396             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53397                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53398             tpls.hsplit.disableFormats = true;
53399         }
53400         tpls.hsplit.compile();
53401
53402         if(!tpls.body){
53403             tpls.body = new Roo.Template(
53404                '<table border="0" cellspacing="0" cellpadding="0">',
53405                "<tbody>{rows}</tbody>",
53406                "</table>"
53407             );
53408             tpls.body.disableFormats = true;
53409         }
53410         tpls.body.compile();
53411
53412         if(!tpls.row){
53413             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53414             tpls.row.disableFormats = true;
53415         }
53416         tpls.row.compile();
53417
53418         if(!tpls.cell){
53419             tpls.cell = new Roo.Template(
53420                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53421                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53422                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53423                 "</td>"
53424             );
53425             tpls.cell.disableFormats = true;
53426         }
53427         tpls.cell.compile();
53428
53429         this.templates = tpls;
53430     },
53431
53432     // remap these for backwards compat
53433     onColWidthChange : function(){
53434         this.updateColumns.apply(this, arguments);
53435     },
53436     onHeaderChange : function(){
53437         this.updateHeaders.apply(this, arguments);
53438     }, 
53439     onHiddenChange : function(){
53440         this.handleHiddenChange.apply(this, arguments);
53441     },
53442     onColumnMove : function(){
53443         this.handleColumnMove.apply(this, arguments);
53444     },
53445     onColumnLock : function(){
53446         this.handleLockChange.apply(this, arguments);
53447     },
53448
53449     onDataChange : function(){
53450         this.refresh();
53451         this.updateHeaderSortState();
53452     },
53453
53454     onClear : function(){
53455         this.refresh();
53456     },
53457
53458     onUpdate : function(ds, record){
53459         this.refreshRow(record);
53460     },
53461
53462     refreshRow : function(record){
53463         var ds = this.ds, index;
53464         if(typeof record == 'number'){
53465             index = record;
53466             record = ds.getAt(index);
53467         }else{
53468             index = ds.indexOf(record);
53469         }
53470         this.insertRows(ds, index, index, true);
53471         this.onRemove(ds, record, index+1, true);
53472         this.syncRowHeights(index, index);
53473         this.layout();
53474         this.fireEvent("rowupdated", this, index, record);
53475     },
53476
53477     onAdd : function(ds, records, index){
53478         this.insertRows(ds, index, index + (records.length-1));
53479     },
53480
53481     onRemove : function(ds, record, index, isUpdate){
53482         if(isUpdate !== true){
53483             this.fireEvent("beforerowremoved", this, index, record);
53484         }
53485         var bt = this.getBodyTable(), lt = this.getLockedTable();
53486         if(bt.rows[index]){
53487             bt.firstChild.removeChild(bt.rows[index]);
53488         }
53489         if(lt.rows[index]){
53490             lt.firstChild.removeChild(lt.rows[index]);
53491         }
53492         if(isUpdate !== true){
53493             this.stripeRows(index);
53494             this.syncRowHeights(index, index);
53495             this.layout();
53496             this.fireEvent("rowremoved", this, index, record);
53497         }
53498     },
53499
53500     onLoad : function(){
53501         this.scrollToTop();
53502     },
53503
53504     /**
53505      * Scrolls the grid to the top
53506      */
53507     scrollToTop : function(){
53508         if(this.scroller){
53509             this.scroller.dom.scrollTop = 0;
53510             this.syncScroll();
53511         }
53512     },
53513
53514     /**
53515      * Gets a panel in the header of the grid that can be used for toolbars etc.
53516      * After modifying the contents of this panel a call to grid.autoSize() may be
53517      * required to register any changes in size.
53518      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53519      * @return Roo.Element
53520      */
53521     getHeaderPanel : function(doShow){
53522         if(doShow){
53523             this.headerPanel.show();
53524         }
53525         return this.headerPanel;
53526     },
53527
53528     /**
53529      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53530      * After modifying the contents of this panel a call to grid.autoSize() may be
53531      * required to register any changes in size.
53532      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53533      * @return Roo.Element
53534      */
53535     getFooterPanel : function(doShow){
53536         if(doShow){
53537             this.footerPanel.show();
53538         }
53539         return this.footerPanel;
53540     },
53541
53542     initElements : function(){
53543         var E = Roo.Element;
53544         var el = this.grid.getGridEl().dom.firstChild;
53545         var cs = el.childNodes;
53546
53547         this.el = new E(el);
53548         
53549          this.focusEl = new E(el.firstChild);
53550         this.focusEl.swallowEvent("click", true);
53551         
53552         this.headerPanel = new E(cs[1]);
53553         this.headerPanel.enableDisplayMode("block");
53554
53555         this.scroller = new E(cs[2]);
53556         this.scrollSizer = new E(this.scroller.dom.firstChild);
53557
53558         this.lockedWrap = new E(cs[3]);
53559         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53560         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53561
53562         this.mainWrap = new E(cs[4]);
53563         this.mainHd = new E(this.mainWrap.dom.firstChild);
53564         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53565
53566         this.footerPanel = new E(cs[5]);
53567         this.footerPanel.enableDisplayMode("block");
53568
53569         this.resizeProxy = new E(cs[6]);
53570
53571         this.headerSelector = String.format(
53572            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53573            this.lockedHd.id, this.mainHd.id
53574         );
53575
53576         this.splitterSelector = String.format(
53577            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53578            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53579         );
53580     },
53581     idToCssName : function(s)
53582     {
53583         return s.replace(/[^a-z0-9]+/ig, '-');
53584     },
53585
53586     getHeaderCell : function(index){
53587         return Roo.DomQuery.select(this.headerSelector)[index];
53588     },
53589
53590     getHeaderCellMeasure : function(index){
53591         return this.getHeaderCell(index).firstChild;
53592     },
53593
53594     getHeaderCellText : function(index){
53595         return this.getHeaderCell(index).firstChild.firstChild;
53596     },
53597
53598     getLockedTable : function(){
53599         return this.lockedBody.dom.firstChild;
53600     },
53601
53602     getBodyTable : function(){
53603         return this.mainBody.dom.firstChild;
53604     },
53605
53606     getLockedRow : function(index){
53607         return this.getLockedTable().rows[index];
53608     },
53609
53610     getRow : function(index){
53611         return this.getBodyTable().rows[index];
53612     },
53613
53614     getRowComposite : function(index){
53615         if(!this.rowEl){
53616             this.rowEl = new Roo.CompositeElementLite();
53617         }
53618         var els = [], lrow, mrow;
53619         if(lrow = this.getLockedRow(index)){
53620             els.push(lrow);
53621         }
53622         if(mrow = this.getRow(index)){
53623             els.push(mrow);
53624         }
53625         this.rowEl.elements = els;
53626         return this.rowEl;
53627     },
53628     /**
53629      * Gets the 'td' of the cell
53630      * 
53631      * @param {Integer} rowIndex row to select
53632      * @param {Integer} colIndex column to select
53633      * 
53634      * @return {Object} 
53635      */
53636     getCell : function(rowIndex, colIndex){
53637         var locked = this.cm.getLockedCount();
53638         var source;
53639         if(colIndex < locked){
53640             source = this.lockedBody.dom.firstChild;
53641         }else{
53642             source = this.mainBody.dom.firstChild;
53643             colIndex -= locked;
53644         }
53645         return source.rows[rowIndex].childNodes[colIndex];
53646     },
53647
53648     getCellText : function(rowIndex, colIndex){
53649         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53650     },
53651
53652     getCellBox : function(cell){
53653         var b = this.fly(cell).getBox();
53654         if(Roo.isOpera){ // opera fails to report the Y
53655             b.y = cell.offsetTop + this.mainBody.getY();
53656         }
53657         return b;
53658     },
53659
53660     getCellIndex : function(cell){
53661         var id = String(cell.className).match(this.cellRE);
53662         if(id){
53663             return parseInt(id[1], 10);
53664         }
53665         return 0;
53666     },
53667
53668     findHeaderIndex : function(n){
53669         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53670         return r ? this.getCellIndex(r) : false;
53671     },
53672
53673     findHeaderCell : function(n){
53674         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53675         return r ? r : false;
53676     },
53677
53678     findRowIndex : function(n){
53679         if(!n){
53680             return false;
53681         }
53682         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53683         return r ? r.rowIndex : false;
53684     },
53685
53686     findCellIndex : function(node){
53687         var stop = this.el.dom;
53688         while(node && node != stop){
53689             if(this.findRE.test(node.className)){
53690                 return this.getCellIndex(node);
53691             }
53692             node = node.parentNode;
53693         }
53694         return false;
53695     },
53696
53697     getColumnId : function(index){
53698         return this.cm.getColumnId(index);
53699     },
53700
53701     getSplitters : function()
53702     {
53703         if(this.splitterSelector){
53704            return Roo.DomQuery.select(this.splitterSelector);
53705         }else{
53706             return null;
53707       }
53708     },
53709
53710     getSplitter : function(index){
53711         return this.getSplitters()[index];
53712     },
53713
53714     onRowOver : function(e, t){
53715         var row;
53716         if((row = this.findRowIndex(t)) !== false){
53717             this.getRowComposite(row).addClass("x-grid-row-over");
53718         }
53719     },
53720
53721     onRowOut : function(e, t){
53722         var row;
53723         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53724             this.getRowComposite(row).removeClass("x-grid-row-over");
53725         }
53726     },
53727
53728     renderHeaders : function(){
53729         var cm = this.cm;
53730         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53731         var cb = [], lb = [], sb = [], lsb = [], p = {};
53732         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53733             p.cellId = "x-grid-hd-0-" + i;
53734             p.splitId = "x-grid-csplit-0-" + i;
53735             p.id = cm.getColumnId(i);
53736             p.title = cm.getColumnTooltip(i) || "";
53737             p.value = cm.getColumnHeader(i) || "";
53738             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53739             if(!cm.isLocked(i)){
53740                 cb[cb.length] = ct.apply(p);
53741                 sb[sb.length] = st.apply(p);
53742             }else{
53743                 lb[lb.length] = ct.apply(p);
53744                 lsb[lsb.length] = st.apply(p);
53745             }
53746         }
53747         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53748                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53749     },
53750
53751     updateHeaders : function(){
53752         var html = this.renderHeaders();
53753         this.lockedHd.update(html[0]);
53754         this.mainHd.update(html[1]);
53755     },
53756
53757     /**
53758      * Focuses the specified row.
53759      * @param {Number} row The row index
53760      */
53761     focusRow : function(row)
53762     {
53763         //Roo.log('GridView.focusRow');
53764         var x = this.scroller.dom.scrollLeft;
53765         this.focusCell(row, 0, false);
53766         this.scroller.dom.scrollLeft = x;
53767     },
53768
53769     /**
53770      * Focuses the specified cell.
53771      * @param {Number} row The row index
53772      * @param {Number} col The column index
53773      * @param {Boolean} hscroll false to disable horizontal scrolling
53774      */
53775     focusCell : function(row, col, hscroll)
53776     {
53777         //Roo.log('GridView.focusCell');
53778         var el = this.ensureVisible(row, col, hscroll);
53779         this.focusEl.alignTo(el, "tl-tl");
53780         if(Roo.isGecko){
53781             this.focusEl.focus();
53782         }else{
53783             this.focusEl.focus.defer(1, this.focusEl);
53784         }
53785     },
53786
53787     /**
53788      * Scrolls the specified cell into view
53789      * @param {Number} row The row index
53790      * @param {Number} col The column index
53791      * @param {Boolean} hscroll false to disable horizontal scrolling
53792      */
53793     ensureVisible : function(row, col, hscroll)
53794     {
53795         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53796         //return null; //disable for testing.
53797         if(typeof row != "number"){
53798             row = row.rowIndex;
53799         }
53800         if(row < 0 && row >= this.ds.getCount()){
53801             return  null;
53802         }
53803         col = (col !== undefined ? col : 0);
53804         var cm = this.grid.colModel;
53805         while(cm.isHidden(col)){
53806             col++;
53807         }
53808
53809         var el = this.getCell(row, col);
53810         if(!el){
53811             return null;
53812         }
53813         var c = this.scroller.dom;
53814
53815         var ctop = parseInt(el.offsetTop, 10);
53816         var cleft = parseInt(el.offsetLeft, 10);
53817         var cbot = ctop + el.offsetHeight;
53818         var cright = cleft + el.offsetWidth;
53819         
53820         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53821         var stop = parseInt(c.scrollTop, 10);
53822         var sleft = parseInt(c.scrollLeft, 10);
53823         var sbot = stop + ch;
53824         var sright = sleft + c.clientWidth;
53825         /*
53826         Roo.log('GridView.ensureVisible:' +
53827                 ' ctop:' + ctop +
53828                 ' c.clientHeight:' + c.clientHeight +
53829                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53830                 ' stop:' + stop +
53831                 ' cbot:' + cbot +
53832                 ' sbot:' + sbot +
53833                 ' ch:' + ch  
53834                 );
53835         */
53836         if(ctop < stop){
53837              c.scrollTop = ctop;
53838             //Roo.log("set scrolltop to ctop DISABLE?");
53839         }else if(cbot > sbot){
53840             //Roo.log("set scrolltop to cbot-ch");
53841             c.scrollTop = cbot-ch;
53842         }
53843         
53844         if(hscroll !== false){
53845             if(cleft < sleft){
53846                 c.scrollLeft = cleft;
53847             }else if(cright > sright){
53848                 c.scrollLeft = cright-c.clientWidth;
53849             }
53850         }
53851          
53852         return el;
53853     },
53854
53855     updateColumns : function(){
53856         this.grid.stopEditing();
53857         var cm = this.grid.colModel, colIds = this.getColumnIds();
53858         //var totalWidth = cm.getTotalWidth();
53859         var pos = 0;
53860         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53861             //if(cm.isHidden(i)) continue;
53862             var w = cm.getColumnWidth(i);
53863             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53864             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53865         }
53866         this.updateSplitters();
53867     },
53868
53869     generateRules : function(cm){
53870         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53871         Roo.util.CSS.removeStyleSheet(rulesId);
53872         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53873             var cid = cm.getColumnId(i);
53874             var align = '';
53875             if(cm.config[i].align){
53876                 align = 'text-align:'+cm.config[i].align+';';
53877             }
53878             var hidden = '';
53879             if(cm.isHidden(i)){
53880                 hidden = 'display:none;';
53881             }
53882             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53883             ruleBuf.push(
53884                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53885                     this.hdSelector, cid, " {\n", align, width, "}\n",
53886                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53887                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53888         }
53889         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53890     },
53891
53892     updateSplitters : function(){
53893         var cm = this.cm, s = this.getSplitters();
53894         if(s){ // splitters not created yet
53895             var pos = 0, locked = true;
53896             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53897                 if(cm.isHidden(i)) continue;
53898                 var w = cm.getColumnWidth(i); // make sure it's a number
53899                 if(!cm.isLocked(i) && locked){
53900                     pos = 0;
53901                     locked = false;
53902                 }
53903                 pos += w;
53904                 s[i].style.left = (pos-this.splitOffset) + "px";
53905             }
53906         }
53907     },
53908
53909     handleHiddenChange : function(colModel, colIndex, hidden){
53910         if(hidden){
53911             this.hideColumn(colIndex);
53912         }else{
53913             this.unhideColumn(colIndex);
53914         }
53915     },
53916
53917     hideColumn : function(colIndex){
53918         var cid = this.getColumnId(colIndex);
53919         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53920         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53921         if(Roo.isSafari){
53922             this.updateHeaders();
53923         }
53924         this.updateSplitters();
53925         this.layout();
53926     },
53927
53928     unhideColumn : function(colIndex){
53929         var cid = this.getColumnId(colIndex);
53930         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53931         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53932
53933         if(Roo.isSafari){
53934             this.updateHeaders();
53935         }
53936         this.updateSplitters();
53937         this.layout();
53938     },
53939
53940     insertRows : function(dm, firstRow, lastRow, isUpdate){
53941         if(firstRow == 0 && lastRow == dm.getCount()-1){
53942             this.refresh();
53943         }else{
53944             if(!isUpdate){
53945                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53946             }
53947             var s = this.getScrollState();
53948             var markup = this.renderRows(firstRow, lastRow);
53949             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53950             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53951             this.restoreScroll(s);
53952             if(!isUpdate){
53953                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53954                 this.syncRowHeights(firstRow, lastRow);
53955                 this.stripeRows(firstRow);
53956                 this.layout();
53957             }
53958         }
53959     },
53960
53961     bufferRows : function(markup, target, index){
53962         var before = null, trows = target.rows, tbody = target.tBodies[0];
53963         if(index < trows.length){
53964             before = trows[index];
53965         }
53966         var b = document.createElement("div");
53967         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53968         var rows = b.firstChild.rows;
53969         for(var i = 0, len = rows.length; i < len; i++){
53970             if(before){
53971                 tbody.insertBefore(rows[0], before);
53972             }else{
53973                 tbody.appendChild(rows[0]);
53974             }
53975         }
53976         b.innerHTML = "";
53977         b = null;
53978     },
53979
53980     deleteRows : function(dm, firstRow, lastRow){
53981         if(dm.getRowCount()<1){
53982             this.fireEvent("beforerefresh", this);
53983             this.mainBody.update("");
53984             this.lockedBody.update("");
53985             this.fireEvent("refresh", this);
53986         }else{
53987             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53988             var bt = this.getBodyTable();
53989             var tbody = bt.firstChild;
53990             var rows = bt.rows;
53991             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53992                 tbody.removeChild(rows[firstRow]);
53993             }
53994             this.stripeRows(firstRow);
53995             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53996         }
53997     },
53998
53999     updateRows : function(dataSource, firstRow, lastRow){
54000         var s = this.getScrollState();
54001         this.refresh();
54002         this.restoreScroll(s);
54003     },
54004
54005     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54006         if(!noRefresh){
54007            this.refresh();
54008         }
54009         this.updateHeaderSortState();
54010     },
54011
54012     getScrollState : function(){
54013         
54014         var sb = this.scroller.dom;
54015         return {left: sb.scrollLeft, top: sb.scrollTop};
54016     },
54017
54018     stripeRows : function(startRow){
54019         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54020             return;
54021         }
54022         startRow = startRow || 0;
54023         var rows = this.getBodyTable().rows;
54024         var lrows = this.getLockedTable().rows;
54025         var cls = ' x-grid-row-alt ';
54026         for(var i = startRow, len = rows.length; i < len; i++){
54027             var row = rows[i], lrow = lrows[i];
54028             var isAlt = ((i+1) % 2 == 0);
54029             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54030             if(isAlt == hasAlt){
54031                 continue;
54032             }
54033             if(isAlt){
54034                 row.className += " x-grid-row-alt";
54035             }else{
54036                 row.className = row.className.replace("x-grid-row-alt", "");
54037             }
54038             if(lrow){
54039                 lrow.className = row.className;
54040             }
54041         }
54042     },
54043
54044     restoreScroll : function(state){
54045         //Roo.log('GridView.restoreScroll');
54046         var sb = this.scroller.dom;
54047         sb.scrollLeft = state.left;
54048         sb.scrollTop = state.top;
54049         this.syncScroll();
54050     },
54051
54052     syncScroll : function(){
54053         //Roo.log('GridView.syncScroll');
54054         var sb = this.scroller.dom;
54055         var sh = this.mainHd.dom;
54056         var bs = this.mainBody.dom;
54057         var lv = this.lockedBody.dom;
54058         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54059         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54060     },
54061
54062     handleScroll : function(e){
54063         this.syncScroll();
54064         var sb = this.scroller.dom;
54065         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54066         e.stopEvent();
54067     },
54068
54069     handleWheel : function(e){
54070         var d = e.getWheelDelta();
54071         this.scroller.dom.scrollTop -= d*22;
54072         // set this here to prevent jumpy scrolling on large tables
54073         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54074         e.stopEvent();
54075     },
54076
54077     renderRows : function(startRow, endRow){
54078         // pull in all the crap needed to render rows
54079         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54080         var colCount = cm.getColumnCount();
54081
54082         if(ds.getCount() < 1){
54083             return ["", ""];
54084         }
54085
54086         // build a map for all the columns
54087         var cs = [];
54088         for(var i = 0; i < colCount; i++){
54089             var name = cm.getDataIndex(i);
54090             cs[i] = {
54091                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54092                 renderer : cm.getRenderer(i),
54093                 id : cm.getColumnId(i),
54094                 locked : cm.isLocked(i)
54095             };
54096         }
54097
54098         startRow = startRow || 0;
54099         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54100
54101         // records to render
54102         var rs = ds.getRange(startRow, endRow);
54103
54104         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54105     },
54106
54107     // As much as I hate to duplicate code, this was branched because FireFox really hates
54108     // [].join("") on strings. The performance difference was substantial enough to
54109     // branch this function
54110     doRender : Roo.isGecko ?
54111             function(cs, rs, ds, startRow, colCount, stripe){
54112                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54113                 // buffers
54114                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54115                 
54116                 var hasListener = this.grid.hasListener('rowclass');
54117                 var rowcfg = {};
54118                 for(var j = 0, len = rs.length; j < len; j++){
54119                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54120                     for(var i = 0; i < colCount; i++){
54121                         c = cs[i];
54122                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54123                         p.id = c.id;
54124                         p.css = p.attr = "";
54125                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54126                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54127                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54128                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54129                         }
54130                         var markup = ct.apply(p);
54131                         if(!c.locked){
54132                             cb+= markup;
54133                         }else{
54134                             lcb+= markup;
54135                         }
54136                     }
54137                     var alt = [];
54138                     if(stripe && ((rowIndex+1) % 2 == 0)){
54139                         alt.push("x-grid-row-alt")
54140                     }
54141                     if(r.dirty){
54142                         alt.push(  " x-grid-dirty-row");
54143                     }
54144                     rp.cells = lcb;
54145                     if(this.getRowClass){
54146                         alt.push(this.getRowClass(r, rowIndex));
54147                     }
54148                     if (hasListener) {
54149                         rowcfg = {
54150                              
54151                             record: r,
54152                             rowIndex : rowIndex,
54153                             rowClass : ''
54154                         }
54155                         this.grid.fireEvent('rowclass', this, rowcfg);
54156                         alt.push(rowcfg.rowClass);
54157                     }
54158                     rp.alt = alt.join(" ");
54159                     lbuf+= rt.apply(rp);
54160                     rp.cells = cb;
54161                     buf+=  rt.apply(rp);
54162                 }
54163                 return [lbuf, buf];
54164             } :
54165             function(cs, rs, ds, startRow, colCount, stripe){
54166                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54167                 // buffers
54168                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54169                 var hasListener = this.grid.hasListener('rowclass');
54170  
54171                 var rowcfg = {};
54172                 for(var j = 0, len = rs.length; j < len; j++){
54173                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54174                     for(var i = 0; i < colCount; i++){
54175                         c = cs[i];
54176                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54177                         p.id = c.id;
54178                         p.css = p.attr = "";
54179                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54180                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54181                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54182                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54183                         }
54184                         
54185                         var markup = ct.apply(p);
54186                         if(!c.locked){
54187                             cb[cb.length] = markup;
54188                         }else{
54189                             lcb[lcb.length] = markup;
54190                         }
54191                     }
54192                     var alt = [];
54193                     if(stripe && ((rowIndex+1) % 2 == 0)){
54194                         alt.push( "x-grid-row-alt");
54195                     }
54196                     if(r.dirty){
54197                         alt.push(" x-grid-dirty-row");
54198                     }
54199                     rp.cells = lcb;
54200                     if(this.getRowClass){
54201                         alt.push( this.getRowClass(r, rowIndex));
54202                     }
54203                     if (hasListener) {
54204                         rowcfg = {
54205                              
54206                             record: r,
54207                             rowIndex : rowIndex,
54208                             rowClass : ''
54209                         }
54210                         this.grid.fireEvent('rowclass', this, rowcfg);
54211                         alt.push(rowcfg.rowClass);
54212                     }
54213                     rp.alt = alt.join(" ");
54214                     rp.cells = lcb.join("");
54215                     lbuf[lbuf.length] = rt.apply(rp);
54216                     rp.cells = cb.join("");
54217                     buf[buf.length] =  rt.apply(rp);
54218                 }
54219                 return [lbuf.join(""), buf.join("")];
54220             },
54221
54222     renderBody : function(){
54223         var markup = this.renderRows();
54224         var bt = this.templates.body;
54225         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54226     },
54227
54228     /**
54229      * Refreshes the grid
54230      * @param {Boolean} headersToo
54231      */
54232     refresh : function(headersToo){
54233         this.fireEvent("beforerefresh", this);
54234         this.grid.stopEditing();
54235         var result = this.renderBody();
54236         this.lockedBody.update(result[0]);
54237         this.mainBody.update(result[1]);
54238         if(headersToo === true){
54239             this.updateHeaders();
54240             this.updateColumns();
54241             this.updateSplitters();
54242             this.updateHeaderSortState();
54243         }
54244         this.syncRowHeights();
54245         this.layout();
54246         this.fireEvent("refresh", this);
54247     },
54248
54249     handleColumnMove : function(cm, oldIndex, newIndex){
54250         this.indexMap = null;
54251         var s = this.getScrollState();
54252         this.refresh(true);
54253         this.restoreScroll(s);
54254         this.afterMove(newIndex);
54255     },
54256
54257     afterMove : function(colIndex){
54258         if(this.enableMoveAnim && Roo.enableFx){
54259             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54260         }
54261         // if multisort - fix sortOrder, and reload..
54262         if (this.grid.dataSource.multiSort) {
54263             // the we can call sort again..
54264             var dm = this.grid.dataSource;
54265             var cm = this.grid.colModel;
54266             var so = [];
54267             for(var i = 0; i < cm.config.length; i++ ) {
54268                 
54269                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54270                     continue; // dont' bother, it's not in sort list or being set.
54271                 }
54272                 
54273                 so.push(cm.config[i].dataIndex);
54274             };
54275             dm.sortOrder = so;
54276             dm.load(dm.lastOptions);
54277             
54278             
54279         }
54280         
54281     },
54282
54283     updateCell : function(dm, rowIndex, dataIndex){
54284         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54285         if(typeof colIndex == "undefined"){ // not present in grid
54286             return;
54287         }
54288         var cm = this.grid.colModel;
54289         var cell = this.getCell(rowIndex, colIndex);
54290         var cellText = this.getCellText(rowIndex, colIndex);
54291
54292         var p = {
54293             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54294             id : cm.getColumnId(colIndex),
54295             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54296         };
54297         var renderer = cm.getRenderer(colIndex);
54298         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54299         if(typeof val == "undefined" || val === "") val = "&#160;";
54300         cellText.innerHTML = val;
54301         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54302         this.syncRowHeights(rowIndex, rowIndex);
54303     },
54304
54305     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54306         var maxWidth = 0;
54307         if(this.grid.autoSizeHeaders){
54308             var h = this.getHeaderCellMeasure(colIndex);
54309             maxWidth = Math.max(maxWidth, h.scrollWidth);
54310         }
54311         var tb, index;
54312         if(this.cm.isLocked(colIndex)){
54313             tb = this.getLockedTable();
54314             index = colIndex;
54315         }else{
54316             tb = this.getBodyTable();
54317             index = colIndex - this.cm.getLockedCount();
54318         }
54319         if(tb && tb.rows){
54320             var rows = tb.rows;
54321             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54322             for(var i = 0; i < stopIndex; i++){
54323                 var cell = rows[i].childNodes[index].firstChild;
54324                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54325             }
54326         }
54327         return maxWidth + /*margin for error in IE*/ 5;
54328     },
54329     /**
54330      * Autofit a column to its content.
54331      * @param {Number} colIndex
54332      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54333      */
54334      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54335          if(this.cm.isHidden(colIndex)){
54336              return; // can't calc a hidden column
54337          }
54338         if(forceMinSize){
54339             var cid = this.cm.getColumnId(colIndex);
54340             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54341            if(this.grid.autoSizeHeaders){
54342                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54343            }
54344         }
54345         var newWidth = this.calcColumnWidth(colIndex);
54346         this.cm.setColumnWidth(colIndex,
54347             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54348         if(!suppressEvent){
54349             this.grid.fireEvent("columnresize", colIndex, newWidth);
54350         }
54351     },
54352
54353     /**
54354      * Autofits all columns to their content and then expands to fit any extra space in the grid
54355      */
54356      autoSizeColumns : function(){
54357         var cm = this.grid.colModel;
54358         var colCount = cm.getColumnCount();
54359         for(var i = 0; i < colCount; i++){
54360             this.autoSizeColumn(i, true, true);
54361         }
54362         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54363             this.fitColumns();
54364         }else{
54365             this.updateColumns();
54366             this.layout();
54367         }
54368     },
54369
54370     /**
54371      * Autofits all columns to the grid's width proportionate with their current size
54372      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54373      */
54374     fitColumns : function(reserveScrollSpace){
54375         var cm = this.grid.colModel;
54376         var colCount = cm.getColumnCount();
54377         var cols = [];
54378         var width = 0;
54379         var i, w;
54380         for (i = 0; i < colCount; i++){
54381             if(!cm.isHidden(i) && !cm.isFixed(i)){
54382                 w = cm.getColumnWidth(i);
54383                 cols.push(i);
54384                 cols.push(w);
54385                 width += w;
54386             }
54387         }
54388         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54389         if(reserveScrollSpace){
54390             avail -= 17;
54391         }
54392         var frac = (avail - cm.getTotalWidth())/width;
54393         while (cols.length){
54394             w = cols.pop();
54395             i = cols.pop();
54396             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54397         }
54398         this.updateColumns();
54399         this.layout();
54400     },
54401
54402     onRowSelect : function(rowIndex){
54403         var row = this.getRowComposite(rowIndex);
54404         row.addClass("x-grid-row-selected");
54405     },
54406
54407     onRowDeselect : function(rowIndex){
54408         var row = this.getRowComposite(rowIndex);
54409         row.removeClass("x-grid-row-selected");
54410     },
54411
54412     onCellSelect : function(row, col){
54413         var cell = this.getCell(row, col);
54414         if(cell){
54415             Roo.fly(cell).addClass("x-grid-cell-selected");
54416         }
54417     },
54418
54419     onCellDeselect : function(row, col){
54420         var cell = this.getCell(row, col);
54421         if(cell){
54422             Roo.fly(cell).removeClass("x-grid-cell-selected");
54423         }
54424     },
54425
54426     updateHeaderSortState : function(){
54427         
54428         // sort state can be single { field: xxx, direction : yyy}
54429         // or   { xxx=>ASC , yyy : DESC ..... }
54430         
54431         var mstate = {};
54432         if (!this.ds.multiSort) { 
54433             var state = this.ds.getSortState();
54434             if(!state){
54435                 return;
54436             }
54437             mstate[state.field] = state.direction;
54438             // FIXME... - this is not used here.. but might be elsewhere..
54439             this.sortState = state;
54440             
54441         } else {
54442             mstate = this.ds.sortToggle;
54443         }
54444         //remove existing sort classes..
54445         
54446         var sc = this.sortClasses;
54447         var hds = this.el.select(this.headerSelector).removeClass(sc);
54448         
54449         for(var f in mstate) {
54450         
54451             var sortColumn = this.cm.findColumnIndex(f);
54452             
54453             if(sortColumn != -1){
54454                 var sortDir = mstate[f];        
54455                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54456             }
54457         }
54458         
54459          
54460         
54461     },
54462
54463
54464     handleHeaderClick : function(g, index,e){
54465         
54466         Roo.log("header click");
54467         
54468         if (Roo.isTouch) {
54469             // touch events on header are handled by context
54470             this.handleHdCtx(g,index,e);
54471             return;
54472         }
54473         
54474         
54475         if(this.headersDisabled){
54476             return;
54477         }
54478         var dm = g.dataSource, cm = g.colModel;
54479         if(!cm.isSortable(index)){
54480             return;
54481         }
54482         g.stopEditing();
54483         
54484         if (dm.multiSort) {
54485             // update the sortOrder
54486             var so = [];
54487             for(var i = 0; i < cm.config.length; i++ ) {
54488                 
54489                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54490                     continue; // dont' bother, it's not in sort list or being set.
54491                 }
54492                 
54493                 so.push(cm.config[i].dataIndex);
54494             };
54495             dm.sortOrder = so;
54496         }
54497         
54498         
54499         dm.sort(cm.getDataIndex(index));
54500     },
54501
54502
54503     destroy : function(){
54504         if(this.colMenu){
54505             this.colMenu.removeAll();
54506             Roo.menu.MenuMgr.unregister(this.colMenu);
54507             this.colMenu.getEl().remove();
54508             delete this.colMenu;
54509         }
54510         if(this.hmenu){
54511             this.hmenu.removeAll();
54512             Roo.menu.MenuMgr.unregister(this.hmenu);
54513             this.hmenu.getEl().remove();
54514             delete this.hmenu;
54515         }
54516         if(this.grid.enableColumnMove){
54517             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54518             if(dds){
54519                 for(var dd in dds){
54520                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54521                         var elid = dds[dd].dragElId;
54522                         dds[dd].unreg();
54523                         Roo.get(elid).remove();
54524                     } else if(dds[dd].config.isTarget){
54525                         dds[dd].proxyTop.remove();
54526                         dds[dd].proxyBottom.remove();
54527                         dds[dd].unreg();
54528                     }
54529                     if(Roo.dd.DDM.locationCache[dd]){
54530                         delete Roo.dd.DDM.locationCache[dd];
54531                     }
54532                 }
54533                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54534             }
54535         }
54536         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54537         this.bind(null, null);
54538         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54539     },
54540
54541     handleLockChange : function(){
54542         this.refresh(true);
54543     },
54544
54545     onDenyColumnLock : function(){
54546
54547     },
54548
54549     onDenyColumnHide : function(){
54550
54551     },
54552
54553     handleHdMenuClick : function(item){
54554         var index = this.hdCtxIndex;
54555         var cm = this.cm, ds = this.ds;
54556         switch(item.id){
54557             case "asc":
54558                 ds.sort(cm.getDataIndex(index), "ASC");
54559                 break;
54560             case "desc":
54561                 ds.sort(cm.getDataIndex(index), "DESC");
54562                 break;
54563             case "lock":
54564                 var lc = cm.getLockedCount();
54565                 if(cm.getColumnCount(true) <= lc+1){
54566                     this.onDenyColumnLock();
54567                     return;
54568                 }
54569                 if(lc != index){
54570                     cm.setLocked(index, true, true);
54571                     cm.moveColumn(index, lc);
54572                     this.grid.fireEvent("columnmove", index, lc);
54573                 }else{
54574                     cm.setLocked(index, true);
54575                 }
54576             break;
54577             case "unlock":
54578                 var lc = cm.getLockedCount();
54579                 if((lc-1) != index){
54580                     cm.setLocked(index, false, true);
54581                     cm.moveColumn(index, lc-1);
54582                     this.grid.fireEvent("columnmove", index, lc-1);
54583                 }else{
54584                     cm.setLocked(index, false);
54585                 }
54586             break;
54587             case 'wider': // used to expand cols on touch..
54588             case 'narrow':
54589                 var cw = cm.getColumnWidth(index);
54590                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54591                 cw = Math.max(0, cw);
54592                 cw = Math.min(cw,4000);
54593                 cm.setColumnWidth(index, cw);
54594                 break;
54595                 
54596             default:
54597                 index = cm.getIndexById(item.id.substr(4));
54598                 if(index != -1){
54599                     if(item.checked && cm.getColumnCount(true) <= 1){
54600                         this.onDenyColumnHide();
54601                         return false;
54602                     }
54603                     cm.setHidden(index, item.checked);
54604                 }
54605         }
54606         return true;
54607     },
54608
54609     beforeColMenuShow : function(){
54610         var cm = this.cm,  colCount = cm.getColumnCount();
54611         this.colMenu.removeAll();
54612         for(var i = 0; i < colCount; i++){
54613             this.colMenu.add(new Roo.menu.CheckItem({
54614                 id: "col-"+cm.getColumnId(i),
54615                 text: cm.getColumnHeader(i),
54616                 checked: !cm.isHidden(i),
54617                 hideOnClick:false
54618             }));
54619         }
54620     },
54621
54622     handleHdCtx : function(g, index, e){
54623         e.stopEvent();
54624         var hd = this.getHeaderCell(index);
54625         this.hdCtxIndex = index;
54626         var ms = this.hmenu.items, cm = this.cm;
54627         ms.get("asc").setDisabled(!cm.isSortable(index));
54628         ms.get("desc").setDisabled(!cm.isSortable(index));
54629         if(this.grid.enableColLock !== false){
54630             ms.get("lock").setDisabled(cm.isLocked(index));
54631             ms.get("unlock").setDisabled(!cm.isLocked(index));
54632         }
54633         this.hmenu.show(hd, "tl-bl");
54634     },
54635
54636     handleHdOver : function(e){
54637         var hd = this.findHeaderCell(e.getTarget());
54638         if(hd && !this.headersDisabled){
54639             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54640                this.fly(hd).addClass("x-grid-hd-over");
54641             }
54642         }
54643     },
54644
54645     handleHdOut : function(e){
54646         var hd = this.findHeaderCell(e.getTarget());
54647         if(hd){
54648             this.fly(hd).removeClass("x-grid-hd-over");
54649         }
54650     },
54651
54652     handleSplitDblClick : function(e, t){
54653         var i = this.getCellIndex(t);
54654         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54655             this.autoSizeColumn(i, true);
54656             this.layout();
54657         }
54658     },
54659
54660     render : function(){
54661
54662         var cm = this.cm;
54663         var colCount = cm.getColumnCount();
54664
54665         if(this.grid.monitorWindowResize === true){
54666             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54667         }
54668         var header = this.renderHeaders();
54669         var body = this.templates.body.apply({rows:""});
54670         var html = this.templates.master.apply({
54671             lockedBody: body,
54672             body: body,
54673             lockedHeader: header[0],
54674             header: header[1]
54675         });
54676
54677         //this.updateColumns();
54678
54679         this.grid.getGridEl().dom.innerHTML = html;
54680
54681         this.initElements();
54682         
54683         // a kludge to fix the random scolling effect in webkit
54684         this.el.on("scroll", function() {
54685             this.el.dom.scrollTop=0; // hopefully not recursive..
54686         },this);
54687
54688         this.scroller.on("scroll", this.handleScroll, this);
54689         this.lockedBody.on("mousewheel", this.handleWheel, this);
54690         this.mainBody.on("mousewheel", this.handleWheel, this);
54691
54692         this.mainHd.on("mouseover", this.handleHdOver, this);
54693         this.mainHd.on("mouseout", this.handleHdOut, this);
54694         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54695                 {delegate: "."+this.splitClass});
54696
54697         this.lockedHd.on("mouseover", this.handleHdOver, this);
54698         this.lockedHd.on("mouseout", this.handleHdOut, this);
54699         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54700                 {delegate: "."+this.splitClass});
54701
54702         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54703             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54704         }
54705
54706         this.updateSplitters();
54707
54708         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54709             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54710             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54711         }
54712
54713         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54714             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54715             this.hmenu.add(
54716                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54717                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54718             );
54719             if(this.grid.enableColLock !== false){
54720                 this.hmenu.add('-',
54721                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54722                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54723                 );
54724             }
54725             if (Roo.isTouch) {
54726                  this.hmenu.add('-',
54727                     {id:"wider", text: this.columnsWiderText},
54728                     {id:"narrow", text: this.columnsNarrowText }
54729                 );
54730                 
54731                  
54732             }
54733             
54734             if(this.grid.enableColumnHide !== false){
54735
54736                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54737                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54738                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54739
54740                 this.hmenu.add('-',
54741                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54742                 );
54743             }
54744             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54745
54746             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54747         }
54748
54749         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54750             this.dd = new Roo.grid.GridDragZone(this.grid, {
54751                 ddGroup : this.grid.ddGroup || 'GridDD'
54752             });
54753             
54754         }
54755
54756         /*
54757         for(var i = 0; i < colCount; i++){
54758             if(cm.isHidden(i)){
54759                 this.hideColumn(i);
54760             }
54761             if(cm.config[i].align){
54762                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54763                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54764             }
54765         }*/
54766         
54767         this.updateHeaderSortState();
54768
54769         this.beforeInitialResize();
54770         this.layout(true);
54771
54772         // two part rendering gives faster view to the user
54773         this.renderPhase2.defer(1, this);
54774     },
54775
54776     renderPhase2 : function(){
54777         // render the rows now
54778         this.refresh();
54779         if(this.grid.autoSizeColumns){
54780             this.autoSizeColumns();
54781         }
54782     },
54783
54784     beforeInitialResize : function(){
54785
54786     },
54787
54788     onColumnSplitterMoved : function(i, w){
54789         this.userResized = true;
54790         var cm = this.grid.colModel;
54791         cm.setColumnWidth(i, w, true);
54792         var cid = cm.getColumnId(i);
54793         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54794         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54795         this.updateSplitters();
54796         this.layout();
54797         this.grid.fireEvent("columnresize", i, w);
54798     },
54799
54800     syncRowHeights : function(startIndex, endIndex){
54801         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54802             startIndex = startIndex || 0;
54803             var mrows = this.getBodyTable().rows;
54804             var lrows = this.getLockedTable().rows;
54805             var len = mrows.length-1;
54806             endIndex = Math.min(endIndex || len, len);
54807             for(var i = startIndex; i <= endIndex; i++){
54808                 var m = mrows[i], l = lrows[i];
54809                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54810                 m.style.height = l.style.height = h + "px";
54811             }
54812         }
54813     },
54814
54815     layout : function(initialRender, is2ndPass){
54816         var g = this.grid;
54817         var auto = g.autoHeight;
54818         var scrollOffset = 16;
54819         var c = g.getGridEl(), cm = this.cm,
54820                 expandCol = g.autoExpandColumn,
54821                 gv = this;
54822         //c.beginMeasure();
54823
54824         if(!c.dom.offsetWidth){ // display:none?
54825             if(initialRender){
54826                 this.lockedWrap.show();
54827                 this.mainWrap.show();
54828             }
54829             return;
54830         }
54831
54832         var hasLock = this.cm.isLocked(0);
54833
54834         var tbh = this.headerPanel.getHeight();
54835         var bbh = this.footerPanel.getHeight();
54836
54837         if(auto){
54838             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54839             var newHeight = ch + c.getBorderWidth("tb");
54840             if(g.maxHeight){
54841                 newHeight = Math.min(g.maxHeight, newHeight);
54842             }
54843             c.setHeight(newHeight);
54844         }
54845
54846         if(g.autoWidth){
54847             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54848         }
54849
54850         var s = this.scroller;
54851
54852         var csize = c.getSize(true);
54853
54854         this.el.setSize(csize.width, csize.height);
54855
54856         this.headerPanel.setWidth(csize.width);
54857         this.footerPanel.setWidth(csize.width);
54858
54859         var hdHeight = this.mainHd.getHeight();
54860         var vw = csize.width;
54861         var vh = csize.height - (tbh + bbh);
54862
54863         s.setSize(vw, vh);
54864
54865         var bt = this.getBodyTable();
54866         var ltWidth = hasLock ?
54867                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54868
54869         var scrollHeight = bt.offsetHeight;
54870         var scrollWidth = ltWidth + bt.offsetWidth;
54871         var vscroll = false, hscroll = false;
54872
54873         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54874
54875         var lw = this.lockedWrap, mw = this.mainWrap;
54876         var lb = this.lockedBody, mb = this.mainBody;
54877
54878         setTimeout(function(){
54879             var t = s.dom.offsetTop;
54880             var w = s.dom.clientWidth,
54881                 h = s.dom.clientHeight;
54882
54883             lw.setTop(t);
54884             lw.setSize(ltWidth, h);
54885
54886             mw.setLeftTop(ltWidth, t);
54887             mw.setSize(w-ltWidth, h);
54888
54889             lb.setHeight(h-hdHeight);
54890             mb.setHeight(h-hdHeight);
54891
54892             if(is2ndPass !== true && !gv.userResized && expandCol){
54893                 // high speed resize without full column calculation
54894                 
54895                 var ci = cm.getIndexById(expandCol);
54896                 if (ci < 0) {
54897                     ci = cm.findColumnIndex(expandCol);
54898                 }
54899                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54900                 var expandId = cm.getColumnId(ci);
54901                 var  tw = cm.getTotalWidth(false);
54902                 var currentWidth = cm.getColumnWidth(ci);
54903                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54904                 if(currentWidth != cw){
54905                     cm.setColumnWidth(ci, cw, true);
54906                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54907                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54908                     gv.updateSplitters();
54909                     gv.layout(false, true);
54910                 }
54911             }
54912
54913             if(initialRender){
54914                 lw.show();
54915                 mw.show();
54916             }
54917             //c.endMeasure();
54918         }, 10);
54919     },
54920
54921     onWindowResize : function(){
54922         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54923             return;
54924         }
54925         this.layout();
54926     },
54927
54928     appendFooter : function(parentEl){
54929         return null;
54930     },
54931
54932     sortAscText : "Sort Ascending",
54933     sortDescText : "Sort Descending",
54934     lockText : "Lock Column",
54935     unlockText : "Unlock Column",
54936     columnsText : "Columns",
54937  
54938     columnsWiderText : "Wider",
54939     columnsNarrowText : "Thinner"
54940 });
54941
54942
54943 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54944     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54945     this.proxy.el.addClass('x-grid3-col-dd');
54946 };
54947
54948 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54949     handleMouseDown : function(e){
54950
54951     },
54952
54953     callHandleMouseDown : function(e){
54954         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54955     }
54956 });
54957 /*
54958  * Based on:
54959  * Ext JS Library 1.1.1
54960  * Copyright(c) 2006-2007, Ext JS, LLC.
54961  *
54962  * Originally Released Under LGPL - original licence link has changed is not relivant.
54963  *
54964  * Fork - LGPL
54965  * <script type="text/javascript">
54966  */
54967  
54968 // private
54969 // This is a support class used internally by the Grid components
54970 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54971     this.grid = grid;
54972     this.view = grid.getView();
54973     this.proxy = this.view.resizeProxy;
54974     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54975         "gridSplitters" + this.grid.getGridEl().id, {
54976         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54977     });
54978     this.setHandleElId(Roo.id(hd));
54979     this.setOuterHandleElId(Roo.id(hd2));
54980     this.scroll = false;
54981 };
54982 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54983     fly: Roo.Element.fly,
54984
54985     b4StartDrag : function(x, y){
54986         this.view.headersDisabled = true;
54987         this.proxy.setHeight(this.view.mainWrap.getHeight());
54988         var w = this.cm.getColumnWidth(this.cellIndex);
54989         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54990         this.resetConstraints();
54991         this.setXConstraint(minw, 1000);
54992         this.setYConstraint(0, 0);
54993         this.minX = x - minw;
54994         this.maxX = x + 1000;
54995         this.startPos = x;
54996         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54997     },
54998
54999
55000     handleMouseDown : function(e){
55001         ev = Roo.EventObject.setEvent(e);
55002         var t = this.fly(ev.getTarget());
55003         if(t.hasClass("x-grid-split")){
55004             this.cellIndex = this.view.getCellIndex(t.dom);
55005             this.split = t.dom;
55006             this.cm = this.grid.colModel;
55007             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55008                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55009             }
55010         }
55011     },
55012
55013     endDrag : function(e){
55014         this.view.headersDisabled = false;
55015         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55016         var diff = endX - this.startPos;
55017         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55018     },
55019
55020     autoOffset : function(){
55021         this.setDelta(0,0);
55022     }
55023 });/*
55024  * Based on:
55025  * Ext JS Library 1.1.1
55026  * Copyright(c) 2006-2007, Ext JS, LLC.
55027  *
55028  * Originally Released Under LGPL - original licence link has changed is not relivant.
55029  *
55030  * Fork - LGPL
55031  * <script type="text/javascript">
55032  */
55033  
55034 // private
55035 // This is a support class used internally by the Grid components
55036 Roo.grid.GridDragZone = function(grid, config){
55037     this.view = grid.getView();
55038     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55039     if(this.view.lockedBody){
55040         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55041         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55042     }
55043     this.scroll = false;
55044     this.grid = grid;
55045     this.ddel = document.createElement('div');
55046     this.ddel.className = 'x-grid-dd-wrap';
55047 };
55048
55049 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55050     ddGroup : "GridDD",
55051
55052     getDragData : function(e){
55053         var t = Roo.lib.Event.getTarget(e);
55054         var rowIndex = this.view.findRowIndex(t);
55055         var sm = this.grid.selModel;
55056             
55057         //Roo.log(rowIndex);
55058         
55059         if (sm.getSelectedCell) {
55060             // cell selection..
55061             if (!sm.getSelectedCell()) {
55062                 return false;
55063             }
55064             if (rowIndex != sm.getSelectedCell()[0]) {
55065                 return false;
55066             }
55067         
55068         }
55069         
55070         if(rowIndex !== false){
55071             
55072             // if editorgrid.. 
55073             
55074             
55075             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55076                
55077             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55078               //  
55079             //}
55080             if (e.hasModifier()){
55081                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55082             }
55083             
55084             Roo.log("getDragData");
55085             
55086             return {
55087                 grid: this.grid,
55088                 ddel: this.ddel,
55089                 rowIndex: rowIndex,
55090                 selections:sm.getSelections ? sm.getSelections() : (
55091                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55092                 )
55093             };
55094         }
55095         return false;
55096     },
55097
55098     onInitDrag : function(e){
55099         var data = this.dragData;
55100         this.ddel.innerHTML = this.grid.getDragDropText();
55101         this.proxy.update(this.ddel);
55102         // fire start drag?
55103     },
55104
55105     afterRepair : function(){
55106         this.dragging = false;
55107     },
55108
55109     getRepairXY : function(e, data){
55110         return false;
55111     },
55112
55113     onEndDrag : function(data, e){
55114         // fire end drag?
55115     },
55116
55117     onValidDrop : function(dd, e, id){
55118         // fire drag drop?
55119         this.hideProxy();
55120     },
55121
55122     beforeInvalidDrop : function(e, id){
55123
55124     }
55125 });/*
55126  * Based on:
55127  * Ext JS Library 1.1.1
55128  * Copyright(c) 2006-2007, Ext JS, LLC.
55129  *
55130  * Originally Released Under LGPL - original licence link has changed is not relivant.
55131  *
55132  * Fork - LGPL
55133  * <script type="text/javascript">
55134  */
55135  
55136
55137 /**
55138  * @class Roo.grid.ColumnModel
55139  * @extends Roo.util.Observable
55140  * This is the default implementation of a ColumnModel used by the Grid. It defines
55141  * the columns in the grid.
55142  * <br>Usage:<br>
55143  <pre><code>
55144  var colModel = new Roo.grid.ColumnModel([
55145         {header: "Ticker", width: 60, sortable: true, locked: true},
55146         {header: "Company Name", width: 150, sortable: true},
55147         {header: "Market Cap.", width: 100, sortable: true},
55148         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55149         {header: "Employees", width: 100, sortable: true, resizable: false}
55150  ]);
55151  </code></pre>
55152  * <p>
55153  
55154  * The config options listed for this class are options which may appear in each
55155  * individual column definition.
55156  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55157  * @constructor
55158  * @param {Object} config An Array of column config objects. See this class's
55159  * config objects for details.
55160 */
55161 Roo.grid.ColumnModel = function(config){
55162         /**
55163      * The config passed into the constructor
55164      */
55165     this.config = config;
55166     this.lookup = {};
55167
55168     // if no id, create one
55169     // if the column does not have a dataIndex mapping,
55170     // map it to the order it is in the config
55171     for(var i = 0, len = config.length; i < len; i++){
55172         var c = config[i];
55173         if(typeof c.dataIndex == "undefined"){
55174             c.dataIndex = i;
55175         }
55176         if(typeof c.renderer == "string"){
55177             c.renderer = Roo.util.Format[c.renderer];
55178         }
55179         if(typeof c.id == "undefined"){
55180             c.id = Roo.id();
55181         }
55182         if(c.editor && c.editor.xtype){
55183             c.editor  = Roo.factory(c.editor, Roo.grid);
55184         }
55185         if(c.editor && c.editor.isFormField){
55186             c.editor = new Roo.grid.GridEditor(c.editor);
55187         }
55188         this.lookup[c.id] = c;
55189     }
55190
55191     /**
55192      * The width of columns which have no width specified (defaults to 100)
55193      * @type Number
55194      */
55195     this.defaultWidth = 100;
55196
55197     /**
55198      * Default sortable of columns which have no sortable specified (defaults to false)
55199      * @type Boolean
55200      */
55201     this.defaultSortable = false;
55202
55203     this.addEvents({
55204         /**
55205              * @event widthchange
55206              * Fires when the width of a column changes.
55207              * @param {ColumnModel} this
55208              * @param {Number} columnIndex The column index
55209              * @param {Number} newWidth The new width
55210              */
55211             "widthchange": true,
55212         /**
55213              * @event headerchange
55214              * Fires when the text of a header changes.
55215              * @param {ColumnModel} this
55216              * @param {Number} columnIndex The column index
55217              * @param {Number} newText The new header text
55218              */
55219             "headerchange": true,
55220         /**
55221              * @event hiddenchange
55222              * Fires when a column is hidden or "unhidden".
55223              * @param {ColumnModel} this
55224              * @param {Number} columnIndex The column index
55225              * @param {Boolean} hidden true if hidden, false otherwise
55226              */
55227             "hiddenchange": true,
55228             /**
55229          * @event columnmoved
55230          * Fires when a column is moved.
55231          * @param {ColumnModel} this
55232          * @param {Number} oldIndex
55233          * @param {Number} newIndex
55234          */
55235         "columnmoved" : true,
55236         /**
55237          * @event columlockchange
55238          * Fires when a column's locked state is changed
55239          * @param {ColumnModel} this
55240          * @param {Number} colIndex
55241          * @param {Boolean} locked true if locked
55242          */
55243         "columnlockchange" : true
55244     });
55245     Roo.grid.ColumnModel.superclass.constructor.call(this);
55246 };
55247 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55248     /**
55249      * @cfg {String} header The header text to display in the Grid view.
55250      */
55251     /**
55252      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55253      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55254      * specified, the column's index is used as an index into the Record's data Array.
55255      */
55256     /**
55257      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55258      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55259      */
55260     /**
55261      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55262      * Defaults to the value of the {@link #defaultSortable} property.
55263      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55264      */
55265     /**
55266      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55267      */
55268     /**
55269      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55270      */
55271     /**
55272      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55273      */
55274     /**
55275      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55276      */
55277     /**
55278      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55279      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55280      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55281      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55282      */
55283        /**
55284      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55285      */
55286     /**
55287      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55288      */
55289     /**
55290      * @cfg {String} cursor (Optional)
55291      */
55292     /**
55293      * @cfg {String} tooltip (Optional)
55294      */
55295     /**
55296      * Returns the id of the column at the specified index.
55297      * @param {Number} index The column index
55298      * @return {String} the id
55299      */
55300     getColumnId : function(index){
55301         return this.config[index].id;
55302     },
55303
55304     /**
55305      * Returns the column for a specified id.
55306      * @param {String} id The column id
55307      * @return {Object} the column
55308      */
55309     getColumnById : function(id){
55310         return this.lookup[id];
55311     },
55312
55313     
55314     /**
55315      * Returns the column for a specified dataIndex.
55316      * @param {String} dataIndex The column dataIndex
55317      * @return {Object|Boolean} the column or false if not found
55318      */
55319     getColumnByDataIndex: function(dataIndex){
55320         var index = this.findColumnIndex(dataIndex);
55321         return index > -1 ? this.config[index] : false;
55322     },
55323     
55324     /**
55325      * Returns the index for a specified column id.
55326      * @param {String} id The column id
55327      * @return {Number} the index, or -1 if not found
55328      */
55329     getIndexById : function(id){
55330         for(var i = 0, len = this.config.length; i < len; i++){
55331             if(this.config[i].id == id){
55332                 return i;
55333             }
55334         }
55335         return -1;
55336     },
55337     
55338     /**
55339      * Returns the index for a specified column dataIndex.
55340      * @param {String} dataIndex The column dataIndex
55341      * @return {Number} the index, or -1 if not found
55342      */
55343     
55344     findColumnIndex : function(dataIndex){
55345         for(var i = 0, len = this.config.length; i < len; i++){
55346             if(this.config[i].dataIndex == dataIndex){
55347                 return i;
55348             }
55349         }
55350         return -1;
55351     },
55352     
55353     
55354     moveColumn : function(oldIndex, newIndex){
55355         var c = this.config[oldIndex];
55356         this.config.splice(oldIndex, 1);
55357         this.config.splice(newIndex, 0, c);
55358         this.dataMap = null;
55359         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55360     },
55361
55362     isLocked : function(colIndex){
55363         return this.config[colIndex].locked === true;
55364     },
55365
55366     setLocked : function(colIndex, value, suppressEvent){
55367         if(this.isLocked(colIndex) == value){
55368             return;
55369         }
55370         this.config[colIndex].locked = value;
55371         if(!suppressEvent){
55372             this.fireEvent("columnlockchange", this, colIndex, value);
55373         }
55374     },
55375
55376     getTotalLockedWidth : function(){
55377         var totalWidth = 0;
55378         for(var i = 0; i < this.config.length; i++){
55379             if(this.isLocked(i) && !this.isHidden(i)){
55380                 this.totalWidth += this.getColumnWidth(i);
55381             }
55382         }
55383         return totalWidth;
55384     },
55385
55386     getLockedCount : function(){
55387         for(var i = 0, len = this.config.length; i < len; i++){
55388             if(!this.isLocked(i)){
55389                 return i;
55390             }
55391         }
55392     },
55393
55394     /**
55395      * Returns the number of columns.
55396      * @return {Number}
55397      */
55398     getColumnCount : function(visibleOnly){
55399         if(visibleOnly === true){
55400             var c = 0;
55401             for(var i = 0, len = this.config.length; i < len; i++){
55402                 if(!this.isHidden(i)){
55403                     c++;
55404                 }
55405             }
55406             return c;
55407         }
55408         return this.config.length;
55409     },
55410
55411     /**
55412      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55413      * @param {Function} fn
55414      * @param {Object} scope (optional)
55415      * @return {Array} result
55416      */
55417     getColumnsBy : function(fn, scope){
55418         var r = [];
55419         for(var i = 0, len = this.config.length; i < len; i++){
55420             var c = this.config[i];
55421             if(fn.call(scope||this, c, i) === true){
55422                 r[r.length] = c;
55423             }
55424         }
55425         return r;
55426     },
55427
55428     /**
55429      * Returns true if the specified column is sortable.
55430      * @param {Number} col The column index
55431      * @return {Boolean}
55432      */
55433     isSortable : function(col){
55434         if(typeof this.config[col].sortable == "undefined"){
55435             return this.defaultSortable;
55436         }
55437         return this.config[col].sortable;
55438     },
55439
55440     /**
55441      * Returns the rendering (formatting) function defined for the column.
55442      * @param {Number} col The column index.
55443      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55444      */
55445     getRenderer : function(col){
55446         if(!this.config[col].renderer){
55447             return Roo.grid.ColumnModel.defaultRenderer;
55448         }
55449         return this.config[col].renderer;
55450     },
55451
55452     /**
55453      * Sets the rendering (formatting) function for a column.
55454      * @param {Number} col The column index
55455      * @param {Function} fn The function to use to process the cell's raw data
55456      * to return HTML markup for the grid view. The render function is called with
55457      * the following parameters:<ul>
55458      * <li>Data value.</li>
55459      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55460      * <li>css A CSS style string to apply to the table cell.</li>
55461      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55462      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55463      * <li>Row index</li>
55464      * <li>Column index</li>
55465      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55466      */
55467     setRenderer : function(col, fn){
55468         this.config[col].renderer = fn;
55469     },
55470
55471     /**
55472      * Returns the width for the specified column.
55473      * @param {Number} col The column index
55474      * @return {Number}
55475      */
55476     getColumnWidth : function(col){
55477         return this.config[col].width * 1 || this.defaultWidth;
55478     },
55479
55480     /**
55481      * Sets the width for a column.
55482      * @param {Number} col The column index
55483      * @param {Number} width The new width
55484      */
55485     setColumnWidth : function(col, width, suppressEvent){
55486         this.config[col].width = width;
55487         this.totalWidth = null;
55488         if(!suppressEvent){
55489              this.fireEvent("widthchange", this, col, width);
55490         }
55491     },
55492
55493     /**
55494      * Returns the total width of all columns.
55495      * @param {Boolean} includeHidden True to include hidden column widths
55496      * @return {Number}
55497      */
55498     getTotalWidth : function(includeHidden){
55499         if(!this.totalWidth){
55500             this.totalWidth = 0;
55501             for(var i = 0, len = this.config.length; i < len; i++){
55502                 if(includeHidden || !this.isHidden(i)){
55503                     this.totalWidth += this.getColumnWidth(i);
55504                 }
55505             }
55506         }
55507         return this.totalWidth;
55508     },
55509
55510     /**
55511      * Returns the header for the specified column.
55512      * @param {Number} col The column index
55513      * @return {String}
55514      */
55515     getColumnHeader : function(col){
55516         return this.config[col].header;
55517     },
55518
55519     /**
55520      * Sets the header for a column.
55521      * @param {Number} col The column index
55522      * @param {String} header The new header
55523      */
55524     setColumnHeader : function(col, header){
55525         this.config[col].header = header;
55526         this.fireEvent("headerchange", this, col, header);
55527     },
55528
55529     /**
55530      * Returns the tooltip for the specified column.
55531      * @param {Number} col The column index
55532      * @return {String}
55533      */
55534     getColumnTooltip : function(col){
55535             return this.config[col].tooltip;
55536     },
55537     /**
55538      * Sets the tooltip for a column.
55539      * @param {Number} col The column index
55540      * @param {String} tooltip The new tooltip
55541      */
55542     setColumnTooltip : function(col, tooltip){
55543             this.config[col].tooltip = tooltip;
55544     },
55545
55546     /**
55547      * Returns the dataIndex for the specified column.
55548      * @param {Number} col The column index
55549      * @return {Number}
55550      */
55551     getDataIndex : function(col){
55552         return this.config[col].dataIndex;
55553     },
55554
55555     /**
55556      * Sets the dataIndex for a column.
55557      * @param {Number} col The column index
55558      * @param {Number} dataIndex The new dataIndex
55559      */
55560     setDataIndex : function(col, dataIndex){
55561         this.config[col].dataIndex = dataIndex;
55562     },
55563
55564     
55565     
55566     /**
55567      * Returns true if the cell is editable.
55568      * @param {Number} colIndex The column index
55569      * @param {Number} rowIndex The row index
55570      * @return {Boolean}
55571      */
55572     isCellEditable : function(colIndex, rowIndex){
55573         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55574     },
55575
55576     /**
55577      * Returns the editor defined for the cell/column.
55578      * return false or null to disable editing.
55579      * @param {Number} colIndex The column index
55580      * @param {Number} rowIndex The row index
55581      * @return {Object}
55582      */
55583     getCellEditor : function(colIndex, rowIndex){
55584         return this.config[colIndex].editor;
55585     },
55586
55587     /**
55588      * Sets if a column is editable.
55589      * @param {Number} col The column index
55590      * @param {Boolean} editable True if the column is editable
55591      */
55592     setEditable : function(col, editable){
55593         this.config[col].editable = editable;
55594     },
55595
55596
55597     /**
55598      * Returns true if the column is hidden.
55599      * @param {Number} colIndex The column index
55600      * @return {Boolean}
55601      */
55602     isHidden : function(colIndex){
55603         return this.config[colIndex].hidden;
55604     },
55605
55606
55607     /**
55608      * Returns true if the column width cannot be changed
55609      */
55610     isFixed : function(colIndex){
55611         return this.config[colIndex].fixed;
55612     },
55613
55614     /**
55615      * Returns true if the column can be resized
55616      * @return {Boolean}
55617      */
55618     isResizable : function(colIndex){
55619         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55620     },
55621     /**
55622      * Sets if a column is hidden.
55623      * @param {Number} colIndex The column index
55624      * @param {Boolean} hidden True if the column is hidden
55625      */
55626     setHidden : function(colIndex, hidden){
55627         this.config[colIndex].hidden = hidden;
55628         this.totalWidth = null;
55629         this.fireEvent("hiddenchange", this, colIndex, hidden);
55630     },
55631
55632     /**
55633      * Sets the editor for a column.
55634      * @param {Number} col The column index
55635      * @param {Object} editor The editor object
55636      */
55637     setEditor : function(col, editor){
55638         this.config[col].editor = editor;
55639     }
55640 });
55641
55642 Roo.grid.ColumnModel.defaultRenderer = function(value){
55643         if(typeof value == "string" && value.length < 1){
55644             return "&#160;";
55645         }
55646         return value;
55647 };
55648
55649 // Alias for backwards compatibility
55650 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55651 /*
55652  * Based on:
55653  * Ext JS Library 1.1.1
55654  * Copyright(c) 2006-2007, Ext JS, LLC.
55655  *
55656  * Originally Released Under LGPL - original licence link has changed is not relivant.
55657  *
55658  * Fork - LGPL
55659  * <script type="text/javascript">
55660  */
55661
55662 /**
55663  * @class Roo.grid.AbstractSelectionModel
55664  * @extends Roo.util.Observable
55665  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55666  * implemented by descendant classes.  This class should not be directly instantiated.
55667  * @constructor
55668  */
55669 Roo.grid.AbstractSelectionModel = function(){
55670     this.locked = false;
55671     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55672 };
55673
55674 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55675     /** @ignore Called by the grid automatically. Do not call directly. */
55676     init : function(grid){
55677         this.grid = grid;
55678         this.initEvents();
55679     },
55680
55681     /**
55682      * Locks the selections.
55683      */
55684     lock : function(){
55685         this.locked = true;
55686     },
55687
55688     /**
55689      * Unlocks the selections.
55690      */
55691     unlock : function(){
55692         this.locked = false;
55693     },
55694
55695     /**
55696      * Returns true if the selections are locked.
55697      * @return {Boolean}
55698      */
55699     isLocked : function(){
55700         return this.locked;
55701     }
55702 });/*
55703  * Based on:
55704  * Ext JS Library 1.1.1
55705  * Copyright(c) 2006-2007, Ext JS, LLC.
55706  *
55707  * Originally Released Under LGPL - original licence link has changed is not relivant.
55708  *
55709  * Fork - LGPL
55710  * <script type="text/javascript">
55711  */
55712 /**
55713  * @extends Roo.grid.AbstractSelectionModel
55714  * @class Roo.grid.RowSelectionModel
55715  * The default SelectionModel used by {@link Roo.grid.Grid}.
55716  * It supports multiple selections and keyboard selection/navigation. 
55717  * @constructor
55718  * @param {Object} config
55719  */
55720 Roo.grid.RowSelectionModel = function(config){
55721     Roo.apply(this, config);
55722     this.selections = new Roo.util.MixedCollection(false, function(o){
55723         return o.id;
55724     });
55725
55726     this.last = false;
55727     this.lastActive = false;
55728
55729     this.addEvents({
55730         /**
55731              * @event selectionchange
55732              * Fires when the selection changes
55733              * @param {SelectionModel} this
55734              */
55735             "selectionchange" : true,
55736         /**
55737              * @event afterselectionchange
55738              * Fires after the selection changes (eg. by key press or clicking)
55739              * @param {SelectionModel} this
55740              */
55741             "afterselectionchange" : true,
55742         /**
55743              * @event beforerowselect
55744              * Fires when a row is selected being selected, return false to cancel.
55745              * @param {SelectionModel} this
55746              * @param {Number} rowIndex The selected index
55747              * @param {Boolean} keepExisting False if other selections will be cleared
55748              */
55749             "beforerowselect" : true,
55750         /**
55751              * @event rowselect
55752              * Fires when a row is selected.
55753              * @param {SelectionModel} this
55754              * @param {Number} rowIndex The selected index
55755              * @param {Roo.data.Record} r The record
55756              */
55757             "rowselect" : true,
55758         /**
55759              * @event rowdeselect
55760              * Fires when a row is deselected.
55761              * @param {SelectionModel} this
55762              * @param {Number} rowIndex The selected index
55763              */
55764         "rowdeselect" : true
55765     });
55766     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55767     this.locked = false;
55768 };
55769
55770 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55771     /**
55772      * @cfg {Boolean} singleSelect
55773      * True to allow selection of only one row at a time (defaults to false)
55774      */
55775     singleSelect : false,
55776
55777     // private
55778     initEvents : function(){
55779
55780         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55781             this.grid.on("mousedown", this.handleMouseDown, this);
55782         }else{ // allow click to work like normal
55783             this.grid.on("rowclick", this.handleDragableRowClick, this);
55784         }
55785
55786         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55787             "up" : function(e){
55788                 if(!e.shiftKey){
55789                     this.selectPrevious(e.shiftKey);
55790                 }else if(this.last !== false && this.lastActive !== false){
55791                     var last = this.last;
55792                     this.selectRange(this.last,  this.lastActive-1);
55793                     this.grid.getView().focusRow(this.lastActive);
55794                     if(last !== false){
55795                         this.last = last;
55796                     }
55797                 }else{
55798                     this.selectFirstRow();
55799                 }
55800                 this.fireEvent("afterselectionchange", this);
55801             },
55802             "down" : function(e){
55803                 if(!e.shiftKey){
55804                     this.selectNext(e.shiftKey);
55805                 }else if(this.last !== false && this.lastActive !== false){
55806                     var last = this.last;
55807                     this.selectRange(this.last,  this.lastActive+1);
55808                     this.grid.getView().focusRow(this.lastActive);
55809                     if(last !== false){
55810                         this.last = last;
55811                     }
55812                 }else{
55813                     this.selectFirstRow();
55814                 }
55815                 this.fireEvent("afterselectionchange", this);
55816             },
55817             scope: this
55818         });
55819
55820         var view = this.grid.view;
55821         view.on("refresh", this.onRefresh, this);
55822         view.on("rowupdated", this.onRowUpdated, this);
55823         view.on("rowremoved", this.onRemove, this);
55824     },
55825
55826     // private
55827     onRefresh : function(){
55828         var ds = this.grid.dataSource, i, v = this.grid.view;
55829         var s = this.selections;
55830         s.each(function(r){
55831             if((i = ds.indexOfId(r.id)) != -1){
55832                 v.onRowSelect(i);
55833             }else{
55834                 s.remove(r);
55835             }
55836         });
55837     },
55838
55839     // private
55840     onRemove : function(v, index, r){
55841         this.selections.remove(r);
55842     },
55843
55844     // private
55845     onRowUpdated : function(v, index, r){
55846         if(this.isSelected(r)){
55847             v.onRowSelect(index);
55848         }
55849     },
55850
55851     /**
55852      * Select records.
55853      * @param {Array} records The records to select
55854      * @param {Boolean} keepExisting (optional) True to keep existing selections
55855      */
55856     selectRecords : function(records, keepExisting){
55857         if(!keepExisting){
55858             this.clearSelections();
55859         }
55860         var ds = this.grid.dataSource;
55861         for(var i = 0, len = records.length; i < len; i++){
55862             this.selectRow(ds.indexOf(records[i]), true);
55863         }
55864     },
55865
55866     /**
55867      * Gets the number of selected rows.
55868      * @return {Number}
55869      */
55870     getCount : function(){
55871         return this.selections.length;
55872     },
55873
55874     /**
55875      * Selects the first row in the grid.
55876      */
55877     selectFirstRow : function(){
55878         this.selectRow(0);
55879     },
55880
55881     /**
55882      * Select the last row.
55883      * @param {Boolean} keepExisting (optional) True to keep existing selections
55884      */
55885     selectLastRow : function(keepExisting){
55886         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55887     },
55888
55889     /**
55890      * Selects the row immediately following the last selected row.
55891      * @param {Boolean} keepExisting (optional) True to keep existing selections
55892      */
55893     selectNext : function(keepExisting){
55894         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55895             this.selectRow(this.last+1, keepExisting);
55896             this.grid.getView().focusRow(this.last);
55897         }
55898     },
55899
55900     /**
55901      * Selects the row that precedes the last selected row.
55902      * @param {Boolean} keepExisting (optional) True to keep existing selections
55903      */
55904     selectPrevious : function(keepExisting){
55905         if(this.last){
55906             this.selectRow(this.last-1, keepExisting);
55907             this.grid.getView().focusRow(this.last);
55908         }
55909     },
55910
55911     /**
55912      * Returns the selected records
55913      * @return {Array} Array of selected records
55914      */
55915     getSelections : function(){
55916         return [].concat(this.selections.items);
55917     },
55918
55919     /**
55920      * Returns the first selected record.
55921      * @return {Record}
55922      */
55923     getSelected : function(){
55924         return this.selections.itemAt(0);
55925     },
55926
55927
55928     /**
55929      * Clears all selections.
55930      */
55931     clearSelections : function(fast){
55932         if(this.locked) return;
55933         if(fast !== true){
55934             var ds = this.grid.dataSource;
55935             var s = this.selections;
55936             s.each(function(r){
55937                 this.deselectRow(ds.indexOfId(r.id));
55938             }, this);
55939             s.clear();
55940         }else{
55941             this.selections.clear();
55942         }
55943         this.last = false;
55944     },
55945
55946
55947     /**
55948      * Selects all rows.
55949      */
55950     selectAll : function(){
55951         if(this.locked) return;
55952         this.selections.clear();
55953         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55954             this.selectRow(i, true);
55955         }
55956     },
55957
55958     /**
55959      * Returns True if there is a selection.
55960      * @return {Boolean}
55961      */
55962     hasSelection : function(){
55963         return this.selections.length > 0;
55964     },
55965
55966     /**
55967      * Returns True if the specified row is selected.
55968      * @param {Number/Record} record The record or index of the record to check
55969      * @return {Boolean}
55970      */
55971     isSelected : function(index){
55972         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55973         return (r && this.selections.key(r.id) ? true : false);
55974     },
55975
55976     /**
55977      * Returns True if the specified record id is selected.
55978      * @param {String} id The id of record to check
55979      * @return {Boolean}
55980      */
55981     isIdSelected : function(id){
55982         return (this.selections.key(id) ? true : false);
55983     },
55984
55985     // private
55986     handleMouseDown : function(e, t){
55987         var view = this.grid.getView(), rowIndex;
55988         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55989             return;
55990         };
55991         if(e.shiftKey && this.last !== false){
55992             var last = this.last;
55993             this.selectRange(last, rowIndex, e.ctrlKey);
55994             this.last = last; // reset the last
55995             view.focusRow(rowIndex);
55996         }else{
55997             var isSelected = this.isSelected(rowIndex);
55998             if(e.button !== 0 && isSelected){
55999                 view.focusRow(rowIndex);
56000             }else if(e.ctrlKey && isSelected){
56001                 this.deselectRow(rowIndex);
56002             }else if(!isSelected){
56003                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56004                 view.focusRow(rowIndex);
56005             }
56006         }
56007         this.fireEvent("afterselectionchange", this);
56008     },
56009     // private
56010     handleDragableRowClick :  function(grid, rowIndex, e) 
56011     {
56012         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56013             this.selectRow(rowIndex, false);
56014             grid.view.focusRow(rowIndex);
56015              this.fireEvent("afterselectionchange", this);
56016         }
56017     },
56018     
56019     /**
56020      * Selects multiple rows.
56021      * @param {Array} rows Array of the indexes of the row to select
56022      * @param {Boolean} keepExisting (optional) True to keep existing selections
56023      */
56024     selectRows : function(rows, keepExisting){
56025         if(!keepExisting){
56026             this.clearSelections();
56027         }
56028         for(var i = 0, len = rows.length; i < len; i++){
56029             this.selectRow(rows[i], true);
56030         }
56031     },
56032
56033     /**
56034      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56035      * @param {Number} startRow The index of the first row in the range
56036      * @param {Number} endRow The index of the last row in the range
56037      * @param {Boolean} keepExisting (optional) True to retain existing selections
56038      */
56039     selectRange : function(startRow, endRow, keepExisting){
56040         if(this.locked) return;
56041         if(!keepExisting){
56042             this.clearSelections();
56043         }
56044         if(startRow <= endRow){
56045             for(var i = startRow; i <= endRow; i++){
56046                 this.selectRow(i, true);
56047             }
56048         }else{
56049             for(var i = startRow; i >= endRow; i--){
56050                 this.selectRow(i, true);
56051             }
56052         }
56053     },
56054
56055     /**
56056      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56057      * @param {Number} startRow The index of the first row in the range
56058      * @param {Number} endRow The index of the last row in the range
56059      */
56060     deselectRange : function(startRow, endRow, preventViewNotify){
56061         if(this.locked) return;
56062         for(var i = startRow; i <= endRow; i++){
56063             this.deselectRow(i, preventViewNotify);
56064         }
56065     },
56066
56067     /**
56068      * Selects a row.
56069      * @param {Number} row The index of the row to select
56070      * @param {Boolean} keepExisting (optional) True to keep existing selections
56071      */
56072     selectRow : function(index, keepExisting, preventViewNotify){
56073         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56074         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56075             if(!keepExisting || this.singleSelect){
56076                 this.clearSelections();
56077             }
56078             var r = this.grid.dataSource.getAt(index);
56079             this.selections.add(r);
56080             this.last = this.lastActive = index;
56081             if(!preventViewNotify){
56082                 this.grid.getView().onRowSelect(index);
56083             }
56084             this.fireEvent("rowselect", this, index, r);
56085             this.fireEvent("selectionchange", this);
56086         }
56087     },
56088
56089     /**
56090      * Deselects a row.
56091      * @param {Number} row The index of the row to deselect
56092      */
56093     deselectRow : function(index, preventViewNotify){
56094         if(this.locked) return;
56095         if(this.last == index){
56096             this.last = false;
56097         }
56098         if(this.lastActive == index){
56099             this.lastActive = false;
56100         }
56101         var r = this.grid.dataSource.getAt(index);
56102         this.selections.remove(r);
56103         if(!preventViewNotify){
56104             this.grid.getView().onRowDeselect(index);
56105         }
56106         this.fireEvent("rowdeselect", this, index);
56107         this.fireEvent("selectionchange", this);
56108     },
56109
56110     // private
56111     restoreLast : function(){
56112         if(this._last){
56113             this.last = this._last;
56114         }
56115     },
56116
56117     // private
56118     acceptsNav : function(row, col, cm){
56119         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56120     },
56121
56122     // private
56123     onEditorKey : function(field, e){
56124         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56125         if(k == e.TAB){
56126             e.stopEvent();
56127             ed.completeEdit();
56128             if(e.shiftKey){
56129                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56130             }else{
56131                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56132             }
56133         }else if(k == e.ENTER && !e.ctrlKey){
56134             e.stopEvent();
56135             ed.completeEdit();
56136             if(e.shiftKey){
56137                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56138             }else{
56139                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56140             }
56141         }else if(k == e.ESC){
56142             ed.cancelEdit();
56143         }
56144         if(newCell){
56145             g.startEditing(newCell[0], newCell[1]);
56146         }
56147     }
56148 });/*
56149  * Based on:
56150  * Ext JS Library 1.1.1
56151  * Copyright(c) 2006-2007, Ext JS, LLC.
56152  *
56153  * Originally Released Under LGPL - original licence link has changed is not relivant.
56154  *
56155  * Fork - LGPL
56156  * <script type="text/javascript">
56157  */
56158 /**
56159  * @class Roo.grid.CellSelectionModel
56160  * @extends Roo.grid.AbstractSelectionModel
56161  * This class provides the basic implementation for cell selection in a grid.
56162  * @constructor
56163  * @param {Object} config The object containing the configuration of this model.
56164  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56165  */
56166 Roo.grid.CellSelectionModel = function(config){
56167     Roo.apply(this, config);
56168
56169     this.selection = null;
56170
56171     this.addEvents({
56172         /**
56173              * @event beforerowselect
56174              * Fires before a cell is selected.
56175              * @param {SelectionModel} this
56176              * @param {Number} rowIndex The selected row index
56177              * @param {Number} colIndex The selected cell index
56178              */
56179             "beforecellselect" : true,
56180         /**
56181              * @event cellselect
56182              * Fires when a cell is selected.
56183              * @param {SelectionModel} this
56184              * @param {Number} rowIndex The selected row index
56185              * @param {Number} colIndex The selected cell index
56186              */
56187             "cellselect" : true,
56188         /**
56189              * @event selectionchange
56190              * Fires when the active selection changes.
56191              * @param {SelectionModel} this
56192              * @param {Object} selection null for no selection or an object (o) with two properties
56193                 <ul>
56194                 <li>o.record: the record object for the row the selection is in</li>
56195                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56196                 </ul>
56197              */
56198             "selectionchange" : true,
56199         /**
56200              * @event tabend
56201              * Fires when the tab (or enter) was pressed on the last editable cell
56202              * You can use this to trigger add new row.
56203              * @param {SelectionModel} this
56204              */
56205             "tabend" : true,
56206          /**
56207              * @event beforeeditnext
56208              * Fires before the next editable sell is made active
56209              * You can use this to skip to another cell or fire the tabend
56210              *    if you set cell to false
56211              * @param {Object} eventdata object : { cell : [ row, col ] } 
56212              */
56213             "beforeeditnext" : true
56214     });
56215     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56216 };
56217
56218 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56219     
56220     enter_is_tab: false,
56221
56222     /** @ignore */
56223     initEvents : function(){
56224         this.grid.on("mousedown", this.handleMouseDown, this);
56225         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56226         var view = this.grid.view;
56227         view.on("refresh", this.onViewChange, this);
56228         view.on("rowupdated", this.onRowUpdated, this);
56229         view.on("beforerowremoved", this.clearSelections, this);
56230         view.on("beforerowsinserted", this.clearSelections, this);
56231         if(this.grid.isEditor){
56232             this.grid.on("beforeedit", this.beforeEdit,  this);
56233         }
56234     },
56235
56236         //private
56237     beforeEdit : function(e){
56238         this.select(e.row, e.column, false, true, e.record);
56239     },
56240
56241         //private
56242     onRowUpdated : function(v, index, r){
56243         if(this.selection && this.selection.record == r){
56244             v.onCellSelect(index, this.selection.cell[1]);
56245         }
56246     },
56247
56248         //private
56249     onViewChange : function(){
56250         this.clearSelections(true);
56251     },
56252
56253         /**
56254          * Returns the currently selected cell,.
56255          * @return {Array} The selected cell (row, column) or null if none selected.
56256          */
56257     getSelectedCell : function(){
56258         return this.selection ? this.selection.cell : null;
56259     },
56260
56261     /**
56262      * Clears all selections.
56263      * @param {Boolean} true to prevent the gridview from being notified about the change.
56264      */
56265     clearSelections : function(preventNotify){
56266         var s = this.selection;
56267         if(s){
56268             if(preventNotify !== true){
56269                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56270             }
56271             this.selection = null;
56272             this.fireEvent("selectionchange", this, null);
56273         }
56274     },
56275
56276     /**
56277      * Returns true if there is a selection.
56278      * @return {Boolean}
56279      */
56280     hasSelection : function(){
56281         return this.selection ? true : false;
56282     },
56283
56284     /** @ignore */
56285     handleMouseDown : function(e, t){
56286         var v = this.grid.getView();
56287         if(this.isLocked()){
56288             return;
56289         };
56290         var row = v.findRowIndex(t);
56291         var cell = v.findCellIndex(t);
56292         if(row !== false && cell !== false){
56293             this.select(row, cell);
56294         }
56295     },
56296
56297     /**
56298      * Selects a cell.
56299      * @param {Number} rowIndex
56300      * @param {Number} collIndex
56301      */
56302     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56303         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56304             this.clearSelections();
56305             r = r || this.grid.dataSource.getAt(rowIndex);
56306             this.selection = {
56307                 record : r,
56308                 cell : [rowIndex, colIndex]
56309             };
56310             if(!preventViewNotify){
56311                 var v = this.grid.getView();
56312                 v.onCellSelect(rowIndex, colIndex);
56313                 if(preventFocus !== true){
56314                     v.focusCell(rowIndex, colIndex);
56315                 }
56316             }
56317             this.fireEvent("cellselect", this, rowIndex, colIndex);
56318             this.fireEvent("selectionchange", this, this.selection);
56319         }
56320     },
56321
56322         //private
56323     isSelectable : function(rowIndex, colIndex, cm){
56324         return !cm.isHidden(colIndex);
56325     },
56326
56327     /** @ignore */
56328     handleKeyDown : function(e){
56329         //Roo.log('Cell Sel Model handleKeyDown');
56330         if(!e.isNavKeyPress()){
56331             return;
56332         }
56333         var g = this.grid, s = this.selection;
56334         if(!s){
56335             e.stopEvent();
56336             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56337             if(cell){
56338                 this.select(cell[0], cell[1]);
56339             }
56340             return;
56341         }
56342         var sm = this;
56343         var walk = function(row, col, step){
56344             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56345         };
56346         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56347         var newCell;
56348
56349       
56350
56351         switch(k){
56352             case e.TAB:
56353                 // handled by onEditorKey
56354                 if (g.isEditor && g.editing) {
56355                     return;
56356                 }
56357                 if(e.shiftKey) {
56358                     newCell = walk(r, c-1, -1);
56359                 } else {
56360                     newCell = walk(r, c+1, 1);
56361                 }
56362                 break;
56363             
56364             case e.DOWN:
56365                newCell = walk(r+1, c, 1);
56366                 break;
56367             
56368             case e.UP:
56369                 newCell = walk(r-1, c, -1);
56370                 break;
56371             
56372             case e.RIGHT:
56373                 newCell = walk(r, c+1, 1);
56374                 break;
56375             
56376             case e.LEFT:
56377                 newCell = walk(r, c-1, -1);
56378                 break;
56379             
56380             case e.ENTER:
56381                 
56382                 if(g.isEditor && !g.editing){
56383                    g.startEditing(r, c);
56384                    e.stopEvent();
56385                    return;
56386                 }
56387                 
56388                 
56389              break;
56390         };
56391         if(newCell){
56392             this.select(newCell[0], newCell[1]);
56393             e.stopEvent();
56394             
56395         }
56396     },
56397
56398     acceptsNav : function(row, col, cm){
56399         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56400     },
56401     /**
56402      * Selects a cell.
56403      * @param {Number} field (not used) - as it's normally used as a listener
56404      * @param {Number} e - event - fake it by using
56405      *
56406      * var e = Roo.EventObjectImpl.prototype;
56407      * e.keyCode = e.TAB
56408      *
56409      * 
56410      */
56411     onEditorKey : function(field, e){
56412         
56413         var k = e.getKey(),
56414             newCell,
56415             g = this.grid,
56416             ed = g.activeEditor,
56417             forward = false;
56418         ///Roo.log('onEditorKey' + k);
56419         
56420         
56421         if (this.enter_is_tab && k == e.ENTER) {
56422             k = e.TAB;
56423         }
56424         
56425         if(k == e.TAB){
56426             if(e.shiftKey){
56427                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56428             }else{
56429                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56430                 forward = true;
56431             }
56432             
56433             e.stopEvent();
56434             
56435         } else if(k == e.ENTER &&  !e.ctrlKey){
56436             ed.completeEdit();
56437             e.stopEvent();
56438             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56439         
56440                 } else if(k == e.ESC){
56441             ed.cancelEdit();
56442         }
56443                 
56444         if (newCell) {
56445             var ecall = { cell : newCell, forward : forward };
56446             this.fireEvent('beforeeditnext', ecall );
56447             newCell = ecall.cell;
56448                         forward = ecall.forward;
56449         }
56450                 
56451         if(newCell){
56452             //Roo.log('next cell after edit');
56453             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56454         } else if (forward) {
56455             // tabbed past last
56456             this.fireEvent.defer(100, this, ['tabend',this]);
56457         }
56458     }
56459 });/*
56460  * Based on:
56461  * Ext JS Library 1.1.1
56462  * Copyright(c) 2006-2007, Ext JS, LLC.
56463  *
56464  * Originally Released Under LGPL - original licence link has changed is not relivant.
56465  *
56466  * Fork - LGPL
56467  * <script type="text/javascript">
56468  */
56469  
56470 /**
56471  * @class Roo.grid.EditorGrid
56472  * @extends Roo.grid.Grid
56473  * Class for creating and editable grid.
56474  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56475  * The container MUST have some type of size defined for the grid to fill. The container will be 
56476  * automatically set to position relative if it isn't already.
56477  * @param {Object} dataSource The data model to bind to
56478  * @param {Object} colModel The column model with info about this grid's columns
56479  */
56480 Roo.grid.EditorGrid = function(container, config){
56481     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56482     this.getGridEl().addClass("xedit-grid");
56483
56484     if(!this.selModel){
56485         this.selModel = new Roo.grid.CellSelectionModel();
56486     }
56487
56488     this.activeEditor = null;
56489
56490         this.addEvents({
56491             /**
56492              * @event beforeedit
56493              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56494              * <ul style="padding:5px;padding-left:16px;">
56495              * <li>grid - This grid</li>
56496              * <li>record - The record being edited</li>
56497              * <li>field - The field name being edited</li>
56498              * <li>value - The value for the field being edited.</li>
56499              * <li>row - The grid row index</li>
56500              * <li>column - The grid column index</li>
56501              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56502              * </ul>
56503              * @param {Object} e An edit event (see above for description)
56504              */
56505             "beforeedit" : true,
56506             /**
56507              * @event afteredit
56508              * Fires after a cell is edited. <br />
56509              * <ul style="padding:5px;padding-left:16px;">
56510              * <li>grid - This grid</li>
56511              * <li>record - The record being edited</li>
56512              * <li>field - The field name being edited</li>
56513              * <li>value - The value being set</li>
56514              * <li>originalValue - The original value for the field, before the edit.</li>
56515              * <li>row - The grid row index</li>
56516              * <li>column - The grid column index</li>
56517              * </ul>
56518              * @param {Object} e An edit event (see above for description)
56519              */
56520             "afteredit" : true,
56521             /**
56522              * @event validateedit
56523              * Fires after a cell is edited, but before the value is set in the record. 
56524          * You can use this to modify the value being set in the field, Return false
56525              * to cancel the change. The edit event object has the following properties <br />
56526              * <ul style="padding:5px;padding-left:16px;">
56527          * <li>editor - This editor</li>
56528              * <li>grid - This grid</li>
56529              * <li>record - The record being edited</li>
56530              * <li>field - The field name being edited</li>
56531              * <li>value - The value being set</li>
56532              * <li>originalValue - The original value for the field, before the edit.</li>
56533              * <li>row - The grid row index</li>
56534              * <li>column - The grid column index</li>
56535              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56536              * </ul>
56537              * @param {Object} e An edit event (see above for description)
56538              */
56539             "validateedit" : true
56540         });
56541     this.on("bodyscroll", this.stopEditing,  this);
56542     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56543 };
56544
56545 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56546     /**
56547      * @cfg {Number} clicksToEdit
56548      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56549      */
56550     clicksToEdit: 2,
56551
56552     // private
56553     isEditor : true,
56554     // private
56555     trackMouseOver: false, // causes very odd FF errors
56556
56557     onCellDblClick : function(g, row, col){
56558         this.startEditing(row, col);
56559     },
56560
56561     onEditComplete : function(ed, value, startValue){
56562         this.editing = false;
56563         this.activeEditor = null;
56564         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56565         var r = ed.record;
56566         var field = this.colModel.getDataIndex(ed.col);
56567         var e = {
56568             grid: this,
56569             record: r,
56570             field: field,
56571             originalValue: startValue,
56572             value: value,
56573             row: ed.row,
56574             column: ed.col,
56575             cancel:false,
56576             editor: ed
56577         };
56578         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56579         cell.show();
56580           
56581         if(String(value) !== String(startValue)){
56582             
56583             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56584                 r.set(field, e.value);
56585                 // if we are dealing with a combo box..
56586                 // then we also set the 'name' colum to be the displayField
56587                 if (ed.field.displayField && ed.field.name) {
56588                     r.set(ed.field.name, ed.field.el.dom.value);
56589                 }
56590                 
56591                 delete e.cancel; //?? why!!!
56592                 this.fireEvent("afteredit", e);
56593             }
56594         } else {
56595             this.fireEvent("afteredit", e); // always fire it!
56596         }
56597         this.view.focusCell(ed.row, ed.col);
56598     },
56599
56600     /**
56601      * Starts editing the specified for the specified row/column
56602      * @param {Number} rowIndex
56603      * @param {Number} colIndex
56604      */
56605     startEditing : function(row, col){
56606         this.stopEditing();
56607         if(this.colModel.isCellEditable(col, row)){
56608             this.view.ensureVisible(row, col, true);
56609           
56610             var r = this.dataSource.getAt(row);
56611             var field = this.colModel.getDataIndex(col);
56612             var cell = Roo.get(this.view.getCell(row,col));
56613             var e = {
56614                 grid: this,
56615                 record: r,
56616                 field: field,
56617                 value: r.data[field],
56618                 row: row,
56619                 column: col,
56620                 cancel:false 
56621             };
56622             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56623                 this.editing = true;
56624                 var ed = this.colModel.getCellEditor(col, row);
56625                 
56626                 if (!ed) {
56627                     return;
56628                 }
56629                 if(!ed.rendered){
56630                     ed.render(ed.parentEl || document.body);
56631                 }
56632                 ed.field.reset();
56633                
56634                 cell.hide();
56635                 
56636                 (function(){ // complex but required for focus issues in safari, ie and opera
56637                     ed.row = row;
56638                     ed.col = col;
56639                     ed.record = r;
56640                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56641                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56642                     this.activeEditor = ed;
56643                     var v = r.data[field];
56644                     ed.startEdit(this.view.getCell(row, col), v);
56645                     // combo's with 'displayField and name set
56646                     if (ed.field.displayField && ed.field.name) {
56647                         ed.field.el.dom.value = r.data[ed.field.name];
56648                     }
56649                     
56650                     
56651                 }).defer(50, this);
56652             }
56653         }
56654     },
56655         
56656     /**
56657      * Stops any active editing
56658      */
56659     stopEditing : function(){
56660         if(this.activeEditor){
56661             this.activeEditor.completeEdit();
56662         }
56663         this.activeEditor = null;
56664     },
56665         
56666          /**
56667      * Called to get grid's drag proxy text, by default returns this.ddText.
56668      * @return {String}
56669      */
56670     getDragDropText : function(){
56671         var count = this.selModel.getSelectedCell() ? 1 : 0;
56672         return String.format(this.ddText, count, count == 1 ? '' : 's');
56673     }
56674         
56675 });/*
56676  * Based on:
56677  * Ext JS Library 1.1.1
56678  * Copyright(c) 2006-2007, Ext JS, LLC.
56679  *
56680  * Originally Released Under LGPL - original licence link has changed is not relivant.
56681  *
56682  * Fork - LGPL
56683  * <script type="text/javascript">
56684  */
56685
56686 // private - not really -- you end up using it !
56687 // This is a support class used internally by the Grid components
56688
56689 /**
56690  * @class Roo.grid.GridEditor
56691  * @extends Roo.Editor
56692  * Class for creating and editable grid elements.
56693  * @param {Object} config any settings (must include field)
56694  */
56695 Roo.grid.GridEditor = function(field, config){
56696     if (!config && field.field) {
56697         config = field;
56698         field = Roo.factory(config.field, Roo.form);
56699     }
56700     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56701     field.monitorTab = false;
56702 };
56703
56704 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56705     
56706     /**
56707      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56708      */
56709     
56710     alignment: "tl-tl",
56711     autoSize: "width",
56712     hideEl : false,
56713     cls: "x-small-editor x-grid-editor",
56714     shim:false,
56715     shadow:"frame"
56716 });/*
56717  * Based on:
56718  * Ext JS Library 1.1.1
56719  * Copyright(c) 2006-2007, Ext JS, LLC.
56720  *
56721  * Originally Released Under LGPL - original licence link has changed is not relivant.
56722  *
56723  * Fork - LGPL
56724  * <script type="text/javascript">
56725  */
56726   
56727
56728   
56729 Roo.grid.PropertyRecord = Roo.data.Record.create([
56730     {name:'name',type:'string'},  'value'
56731 ]);
56732
56733
56734 Roo.grid.PropertyStore = function(grid, source){
56735     this.grid = grid;
56736     this.store = new Roo.data.Store({
56737         recordType : Roo.grid.PropertyRecord
56738     });
56739     this.store.on('update', this.onUpdate,  this);
56740     if(source){
56741         this.setSource(source);
56742     }
56743     Roo.grid.PropertyStore.superclass.constructor.call(this);
56744 };
56745
56746
56747
56748 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56749     setSource : function(o){
56750         this.source = o;
56751         this.store.removeAll();
56752         var data = [];
56753         for(var k in o){
56754             if(this.isEditableValue(o[k])){
56755                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56756             }
56757         }
56758         this.store.loadRecords({records: data}, {}, true);
56759     },
56760
56761     onUpdate : function(ds, record, type){
56762         if(type == Roo.data.Record.EDIT){
56763             var v = record.data['value'];
56764             var oldValue = record.modified['value'];
56765             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56766                 this.source[record.id] = v;
56767                 record.commit();
56768                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56769             }else{
56770                 record.reject();
56771             }
56772         }
56773     },
56774
56775     getProperty : function(row){
56776        return this.store.getAt(row);
56777     },
56778
56779     isEditableValue: function(val){
56780         if(val && val instanceof Date){
56781             return true;
56782         }else if(typeof val == 'object' || typeof val == 'function'){
56783             return false;
56784         }
56785         return true;
56786     },
56787
56788     setValue : function(prop, value){
56789         this.source[prop] = value;
56790         this.store.getById(prop).set('value', value);
56791     },
56792
56793     getSource : function(){
56794         return this.source;
56795     }
56796 });
56797
56798 Roo.grid.PropertyColumnModel = function(grid, store){
56799     this.grid = grid;
56800     var g = Roo.grid;
56801     g.PropertyColumnModel.superclass.constructor.call(this, [
56802         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56803         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56804     ]);
56805     this.store = store;
56806     this.bselect = Roo.DomHelper.append(document.body, {
56807         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56808             {tag: 'option', value: 'true', html: 'true'},
56809             {tag: 'option', value: 'false', html: 'false'}
56810         ]
56811     });
56812     Roo.id(this.bselect);
56813     var f = Roo.form;
56814     this.editors = {
56815         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56816         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56817         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56818         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56819         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56820     };
56821     this.renderCellDelegate = this.renderCell.createDelegate(this);
56822     this.renderPropDelegate = this.renderProp.createDelegate(this);
56823 };
56824
56825 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56826     
56827     
56828     nameText : 'Name',
56829     valueText : 'Value',
56830     
56831     dateFormat : 'm/j/Y',
56832     
56833     
56834     renderDate : function(dateVal){
56835         return dateVal.dateFormat(this.dateFormat);
56836     },
56837
56838     renderBool : function(bVal){
56839         return bVal ? 'true' : 'false';
56840     },
56841
56842     isCellEditable : function(colIndex, rowIndex){
56843         return colIndex == 1;
56844     },
56845
56846     getRenderer : function(col){
56847         return col == 1 ?
56848             this.renderCellDelegate : this.renderPropDelegate;
56849     },
56850
56851     renderProp : function(v){
56852         return this.getPropertyName(v);
56853     },
56854
56855     renderCell : function(val){
56856         var rv = val;
56857         if(val instanceof Date){
56858             rv = this.renderDate(val);
56859         }else if(typeof val == 'boolean'){
56860             rv = this.renderBool(val);
56861         }
56862         return Roo.util.Format.htmlEncode(rv);
56863     },
56864
56865     getPropertyName : function(name){
56866         var pn = this.grid.propertyNames;
56867         return pn && pn[name] ? pn[name] : name;
56868     },
56869
56870     getCellEditor : function(colIndex, rowIndex){
56871         var p = this.store.getProperty(rowIndex);
56872         var n = p.data['name'], val = p.data['value'];
56873         
56874         if(typeof(this.grid.customEditors[n]) == 'string'){
56875             return this.editors[this.grid.customEditors[n]];
56876         }
56877         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56878             return this.grid.customEditors[n];
56879         }
56880         if(val instanceof Date){
56881             return this.editors['date'];
56882         }else if(typeof val == 'number'){
56883             return this.editors['number'];
56884         }else if(typeof val == 'boolean'){
56885             return this.editors['boolean'];
56886         }else{
56887             return this.editors['string'];
56888         }
56889     }
56890 });
56891
56892 /**
56893  * @class Roo.grid.PropertyGrid
56894  * @extends Roo.grid.EditorGrid
56895  * This class represents the  interface of a component based property grid control.
56896  * <br><br>Usage:<pre><code>
56897  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56898       
56899  });
56900  // set any options
56901  grid.render();
56902  * </code></pre>
56903   
56904  * @constructor
56905  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56906  * The container MUST have some type of size defined for the grid to fill. The container will be
56907  * automatically set to position relative if it isn't already.
56908  * @param {Object} config A config object that sets properties on this grid.
56909  */
56910 Roo.grid.PropertyGrid = function(container, config){
56911     config = config || {};
56912     var store = new Roo.grid.PropertyStore(this);
56913     this.store = store;
56914     var cm = new Roo.grid.PropertyColumnModel(this, store);
56915     store.store.sort('name', 'ASC');
56916     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56917         ds: store.store,
56918         cm: cm,
56919         enableColLock:false,
56920         enableColumnMove:false,
56921         stripeRows:false,
56922         trackMouseOver: false,
56923         clicksToEdit:1
56924     }, config));
56925     this.getGridEl().addClass('x-props-grid');
56926     this.lastEditRow = null;
56927     this.on('columnresize', this.onColumnResize, this);
56928     this.addEvents({
56929          /**
56930              * @event beforepropertychange
56931              * Fires before a property changes (return false to stop?)
56932              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56933              * @param {String} id Record Id
56934              * @param {String} newval New Value
56935          * @param {String} oldval Old Value
56936              */
56937         "beforepropertychange": true,
56938         /**
56939              * @event propertychange
56940              * Fires after a property changes
56941              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56942              * @param {String} id Record Id
56943              * @param {String} newval New Value
56944          * @param {String} oldval Old Value
56945              */
56946         "propertychange": true
56947     });
56948     this.customEditors = this.customEditors || {};
56949 };
56950 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56951     
56952      /**
56953      * @cfg {Object} customEditors map of colnames=> custom editors.
56954      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56955      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56956      * false disables editing of the field.
56957          */
56958     
56959       /**
56960      * @cfg {Object} propertyNames map of property Names to their displayed value
56961          */
56962     
56963     render : function(){
56964         Roo.grid.PropertyGrid.superclass.render.call(this);
56965         this.autoSize.defer(100, this);
56966     },
56967
56968     autoSize : function(){
56969         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56970         if(this.view){
56971             this.view.fitColumns();
56972         }
56973     },
56974
56975     onColumnResize : function(){
56976         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56977         this.autoSize();
56978     },
56979     /**
56980      * Sets the data for the Grid
56981      * accepts a Key => Value object of all the elements avaiable.
56982      * @param {Object} data  to appear in grid.
56983      */
56984     setSource : function(source){
56985         this.store.setSource(source);
56986         //this.autoSize();
56987     },
56988     /**
56989      * Gets all the data from the grid.
56990      * @return {Object} data  data stored in grid
56991      */
56992     getSource : function(){
56993         return this.store.getSource();
56994     }
56995 });/*
56996   
56997  * Licence LGPL
56998  
56999  */
57000  
57001 /**
57002  * @class Roo.grid.Calendar
57003  * @extends Roo.util.Grid
57004  * This class extends the Grid to provide a calendar widget
57005  * <br><br>Usage:<pre><code>
57006  var grid = new Roo.grid.Calendar("my-container-id", {
57007      ds: myDataStore,
57008      cm: myColModel,
57009      selModel: mySelectionModel,
57010      autoSizeColumns: true,
57011      monitorWindowResize: false,
57012      trackMouseOver: true
57013      eventstore : real data store..
57014  });
57015  // set any options
57016  grid.render();
57017   
57018   * @constructor
57019  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57020  * The container MUST have some type of size defined for the grid to fill. The container will be
57021  * automatically set to position relative if it isn't already.
57022  * @param {Object} config A config object that sets properties on this grid.
57023  */
57024 Roo.grid.Calendar = function(container, config){
57025         // initialize the container
57026         this.container = Roo.get(container);
57027         this.container.update("");
57028         this.container.setStyle("overflow", "hidden");
57029     this.container.addClass('x-grid-container');
57030
57031     this.id = this.container.id;
57032
57033     Roo.apply(this, config);
57034     // check and correct shorthanded configs
57035     
57036     var rows = [];
57037     var d =1;
57038     for (var r = 0;r < 6;r++) {
57039         
57040         rows[r]=[];
57041         for (var c =0;c < 7;c++) {
57042             rows[r][c]= '';
57043         }
57044     }
57045     if (this.eventStore) {
57046         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57047         this.eventStore.on('load',this.onLoad, this);
57048         this.eventStore.on('beforeload',this.clearEvents, this);
57049          
57050     }
57051     
57052     this.dataSource = new Roo.data.Store({
57053             proxy: new Roo.data.MemoryProxy(rows),
57054             reader: new Roo.data.ArrayReader({}, [
57055                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57056     });
57057
57058     this.dataSource.load();
57059     this.ds = this.dataSource;
57060     this.ds.xmodule = this.xmodule || false;
57061     
57062     
57063     var cellRender = function(v,x,r)
57064     {
57065         return String.format(
57066             '<div class="fc-day  fc-widget-content"><div>' +
57067                 '<div class="fc-event-container"></div>' +
57068                 '<div class="fc-day-number">{0}</div>'+
57069                 
57070                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57071             '</div></div>', v);
57072     
57073     }
57074     
57075     
57076     this.colModel = new Roo.grid.ColumnModel( [
57077         {
57078             xtype: 'ColumnModel',
57079             xns: Roo.grid,
57080             dataIndex : 'weekday0',
57081             header : 'Sunday',
57082             renderer : cellRender
57083         },
57084         {
57085             xtype: 'ColumnModel',
57086             xns: Roo.grid,
57087             dataIndex : 'weekday1',
57088             header : 'Monday',
57089             renderer : cellRender
57090         },
57091         {
57092             xtype: 'ColumnModel',
57093             xns: Roo.grid,
57094             dataIndex : 'weekday2',
57095             header : 'Tuesday',
57096             renderer : cellRender
57097         },
57098         {
57099             xtype: 'ColumnModel',
57100             xns: Roo.grid,
57101             dataIndex : 'weekday3',
57102             header : 'Wednesday',
57103             renderer : cellRender
57104         },
57105         {
57106             xtype: 'ColumnModel',
57107             xns: Roo.grid,
57108             dataIndex : 'weekday4',
57109             header : 'Thursday',
57110             renderer : cellRender
57111         },
57112         {
57113             xtype: 'ColumnModel',
57114             xns: Roo.grid,
57115             dataIndex : 'weekday5',
57116             header : 'Friday',
57117             renderer : cellRender
57118         },
57119         {
57120             xtype: 'ColumnModel',
57121             xns: Roo.grid,
57122             dataIndex : 'weekday6',
57123             header : 'Saturday',
57124             renderer : cellRender
57125         }
57126     ]);
57127     this.cm = this.colModel;
57128     this.cm.xmodule = this.xmodule || false;
57129  
57130         
57131           
57132     //this.selModel = new Roo.grid.CellSelectionModel();
57133     //this.sm = this.selModel;
57134     //this.selModel.init(this);
57135     
57136     
57137     if(this.width){
57138         this.container.setWidth(this.width);
57139     }
57140
57141     if(this.height){
57142         this.container.setHeight(this.height);
57143     }
57144     /** @private */
57145         this.addEvents({
57146         // raw events
57147         /**
57148          * @event click
57149          * The raw click event for the entire grid.
57150          * @param {Roo.EventObject} e
57151          */
57152         "click" : true,
57153         /**
57154          * @event dblclick
57155          * The raw dblclick event for the entire grid.
57156          * @param {Roo.EventObject} e
57157          */
57158         "dblclick" : true,
57159         /**
57160          * @event contextmenu
57161          * The raw contextmenu event for the entire grid.
57162          * @param {Roo.EventObject} e
57163          */
57164         "contextmenu" : true,
57165         /**
57166          * @event mousedown
57167          * The raw mousedown event for the entire grid.
57168          * @param {Roo.EventObject} e
57169          */
57170         "mousedown" : true,
57171         /**
57172          * @event mouseup
57173          * The raw mouseup event for the entire grid.
57174          * @param {Roo.EventObject} e
57175          */
57176         "mouseup" : true,
57177         /**
57178          * @event mouseover
57179          * The raw mouseover event for the entire grid.
57180          * @param {Roo.EventObject} e
57181          */
57182         "mouseover" : true,
57183         /**
57184          * @event mouseout
57185          * The raw mouseout event for the entire grid.
57186          * @param {Roo.EventObject} e
57187          */
57188         "mouseout" : true,
57189         /**
57190          * @event keypress
57191          * The raw keypress event for the entire grid.
57192          * @param {Roo.EventObject} e
57193          */
57194         "keypress" : true,
57195         /**
57196          * @event keydown
57197          * The raw keydown event for the entire grid.
57198          * @param {Roo.EventObject} e
57199          */
57200         "keydown" : true,
57201
57202         // custom events
57203
57204         /**
57205          * @event cellclick
57206          * Fires when a cell is clicked
57207          * @param {Grid} this
57208          * @param {Number} rowIndex
57209          * @param {Number} columnIndex
57210          * @param {Roo.EventObject} e
57211          */
57212         "cellclick" : true,
57213         /**
57214          * @event celldblclick
57215          * Fires when a cell is double clicked
57216          * @param {Grid} this
57217          * @param {Number} rowIndex
57218          * @param {Number} columnIndex
57219          * @param {Roo.EventObject} e
57220          */
57221         "celldblclick" : true,
57222         /**
57223          * @event rowclick
57224          * Fires when a row is clicked
57225          * @param {Grid} this
57226          * @param {Number} rowIndex
57227          * @param {Roo.EventObject} e
57228          */
57229         "rowclick" : true,
57230         /**
57231          * @event rowdblclick
57232          * Fires when a row is double clicked
57233          * @param {Grid} this
57234          * @param {Number} rowIndex
57235          * @param {Roo.EventObject} e
57236          */
57237         "rowdblclick" : true,
57238         /**
57239          * @event headerclick
57240          * Fires when a header is clicked
57241          * @param {Grid} this
57242          * @param {Number} columnIndex
57243          * @param {Roo.EventObject} e
57244          */
57245         "headerclick" : true,
57246         /**
57247          * @event headerdblclick
57248          * Fires when a header cell is double clicked
57249          * @param {Grid} this
57250          * @param {Number} columnIndex
57251          * @param {Roo.EventObject} e
57252          */
57253         "headerdblclick" : true,
57254         /**
57255          * @event rowcontextmenu
57256          * Fires when a row is right clicked
57257          * @param {Grid} this
57258          * @param {Number} rowIndex
57259          * @param {Roo.EventObject} e
57260          */
57261         "rowcontextmenu" : true,
57262         /**
57263          * @event cellcontextmenu
57264          * Fires when a cell is right clicked
57265          * @param {Grid} this
57266          * @param {Number} rowIndex
57267          * @param {Number} cellIndex
57268          * @param {Roo.EventObject} e
57269          */
57270          "cellcontextmenu" : true,
57271         /**
57272          * @event headercontextmenu
57273          * Fires when a header is right clicked
57274          * @param {Grid} this
57275          * @param {Number} columnIndex
57276          * @param {Roo.EventObject} e
57277          */
57278         "headercontextmenu" : true,
57279         /**
57280          * @event bodyscroll
57281          * Fires when the body element is scrolled
57282          * @param {Number} scrollLeft
57283          * @param {Number} scrollTop
57284          */
57285         "bodyscroll" : true,
57286         /**
57287          * @event columnresize
57288          * Fires when the user resizes a column
57289          * @param {Number} columnIndex
57290          * @param {Number} newSize
57291          */
57292         "columnresize" : true,
57293         /**
57294          * @event columnmove
57295          * Fires when the user moves a column
57296          * @param {Number} oldIndex
57297          * @param {Number} newIndex
57298          */
57299         "columnmove" : true,
57300         /**
57301          * @event startdrag
57302          * Fires when row(s) start being dragged
57303          * @param {Grid} this
57304          * @param {Roo.GridDD} dd The drag drop object
57305          * @param {event} e The raw browser event
57306          */
57307         "startdrag" : true,
57308         /**
57309          * @event enddrag
57310          * Fires when a drag operation is complete
57311          * @param {Grid} this
57312          * @param {Roo.GridDD} dd The drag drop object
57313          * @param {event} e The raw browser event
57314          */
57315         "enddrag" : true,
57316         /**
57317          * @event dragdrop
57318          * Fires when dragged row(s) are dropped on a valid DD target
57319          * @param {Grid} this
57320          * @param {Roo.GridDD} dd The drag drop object
57321          * @param {String} targetId The target drag drop object
57322          * @param {event} e The raw browser event
57323          */
57324         "dragdrop" : true,
57325         /**
57326          * @event dragover
57327          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57328          * @param {Grid} this
57329          * @param {Roo.GridDD} dd The drag drop object
57330          * @param {String} targetId The target drag drop object
57331          * @param {event} e The raw browser event
57332          */
57333         "dragover" : true,
57334         /**
57335          * @event dragenter
57336          *  Fires when the dragged row(s) first cross another DD target while being dragged
57337          * @param {Grid} this
57338          * @param {Roo.GridDD} dd The drag drop object
57339          * @param {String} targetId The target drag drop object
57340          * @param {event} e The raw browser event
57341          */
57342         "dragenter" : true,
57343         /**
57344          * @event dragout
57345          * Fires when the dragged row(s) leave another DD target while being dragged
57346          * @param {Grid} this
57347          * @param {Roo.GridDD} dd The drag drop object
57348          * @param {String} targetId The target drag drop object
57349          * @param {event} e The raw browser event
57350          */
57351         "dragout" : true,
57352         /**
57353          * @event rowclass
57354          * Fires when a row is rendered, so you can change add a style to it.
57355          * @param {GridView} gridview   The grid view
57356          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57357          */
57358         'rowclass' : true,
57359
57360         /**
57361          * @event render
57362          * Fires when the grid is rendered
57363          * @param {Grid} grid
57364          */
57365         'render' : true,
57366             /**
57367              * @event select
57368              * Fires when a date is selected
57369              * @param {DatePicker} this
57370              * @param {Date} date The selected date
57371              */
57372         'select': true,
57373         /**
57374              * @event monthchange
57375              * Fires when the displayed month changes 
57376              * @param {DatePicker} this
57377              * @param {Date} date The selected month
57378              */
57379         'monthchange': true,
57380         /**
57381              * @event evententer
57382              * Fires when mouse over an event
57383              * @param {Calendar} this
57384              * @param {event} Event
57385              */
57386         'evententer': true,
57387         /**
57388              * @event eventleave
57389              * Fires when the mouse leaves an
57390              * @param {Calendar} this
57391              * @param {event}
57392              */
57393         'eventleave': true,
57394         /**
57395              * @event eventclick
57396              * Fires when the mouse click an
57397              * @param {Calendar} this
57398              * @param {event}
57399              */
57400         'eventclick': true,
57401         /**
57402              * @event eventrender
57403              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57404              * @param {Calendar} this
57405              * @param {data} data to be modified
57406              */
57407         'eventrender': true
57408         
57409     });
57410
57411     Roo.grid.Grid.superclass.constructor.call(this);
57412     this.on('render', function() {
57413         this.view.el.addClass('x-grid-cal'); 
57414         
57415         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57416
57417     },this);
57418     
57419     if (!Roo.grid.Calendar.style) {
57420         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57421             
57422             
57423             '.x-grid-cal .x-grid-col' :  {
57424                 height: 'auto !important',
57425                 'vertical-align': 'top'
57426             },
57427             '.x-grid-cal  .fc-event-hori' : {
57428                 height: '14px'
57429             }
57430              
57431             
57432         }, Roo.id());
57433     }
57434
57435     
57436     
57437 };
57438 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57439     /**
57440      * @cfg {Store} eventStore The store that loads events.
57441      */
57442     eventStore : 25,
57443
57444      
57445     activeDate : false,
57446     startDay : 0,
57447     autoWidth : true,
57448     monitorWindowResize : false,
57449
57450     
57451     resizeColumns : function() {
57452         var col = (this.view.el.getWidth() / 7) - 3;
57453         // loop through cols, and setWidth
57454         for(var i =0 ; i < 7 ; i++){
57455             this.cm.setColumnWidth(i, col);
57456         }
57457     },
57458      setDate :function(date) {
57459         
57460         Roo.log('setDate?');
57461         
57462         this.resizeColumns();
57463         var vd = this.activeDate;
57464         this.activeDate = date;
57465 //        if(vd && this.el){
57466 //            var t = date.getTime();
57467 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57468 //                Roo.log('using add remove');
57469 //                
57470 //                this.fireEvent('monthchange', this, date);
57471 //                
57472 //                this.cells.removeClass("fc-state-highlight");
57473 //                this.cells.each(function(c){
57474 //                   if(c.dateValue == t){
57475 //                       c.addClass("fc-state-highlight");
57476 //                       setTimeout(function(){
57477 //                            try{c.dom.firstChild.focus();}catch(e){}
57478 //                       }, 50);
57479 //                       return false;
57480 //                   }
57481 //                   return true;
57482 //                });
57483 //                return;
57484 //            }
57485 //        }
57486         
57487         var days = date.getDaysInMonth();
57488         
57489         var firstOfMonth = date.getFirstDateOfMonth();
57490         var startingPos = firstOfMonth.getDay()-this.startDay;
57491         
57492         if(startingPos < this.startDay){
57493             startingPos += 7;
57494         }
57495         
57496         var pm = date.add(Date.MONTH, -1);
57497         var prevStart = pm.getDaysInMonth()-startingPos;
57498 //        
57499         
57500         
57501         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57502         
57503         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57504         //this.cells.addClassOnOver('fc-state-hover');
57505         
57506         var cells = this.cells.elements;
57507         var textEls = this.textNodes;
57508         
57509         //Roo.each(cells, function(cell){
57510         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57511         //});
57512         
57513         days += startingPos;
57514
57515         // convert everything to numbers so it's fast
57516         var day = 86400000;
57517         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57518         //Roo.log(d);
57519         //Roo.log(pm);
57520         //Roo.log(prevStart);
57521         
57522         var today = new Date().clearTime().getTime();
57523         var sel = date.clearTime().getTime();
57524         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57525         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57526         var ddMatch = this.disabledDatesRE;
57527         var ddText = this.disabledDatesText;
57528         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57529         var ddaysText = this.disabledDaysText;
57530         var format = this.format;
57531         
57532         var setCellClass = function(cal, cell){
57533             
57534             //Roo.log('set Cell Class');
57535             cell.title = "";
57536             var t = d.getTime();
57537             
57538             //Roo.log(d);
57539             
57540             
57541             cell.dateValue = t;
57542             if(t == today){
57543                 cell.className += " fc-today";
57544                 cell.className += " fc-state-highlight";
57545                 cell.title = cal.todayText;
57546             }
57547             if(t == sel){
57548                 // disable highlight in other month..
57549                 cell.className += " fc-state-highlight";
57550                 
57551             }
57552             // disabling
57553             if(t < min) {
57554                 //cell.className = " fc-state-disabled";
57555                 cell.title = cal.minText;
57556                 return;
57557             }
57558             if(t > max) {
57559                 //cell.className = " fc-state-disabled";
57560                 cell.title = cal.maxText;
57561                 return;
57562             }
57563             if(ddays){
57564                 if(ddays.indexOf(d.getDay()) != -1){
57565                     // cell.title = ddaysText;
57566                    // cell.className = " fc-state-disabled";
57567                 }
57568             }
57569             if(ddMatch && format){
57570                 var fvalue = d.dateFormat(format);
57571                 if(ddMatch.test(fvalue)){
57572                     cell.title = ddText.replace("%0", fvalue);
57573                    cell.className = " fc-state-disabled";
57574                 }
57575             }
57576             
57577             if (!cell.initialClassName) {
57578                 cell.initialClassName = cell.dom.className;
57579             }
57580             
57581             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57582         };
57583
57584         var i = 0;
57585         
57586         for(; i < startingPos; i++) {
57587             cells[i].dayName =  (++prevStart);
57588             Roo.log(textEls[i]);
57589             d.setDate(d.getDate()+1);
57590             
57591             //cells[i].className = "fc-past fc-other-month";
57592             setCellClass(this, cells[i]);
57593         }
57594         
57595         var intDay = 0;
57596         
57597         for(; i < days; i++){
57598             intDay = i - startingPos + 1;
57599             cells[i].dayName =  (intDay);
57600             d.setDate(d.getDate()+1);
57601             
57602             cells[i].className = ''; // "x-date-active";
57603             setCellClass(this, cells[i]);
57604         }
57605         var extraDays = 0;
57606         
57607         for(; i < 42; i++) {
57608             //textEls[i].innerHTML = (++extraDays);
57609             
57610             d.setDate(d.getDate()+1);
57611             cells[i].dayName = (++extraDays);
57612             cells[i].className = "fc-future fc-other-month";
57613             setCellClass(this, cells[i]);
57614         }
57615         
57616         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57617         
57618         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57619         
57620         // this will cause all the cells to mis
57621         var rows= [];
57622         var i =0;
57623         for (var r = 0;r < 6;r++) {
57624             for (var c =0;c < 7;c++) {
57625                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57626             }    
57627         }
57628         
57629         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57630         for(i=0;i<cells.length;i++) {
57631             
57632             this.cells.elements[i].dayName = cells[i].dayName ;
57633             this.cells.elements[i].className = cells[i].className;
57634             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57635             this.cells.elements[i].title = cells[i].title ;
57636             this.cells.elements[i].dateValue = cells[i].dateValue ;
57637         }
57638         
57639         
57640         
57641         
57642         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57643         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57644         
57645         ////if(totalRows != 6){
57646             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57647            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57648        // }
57649         
57650         this.fireEvent('monthchange', this, date);
57651         
57652         
57653     },
57654  /**
57655      * Returns the grid's SelectionModel.
57656      * @return {SelectionModel}
57657      */
57658     getSelectionModel : function(){
57659         if(!this.selModel){
57660             this.selModel = new Roo.grid.CellSelectionModel();
57661         }
57662         return this.selModel;
57663     },
57664
57665     load: function() {
57666         this.eventStore.load()
57667         
57668         
57669         
57670     },
57671     
57672     findCell : function(dt) {
57673         dt = dt.clearTime().getTime();
57674         var ret = false;
57675         this.cells.each(function(c){
57676             //Roo.log("check " +c.dateValue + '?=' + dt);
57677             if(c.dateValue == dt){
57678                 ret = c;
57679                 return false;
57680             }
57681             return true;
57682         });
57683         
57684         return ret;
57685     },
57686     
57687     findCells : function(rec) {
57688         var s = rec.data.start_dt.clone().clearTime().getTime();
57689        // Roo.log(s);
57690         var e= rec.data.end_dt.clone().clearTime().getTime();
57691        // Roo.log(e);
57692         var ret = [];
57693         this.cells.each(function(c){
57694              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57695             
57696             if(c.dateValue > e){
57697                 return ;
57698             }
57699             if(c.dateValue < s){
57700                 return ;
57701             }
57702             ret.push(c);
57703         });
57704         
57705         return ret;    
57706     },
57707     
57708     findBestRow: function(cells)
57709     {
57710         var ret = 0;
57711         
57712         for (var i =0 ; i < cells.length;i++) {
57713             ret  = Math.max(cells[i].rows || 0,ret);
57714         }
57715         return ret;
57716         
57717     },
57718     
57719     
57720     addItem : function(rec)
57721     {
57722         // look for vertical location slot in
57723         var cells = this.findCells(rec);
57724         
57725         rec.row = this.findBestRow(cells);
57726         
57727         // work out the location.
57728         
57729         var crow = false;
57730         var rows = [];
57731         for(var i =0; i < cells.length; i++) {
57732             if (!crow) {
57733                 crow = {
57734                     start : cells[i],
57735                     end :  cells[i]
57736                 };
57737                 continue;
57738             }
57739             if (crow.start.getY() == cells[i].getY()) {
57740                 // on same row.
57741                 crow.end = cells[i];
57742                 continue;
57743             }
57744             // different row.
57745             rows.push(crow);
57746             crow = {
57747                 start: cells[i],
57748                 end : cells[i]
57749             };
57750             
57751         }
57752         
57753         rows.push(crow);
57754         rec.els = [];
57755         rec.rows = rows;
57756         rec.cells = cells;
57757         for (var i = 0; i < cells.length;i++) {
57758             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57759             
57760         }
57761         
57762         
57763     },
57764     
57765     clearEvents: function() {
57766         
57767         if (!this.eventStore.getCount()) {
57768             return;
57769         }
57770         // reset number of rows in cells.
57771         Roo.each(this.cells.elements, function(c){
57772             c.rows = 0;
57773         });
57774         
57775         this.eventStore.each(function(e) {
57776             this.clearEvent(e);
57777         },this);
57778         
57779     },
57780     
57781     clearEvent : function(ev)
57782     {
57783         if (ev.els) {
57784             Roo.each(ev.els, function(el) {
57785                 el.un('mouseenter' ,this.onEventEnter, this);
57786                 el.un('mouseleave' ,this.onEventLeave, this);
57787                 el.remove();
57788             },this);
57789             ev.els = [];
57790         }
57791     },
57792     
57793     
57794     renderEvent : function(ev,ctr) {
57795         if (!ctr) {
57796              ctr = this.view.el.select('.fc-event-container',true).first();
57797         }
57798         
57799          
57800         this.clearEvent(ev);
57801             //code
57802        
57803         
57804         
57805         ev.els = [];
57806         var cells = ev.cells;
57807         var rows = ev.rows;
57808         this.fireEvent('eventrender', this, ev);
57809         
57810         for(var i =0; i < rows.length; i++) {
57811             
57812             cls = '';
57813             if (i == 0) {
57814                 cls += ' fc-event-start';
57815             }
57816             if ((i+1) == rows.length) {
57817                 cls += ' fc-event-end';
57818             }
57819             
57820             //Roo.log(ev.data);
57821             // how many rows should it span..
57822             var cg = this.eventTmpl.append(ctr,Roo.apply({
57823                 fccls : cls
57824                 
57825             }, ev.data) , true);
57826             
57827             
57828             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57829             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57830             cg.on('click', this.onEventClick, this, ev);
57831             
57832             ev.els.push(cg);
57833             
57834             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57835             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57836             //Roo.log(cg);
57837              
57838             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57839             cg.setWidth(ebox.right - sbox.x -2);
57840         }
57841     },
57842     
57843     renderEvents: function()
57844     {   
57845         // first make sure there is enough space..
57846         
57847         if (!this.eventTmpl) {
57848             this.eventTmpl = new Roo.Template(
57849                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57850                     '<div class="fc-event-inner">' +
57851                         '<span class="fc-event-time">{time}</span>' +
57852                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57853                     '</div>' +
57854                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57855                 '</div>'
57856             );
57857                 
57858         }
57859                
57860         
57861         
57862         this.cells.each(function(c) {
57863             //Roo.log(c.select('.fc-day-content div',true).first());
57864             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57865         });
57866         
57867         var ctr = this.view.el.select('.fc-event-container',true).first();
57868         
57869         var cls;
57870         this.eventStore.each(function(ev){
57871             
57872             this.renderEvent(ev);
57873              
57874              
57875         }, this);
57876         this.view.layout();
57877         
57878     },
57879     
57880     onEventEnter: function (e, el,event,d) {
57881         this.fireEvent('evententer', this, el, event);
57882     },
57883     
57884     onEventLeave: function (e, el,event,d) {
57885         this.fireEvent('eventleave', this, el, event);
57886     },
57887     
57888     onEventClick: function (e, el,event,d) {
57889         this.fireEvent('eventclick', this, el, event);
57890     },
57891     
57892     onMonthChange: function () {
57893         this.store.load();
57894     },
57895     
57896     onLoad: function () {
57897         
57898         //Roo.log('calendar onload');
57899 //         
57900         if(this.eventStore.getCount() > 0){
57901             
57902            
57903             
57904             this.eventStore.each(function(d){
57905                 
57906                 
57907                 // FIXME..
57908                 var add =   d.data;
57909                 if (typeof(add.end_dt) == 'undefined')  {
57910                     Roo.log("Missing End time in calendar data: ");
57911                     Roo.log(d);
57912                     return;
57913                 }
57914                 if (typeof(add.start_dt) == 'undefined')  {
57915                     Roo.log("Missing Start time in calendar data: ");
57916                     Roo.log(d);
57917                     return;
57918                 }
57919                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57920                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57921                 add.id = add.id || d.id;
57922                 add.title = add.title || '??';
57923                 
57924                 this.addItem(d);
57925                 
57926              
57927             },this);
57928         }
57929         
57930         this.renderEvents();
57931     }
57932     
57933
57934 });
57935 /*
57936  grid : {
57937                 xtype: 'Grid',
57938                 xns: Roo.grid,
57939                 listeners : {
57940                     render : function ()
57941                     {
57942                         _this.grid = this;
57943                         
57944                         if (!this.view.el.hasClass('course-timesheet')) {
57945                             this.view.el.addClass('course-timesheet');
57946                         }
57947                         if (this.tsStyle) {
57948                             this.ds.load({});
57949                             return; 
57950                         }
57951                         Roo.log('width');
57952                         Roo.log(_this.grid.view.el.getWidth());
57953                         
57954                         
57955                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57956                             '.course-timesheet .x-grid-row' : {
57957                                 height: '80px'
57958                             },
57959                             '.x-grid-row td' : {
57960                                 'vertical-align' : 0
57961                             },
57962                             '.course-edit-link' : {
57963                                 'color' : 'blue',
57964                                 'text-overflow' : 'ellipsis',
57965                                 'overflow' : 'hidden',
57966                                 'white-space' : 'nowrap',
57967                                 'cursor' : 'pointer'
57968                             },
57969                             '.sub-link' : {
57970                                 'color' : 'green'
57971                             },
57972                             '.de-act-sup-link' : {
57973                                 'color' : 'purple',
57974                                 'text-decoration' : 'line-through'
57975                             },
57976                             '.de-act-link' : {
57977                                 'color' : 'red',
57978                                 'text-decoration' : 'line-through'
57979                             },
57980                             '.course-timesheet .course-highlight' : {
57981                                 'border-top-style': 'dashed !important',
57982                                 'border-bottom-bottom': 'dashed !important'
57983                             },
57984                             '.course-timesheet .course-item' : {
57985                                 'font-family'   : 'tahoma, arial, helvetica',
57986                                 'font-size'     : '11px',
57987                                 'overflow'      : 'hidden',
57988                                 'padding-left'  : '10px',
57989                                 'padding-right' : '10px',
57990                                 'padding-top' : '10px' 
57991                             }
57992                             
57993                         }, Roo.id());
57994                                 this.ds.load({});
57995                     }
57996                 },
57997                 autoWidth : true,
57998                 monitorWindowResize : false,
57999                 cellrenderer : function(v,x,r)
58000                 {
58001                     return v;
58002                 },
58003                 sm : {
58004                     xtype: 'CellSelectionModel',
58005                     xns: Roo.grid
58006                 },
58007                 dataSource : {
58008                     xtype: 'Store',
58009                     xns: Roo.data,
58010                     listeners : {
58011                         beforeload : function (_self, options)
58012                         {
58013                             options.params = options.params || {};
58014                             options.params._month = _this.monthField.getValue();
58015                             options.params.limit = 9999;
58016                             options.params['sort'] = 'when_dt';    
58017                             options.params['dir'] = 'ASC';    
58018                             this.proxy.loadResponse = this.loadResponse;
58019                             Roo.log("load?");
58020                             //this.addColumns();
58021                         },
58022                         load : function (_self, records, options)
58023                         {
58024                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58025                                 // if you click on the translation.. you can edit it...
58026                                 var el = Roo.get(this);
58027                                 var id = el.dom.getAttribute('data-id');
58028                                 var d = el.dom.getAttribute('data-date');
58029                                 var t = el.dom.getAttribute('data-time');
58030                                 //var id = this.child('span').dom.textContent;
58031                                 
58032                                 //Roo.log(this);
58033                                 Pman.Dialog.CourseCalendar.show({
58034                                     id : id,
58035                                     when_d : d,
58036                                     when_t : t,
58037                                     productitem_active : id ? 1 : 0
58038                                 }, function() {
58039                                     _this.grid.ds.load({});
58040                                 });
58041                            
58042                            });
58043                            
58044                            _this.panel.fireEvent('resize', [ '', '' ]);
58045                         }
58046                     },
58047                     loadResponse : function(o, success, response){
58048                             // this is overridden on before load..
58049                             
58050                             Roo.log("our code?");       
58051                             //Roo.log(success);
58052                             //Roo.log(response)
58053                             delete this.activeRequest;
58054                             if(!success){
58055                                 this.fireEvent("loadexception", this, o, response);
58056                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58057                                 return;
58058                             }
58059                             var result;
58060                             try {
58061                                 result = o.reader.read(response);
58062                             }catch(e){
58063                                 Roo.log("load exception?");
58064                                 this.fireEvent("loadexception", this, o, response, e);
58065                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58066                                 return;
58067                             }
58068                             Roo.log("ready...");        
58069                             // loop through result.records;
58070                             // and set this.tdate[date] = [] << array of records..
58071                             _this.tdata  = {};
58072                             Roo.each(result.records, function(r){
58073                                 //Roo.log(r.data);
58074                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58075                                     _this.tdata[r.data.when_dt.format('j')] = [];
58076                                 }
58077                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58078                             });
58079                             
58080                             //Roo.log(_this.tdata);
58081                             
58082                             result.records = [];
58083                             result.totalRecords = 6;
58084                     
58085                             // let's generate some duumy records for the rows.
58086                             //var st = _this.dateField.getValue();
58087                             
58088                             // work out monday..
58089                             //st = st.add(Date.DAY, -1 * st.format('w'));
58090                             
58091                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58092                             
58093                             var firstOfMonth = date.getFirstDayOfMonth();
58094                             var days = date.getDaysInMonth();
58095                             var d = 1;
58096                             var firstAdded = false;
58097                             for (var i = 0; i < result.totalRecords ; i++) {
58098                                 //var d= st.add(Date.DAY, i);
58099                                 var row = {};
58100                                 var added = 0;
58101                                 for(var w = 0 ; w < 7 ; w++){
58102                                     if(!firstAdded && firstOfMonth != w){
58103                                         continue;
58104                                     }
58105                                     if(d > days){
58106                                         continue;
58107                                     }
58108                                     firstAdded = true;
58109                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58110                                     row['weekday'+w] = String.format(
58111                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58112                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58113                                                     d,
58114                                                     date.format('Y-m-')+dd
58115                                                 );
58116                                     added++;
58117                                     if(typeof(_this.tdata[d]) != 'undefined'){
58118                                         Roo.each(_this.tdata[d], function(r){
58119                                             var is_sub = '';
58120                                             var deactive = '';
58121                                             var id = r.id;
58122                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58123                                             if(r.parent_id*1>0){
58124                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58125                                                 id = r.parent_id;
58126                                             }
58127                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58128                                                 deactive = 'de-act-link';
58129                                             }
58130                                             
58131                                             row['weekday'+w] += String.format(
58132                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58133                                                     id, //0
58134                                                     r.product_id_name, //1
58135                                                     r.when_dt.format('h:ia'), //2
58136                                                     is_sub, //3
58137                                                     deactive, //4
58138                                                     desc // 5
58139                                             );
58140                                         });
58141                                     }
58142                                     d++;
58143                                 }
58144                                 
58145                                 // only do this if something added..
58146                                 if(added > 0){ 
58147                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58148                                 }
58149                                 
58150                                 
58151                                 // push it twice. (second one with an hour..
58152                                 
58153                             }
58154                             //Roo.log(result);
58155                             this.fireEvent("load", this, o, o.request.arg);
58156                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58157                         },
58158                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58159                     proxy : {
58160                         xtype: 'HttpProxy',
58161                         xns: Roo.data,
58162                         method : 'GET',
58163                         url : baseURL + '/Roo/Shop_course.php'
58164                     },
58165                     reader : {
58166                         xtype: 'JsonReader',
58167                         xns: Roo.data,
58168                         id : 'id',
58169                         fields : [
58170                             {
58171                                 'name': 'id',
58172                                 'type': 'int'
58173                             },
58174                             {
58175                                 'name': 'when_dt',
58176                                 'type': 'string'
58177                             },
58178                             {
58179                                 'name': 'end_dt',
58180                                 'type': 'string'
58181                             },
58182                             {
58183                                 'name': 'parent_id',
58184                                 'type': 'int'
58185                             },
58186                             {
58187                                 'name': 'product_id',
58188                                 'type': 'int'
58189                             },
58190                             {
58191                                 'name': 'productitem_id',
58192                                 'type': 'int'
58193                             },
58194                             {
58195                                 'name': 'guid',
58196                                 'type': 'int'
58197                             }
58198                         ]
58199                     }
58200                 },
58201                 toolbar : {
58202                     xtype: 'Toolbar',
58203                     xns: Roo,
58204                     items : [
58205                         {
58206                             xtype: 'Button',
58207                             xns: Roo.Toolbar,
58208                             listeners : {
58209                                 click : function (_self, e)
58210                                 {
58211                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58212                                     sd.setMonth(sd.getMonth()-1);
58213                                     _this.monthField.setValue(sd.format('Y-m-d'));
58214                                     _this.grid.ds.load({});
58215                                 }
58216                             },
58217                             text : "Back"
58218                         },
58219                         {
58220                             xtype: 'Separator',
58221                             xns: Roo.Toolbar
58222                         },
58223                         {
58224                             xtype: 'MonthField',
58225                             xns: Roo.form,
58226                             listeners : {
58227                                 render : function (_self)
58228                                 {
58229                                     _this.monthField = _self;
58230                                    // _this.monthField.set  today
58231                                 },
58232                                 select : function (combo, date)
58233                                 {
58234                                     _this.grid.ds.load({});
58235                                 }
58236                             },
58237                             value : (function() { return new Date(); })()
58238                         },
58239                         {
58240                             xtype: 'Separator',
58241                             xns: Roo.Toolbar
58242                         },
58243                         {
58244                             xtype: 'TextItem',
58245                             xns: Roo.Toolbar,
58246                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58247                         },
58248                         {
58249                             xtype: 'Fill',
58250                             xns: Roo.Toolbar
58251                         },
58252                         {
58253                             xtype: 'Button',
58254                             xns: Roo.Toolbar,
58255                             listeners : {
58256                                 click : function (_self, e)
58257                                 {
58258                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58259                                     sd.setMonth(sd.getMonth()+1);
58260                                     _this.monthField.setValue(sd.format('Y-m-d'));
58261                                     _this.grid.ds.load({});
58262                                 }
58263                             },
58264                             text : "Next"
58265                         }
58266                     ]
58267                 },
58268                  
58269             }
58270         };
58271         
58272         *//*
58273  * Based on:
58274  * Ext JS Library 1.1.1
58275  * Copyright(c) 2006-2007, Ext JS, LLC.
58276  *
58277  * Originally Released Under LGPL - original licence link has changed is not relivant.
58278  *
58279  * Fork - LGPL
58280  * <script type="text/javascript">
58281  */
58282  
58283 /**
58284  * @class Roo.LoadMask
58285  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58286  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58287  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58288  * element's UpdateManager load indicator and will be destroyed after the initial load.
58289  * @constructor
58290  * Create a new LoadMask
58291  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58292  * @param {Object} config The config object
58293  */
58294 Roo.LoadMask = function(el, config){
58295     this.el = Roo.get(el);
58296     Roo.apply(this, config);
58297     if(this.store){
58298         this.store.on('beforeload', this.onBeforeLoad, this);
58299         this.store.on('load', this.onLoad, this);
58300         this.store.on('loadexception', this.onLoadException, this);
58301         this.removeMask = false;
58302     }else{
58303         var um = this.el.getUpdateManager();
58304         um.showLoadIndicator = false; // disable the default indicator
58305         um.on('beforeupdate', this.onBeforeLoad, this);
58306         um.on('update', this.onLoad, this);
58307         um.on('failure', this.onLoad, this);
58308         this.removeMask = true;
58309     }
58310 };
58311
58312 Roo.LoadMask.prototype = {
58313     /**
58314      * @cfg {Boolean} removeMask
58315      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58316      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58317      */
58318     /**
58319      * @cfg {String} msg
58320      * The text to display in a centered loading message box (defaults to 'Loading...')
58321      */
58322     msg : 'Loading...',
58323     /**
58324      * @cfg {String} msgCls
58325      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58326      */
58327     msgCls : 'x-mask-loading',
58328
58329     /**
58330      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58331      * @type Boolean
58332      */
58333     disabled: false,
58334
58335     /**
58336      * Disables the mask to prevent it from being displayed
58337      */
58338     disable : function(){
58339        this.disabled = true;
58340     },
58341
58342     /**
58343      * Enables the mask so that it can be displayed
58344      */
58345     enable : function(){
58346         this.disabled = false;
58347     },
58348     
58349     onLoadException : function()
58350     {
58351         Roo.log(arguments);
58352         
58353         if (typeof(arguments[3]) != 'undefined') {
58354             Roo.MessageBox.alert("Error loading",arguments[3]);
58355         } 
58356         /*
58357         try {
58358             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58359                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58360             }   
58361         } catch(e) {
58362             
58363         }
58364         */
58365     
58366         
58367         
58368         this.el.unmask(this.removeMask);
58369     },
58370     // private
58371     onLoad : function()
58372     {
58373         this.el.unmask(this.removeMask);
58374     },
58375
58376     // private
58377     onBeforeLoad : function(){
58378         if(!this.disabled){
58379             this.el.mask(this.msg, this.msgCls);
58380         }
58381     },
58382
58383     // private
58384     destroy : function(){
58385         if(this.store){
58386             this.store.un('beforeload', this.onBeforeLoad, this);
58387             this.store.un('load', this.onLoad, this);
58388             this.store.un('loadexception', this.onLoadException, this);
58389         }else{
58390             var um = this.el.getUpdateManager();
58391             um.un('beforeupdate', this.onBeforeLoad, this);
58392             um.un('update', this.onLoad, this);
58393             um.un('failure', this.onLoad, this);
58394         }
58395     }
58396 };/*
58397  * Based on:
58398  * Ext JS Library 1.1.1
58399  * Copyright(c) 2006-2007, Ext JS, LLC.
58400  *
58401  * Originally Released Under LGPL - original licence link has changed is not relivant.
58402  *
58403  * Fork - LGPL
58404  * <script type="text/javascript">
58405  */
58406
58407
58408 /**
58409  * @class Roo.XTemplate
58410  * @extends Roo.Template
58411  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58412 <pre><code>
58413 var t = new Roo.XTemplate(
58414         '&lt;select name="{name}"&gt;',
58415                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58416         '&lt;/select&gt;'
58417 );
58418  
58419 // then append, applying the master template values
58420  </code></pre>
58421  *
58422  * Supported features:
58423  *
58424  *  Tags:
58425
58426 <pre><code>
58427       {a_variable} - output encoded.
58428       {a_variable.format:("Y-m-d")} - call a method on the variable
58429       {a_variable:raw} - unencoded output
58430       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58431       {a_variable:this.method_on_template(...)} - call a method on the template object.
58432  
58433 </code></pre>
58434  *  The tpl tag:
58435 <pre><code>
58436         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58437         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58438         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58439         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58440   
58441         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58442         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58443 </code></pre>
58444  *      
58445  */
58446 Roo.XTemplate = function()
58447 {
58448     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58449     if (this.html) {
58450         this.compile();
58451     }
58452 };
58453
58454
58455 Roo.extend(Roo.XTemplate, Roo.Template, {
58456
58457     /**
58458      * The various sub templates
58459      */
58460     tpls : false,
58461     /**
58462      *
58463      * basic tag replacing syntax
58464      * WORD:WORD()
58465      *
58466      * // you can fake an object call by doing this
58467      *  x.t:(test,tesT) 
58468      * 
58469      */
58470     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58471
58472     /**
58473      * compile the template
58474      *
58475      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58476      *
58477      */
58478     compile: function()
58479     {
58480         var s = this.html;
58481      
58482         s = ['<tpl>', s, '</tpl>'].join('');
58483     
58484         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58485             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58486             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58487             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58488             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58489             m,
58490             id     = 0,
58491             tpls   = [];
58492     
58493         while(true == !!(m = s.match(re))){
58494             var forMatch   = m[0].match(nameRe),
58495                 ifMatch   = m[0].match(ifRe),
58496                 execMatch   = m[0].match(execRe),
58497                 namedMatch   = m[0].match(namedRe),
58498                 
58499                 exp  = null, 
58500                 fn   = null,
58501                 exec = null,
58502                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58503                 
58504             if (ifMatch) {
58505                 // if - puts fn into test..
58506                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58507                 if(exp){
58508                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58509                 }
58510             }
58511             
58512             if (execMatch) {
58513                 // exec - calls a function... returns empty if true is  returned.
58514                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58515                 if(exp){
58516                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58517                 }
58518             }
58519             
58520             
58521             if (name) {
58522                 // for = 
58523                 switch(name){
58524                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58525                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58526                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58527                 }
58528             }
58529             var uid = namedMatch ? namedMatch[1] : id;
58530             
58531             
58532             tpls.push({
58533                 id:     namedMatch ? namedMatch[1] : id,
58534                 target: name,
58535                 exec:   exec,
58536                 test:   fn,
58537                 body:   m[1] || ''
58538             });
58539             if (namedMatch) {
58540                 s = s.replace(m[0], '');
58541             } else { 
58542                 s = s.replace(m[0], '{xtpl'+ id + '}');
58543             }
58544             ++id;
58545         }
58546         this.tpls = [];
58547         for(var i = tpls.length-1; i >= 0; --i){
58548             this.compileTpl(tpls[i]);
58549             this.tpls[tpls[i].id] = tpls[i];
58550         }
58551         this.master = tpls[tpls.length-1];
58552         return this;
58553     },
58554     /**
58555      * same as applyTemplate, except it's done to one of the subTemplates
58556      * when using named templates, you can do:
58557      *
58558      * var str = pl.applySubTemplate('your-name', values);
58559      *
58560      * 
58561      * @param {Number} id of the template
58562      * @param {Object} values to apply to template
58563      * @param {Object} parent (normaly the instance of this object)
58564      */
58565     applySubTemplate : function(id, values, parent)
58566     {
58567         
58568         
58569         var t = this.tpls[id];
58570         
58571         
58572         try { 
58573             if(t.test && !t.test.call(this, values, parent)){
58574                 return '';
58575             }
58576         } catch(e) {
58577             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58578             Roo.log(e.toString());
58579             Roo.log(t.test);
58580             return ''
58581         }
58582         try { 
58583             
58584             if(t.exec && t.exec.call(this, values, parent)){
58585                 return '';
58586             }
58587         } catch(e) {
58588             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58589             Roo.log(e.toString());
58590             Roo.log(t.exec);
58591             return ''
58592         }
58593         try {
58594             var vs = t.target ? t.target.call(this, values, parent) : values;
58595             parent = t.target ? values : parent;
58596             if(t.target && vs instanceof Array){
58597                 var buf = [];
58598                 for(var i = 0, len = vs.length; i < len; i++){
58599                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58600                 }
58601                 return buf.join('');
58602             }
58603             return t.compiled.call(this, vs, parent);
58604         } catch (e) {
58605             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58606             Roo.log(e.toString());
58607             Roo.log(t.compiled);
58608             return '';
58609         }
58610     },
58611
58612     compileTpl : function(tpl)
58613     {
58614         var fm = Roo.util.Format;
58615         var useF = this.disableFormats !== true;
58616         var sep = Roo.isGecko ? "+" : ",";
58617         var undef = function(str) {
58618             Roo.log("Property not found :"  + str);
58619             return '';
58620         };
58621         
58622         var fn = function(m, name, format, args)
58623         {
58624             //Roo.log(arguments);
58625             args = args ? args.replace(/\\'/g,"'") : args;
58626             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58627             if (typeof(format) == 'undefined') {
58628                 format= 'htmlEncode';
58629             }
58630             if (format == 'raw' ) {
58631                 format = false;
58632             }
58633             
58634             if(name.substr(0, 4) == 'xtpl'){
58635                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58636             }
58637             
58638             // build an array of options to determine if value is undefined..
58639             
58640             // basically get 'xxxx.yyyy' then do
58641             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58642             //    (function () { Roo.log("Property not found"); return ''; })() :
58643             //    ......
58644             
58645             var udef_ar = [];
58646             var lookfor = '';
58647             Roo.each(name.split('.'), function(st) {
58648                 lookfor += (lookfor.length ? '.': '') + st;
58649                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58650             });
58651             
58652             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58653             
58654             
58655             if(format && useF){
58656                 
58657                 args = args ? ',' + args : "";
58658                  
58659                 if(format.substr(0, 5) != "this."){
58660                     format = "fm." + format + '(';
58661                 }else{
58662                     format = 'this.call("'+ format.substr(5) + '", ';
58663                     args = ", values";
58664                 }
58665                 
58666                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58667             }
58668              
58669             if (args.length) {
58670                 // called with xxyx.yuu:(test,test)
58671                 // change to ()
58672                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58673             }
58674             // raw.. - :raw modifier..
58675             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58676             
58677         };
58678         var body;
58679         // branched to use + in gecko and [].join() in others
58680         if(Roo.isGecko){
58681             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58682                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58683                     "';};};";
58684         }else{
58685             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58686             body.push(tpl.body.replace(/(\r\n|\n)/g,
58687                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58688             body.push("'].join('');};};");
58689             body = body.join('');
58690         }
58691         
58692         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58693        
58694         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58695         eval(body);
58696         
58697         return this;
58698     },
58699
58700     applyTemplate : function(values){
58701         return this.master.compiled.call(this, values, {});
58702         //var s = this.subs;
58703     },
58704
58705     apply : function(){
58706         return this.applyTemplate.apply(this, arguments);
58707     }
58708
58709  });
58710
58711 Roo.XTemplate.from = function(el){
58712     el = Roo.getDom(el);
58713     return new Roo.XTemplate(el.value || el.innerHTML);
58714 };