Roo/bootstrap/Button.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 };
32
33 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
34     
35     
36     allowDomMove : false, // to stop relocations in parent onRender...
37     
38     cls : false,
39     
40     style : false,
41     
42     autoCreate : false,
43     
44     tooltip : null,
45     /**
46      * Initialize Events for the element
47      */
48     initEvents : function() { },
49     
50     xattr : false,
51     
52     parentId : false,
53     
54     can_build_overlaid : true,
55     
56     container_method : false,
57     
58     dataId : false,
59     
60     name : false,
61     
62     parent: function() {
63         // returns the parent component..
64         return Roo.ComponentMgr.get(this.parentId)
65         
66         
67     },
68     
69     // private
70     onRender : function(ct, position)
71     {
72        // Roo.log("Call onRender: " + this.xtype);
73         
74         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
75         
76         if(this.el){
77             if (this.el.attr('xtype')) {
78                 this.el.attr('xtypex', this.el.attr('xtype'));
79                 this.el.dom.removeAttribute('xtype');
80                 
81                 this.initEvents();
82             }
83             
84             return;
85         }
86         
87          
88         
89         var cfg = Roo.apply({},  this.getAutoCreate());
90         cfg.id = Roo.id();
91         
92         // fill in the extra attributes 
93         if (this.xattr && typeof(this.xattr) =='object') {
94             for (var i in this.xattr) {
95                 cfg[i] = this.xattr[i];
96             }
97         }
98         
99         if(this.dataId){
100             cfg.dataId = this.dataId;
101         }
102         
103         if (this.cls) {
104             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
105         }
106         
107         if (this.style) { // fixme needs to support more complex style data.
108             cfg.style = this.style;
109         }
110         
111         if(this.name){
112             cfg.name = this.name;
113         }
114         
115        
116         
117         this.el = ct.createChild(cfg, position);
118         
119         if (this.tooltip) {
120             this.tooltipEl().attr('tooltip', this.tooltip);
121         }
122         
123         if(this.tabIndex !== undefined){
124             this.el.dom.setAttribute('tabIndex', this.tabIndex);
125         }
126         this.initEvents();
127         
128         
129     },
130     /**
131      * Fetch the element to add children to
132      * @return {Roo.Element} defaults to this.el
133      */
134     getChildContainer : function()
135     {
136         return this.el;
137     },
138     /**
139      * Fetch the element to display the tooltip on.
140      * @return {Roo.Element} defaults to this.el
141      */
142     tooltipEl : function()
143     {
144         return this.el;
145     },
146         
147     addxtype  : function(tree,cntr)
148     {
149         var cn = this;
150         
151         cn = Roo.factory(tree);
152            
153         cn.parentType = this.xtype; //??
154         cn.parentId = this.id;
155         
156         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
157         if (typeof(cn.container_method) == 'string') {
158             cntr = cn.container_method;
159         }
160         
161         
162         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
163         
164         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
165         
166         var build_from_html =  Roo.XComponent.build_from_html;
167           
168         var is_body  = (tree.xtype == 'Body') ;
169           
170         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
171           
172         var self_cntr_el = Roo.get(this[cntr](false));
173         
174         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
175             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
176                 return this.addxtypeChild(tree,cntr);
177             }
178             
179             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
180                 
181             if(echild){
182                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
183             }
184             
185             Roo.log('skipping render');
186             return cn;
187             
188         }
189         
190         var ret = false;
191         
192         while (true) {
193             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
194             
195             if (!echild) {
196                 break;
197             }
198             
199             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
200                 break;
201             }
202             
203             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
204         }
205         return ret;
206     },
207     
208     addxtypeChild : function (tree, cntr)
209     {
210         Roo.debug && Roo.log('addxtypeChild:' + cntr);
211         var cn = this;
212         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
213         
214         
215         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
216                     (typeof(tree['flexy:foreach']) != 'undefined');
217           
218         
219         
220          skip_children = false;
221         // render the element if it's not BODY.
222         if (tree.xtype != 'Body') {
223            
224             cn = Roo.factory(tree);
225            
226             cn.parentType = this.xtype; //??
227             cn.parentId = this.id;
228             
229             var build_from_html =  Roo.XComponent.build_from_html;
230             
231             
232             // does the container contain child eleemnts with 'xtype' attributes.
233             // that match this xtype..
234             // note - when we render we create these as well..
235             // so we should check to see if body has xtype set.
236             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
237                
238                 var self_cntr_el = Roo.get(this[cntr](false));
239                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
240                 
241                 
242                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
243                 // and are not displayed -this causes this to use up the wrong element when matching.
244                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
245                 
246                 
247                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
248                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
249                   
250                   
251                   
252                     cn.el = echild;
253                   //  Roo.log("GOT");
254                     //echild.dom.removeAttribute('xtype');
255                 } else {
256                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
257                     Roo.debug && Roo.log(self_cntr_el);
258                     Roo.debug && Roo.log(echild);
259                     Roo.debug && Roo.log(cn);
260                 }
261             }
262            
263             
264            
265             // if object has flexy:if - then it may or may not be rendered.
266             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
267                 // skip a flexy if element.
268                 Roo.debug && Roo.log('skipping render');
269                 Roo.debug && Roo.log(tree);
270                 if (!cn.el) {
271                     Roo.debug && Roo.log('skipping all children');
272                     skip_children = true;
273                 }
274                 
275              } else {
276                  
277                 // actually if flexy:foreach is found, we really want to create 
278                 // multiple copies here...
279                 //Roo.log('render');
280                 //Roo.log(this[cntr]());
281                 cn.render(this[cntr](true));
282              }
283             // then add the element..
284         }
285         
286         
287         // handle the kids..
288         
289         var nitems = [];
290         /*
291         if (typeof (tree.menu) != 'undefined') {
292             tree.menu.parentType = cn.xtype;
293             tree.menu.triggerEl = cn.el;
294             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
295             
296         }
297         */
298         if (!tree.items || !tree.items.length) {
299             cn.items = nitems;
300             return cn;
301         }
302         var items = tree.items;
303         delete tree.items;
304         
305         //Roo.log(items.length);
306             // add the items..
307         if (!skip_children) {    
308             for(var i =0;i < items.length;i++) {
309                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
310             }
311         }
312         
313         cn.items = nitems;
314         
315         return cn;
316     }
317     
318     
319     
320     
321 });
322
323  /*
324  * - LGPL
325  *
326  * Body
327  * 
328  */
329
330 /**
331  * @class Roo.bootstrap.Body
332  * @extends Roo.bootstrap.Component
333  * Bootstrap Body class
334  * 
335  * @constructor
336  * Create a new body
337  * @param {Object} config The config object
338  */
339
340 Roo.bootstrap.Body = function(config){
341     Roo.bootstrap.Body.superclass.constructor.call(this, config);
342     this.el = Roo.get(document.body);
343     if (this.cls && this.cls.length) {
344         Roo.get(document.body).addClass(this.cls);
345     }
346 };
347
348 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
349       
350         autoCreate : {
351         cls: 'container'
352     },
353     onRender : function(ct, position)
354     {
355        /* Roo.log("Roo.bootstrap.Body - onRender");
356         if (this.cls && this.cls.length) {
357             Roo.get(document.body).addClass(this.cls);
358         }
359         // style??? xttr???
360         */
361     }
362     
363     
364  
365    
366 });
367
368  /*
369  * - LGPL
370  *
371  * button group
372  * 
373  */
374
375
376 /**
377  * @class Roo.bootstrap.ButtonGroup
378  * @extends Roo.bootstrap.Component
379  * Bootstrap ButtonGroup class
380  * @cfg {String} size lg | sm | xs (default empty normal)
381  * @cfg {String} align vertical | justified  (default none)
382  * @cfg {String} direction up | down (default down)
383  * @cfg {Boolean} toolbar false | true
384  * @cfg {Boolean} btn true | false
385  * 
386  * 
387  * @constructor
388  * Create a new Input
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.ButtonGroup = function(config){
393     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
394 };
395
396 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
397     
398     size: '',
399     align: '',
400     direction: '',
401     toolbar: false,
402     btn: true,
403
404     getAutoCreate : function(){
405         var cfg = {
406             cls: 'btn-group',
407             html : null
408         }
409         
410         cfg.html = this.html || cfg.html;
411         
412         if (this.toolbar) {
413             cfg = {
414                 cls: 'btn-toolbar',
415                 html: null
416             }
417             
418             return cfg;
419         }
420         
421         if (['vertical','justified'].indexOf(this.align)!==-1) {
422             cfg.cls = 'btn-group-' + this.align;
423             
424             if (this.align == 'justified') {
425                 console.log(this.items);
426             }
427         }
428         
429         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
430             cfg.cls += ' btn-group-' + this.size;
431         }
432         
433         if (this.direction == 'up') {
434             cfg.cls += ' dropup' ;
435         }
436         
437         return cfg;
438     }
439    
440 });
441
442  /*
443  * - LGPL
444  *
445  * button
446  * 
447  */
448
449 /**
450  * @class Roo.bootstrap.Button
451  * @extends Roo.bootstrap.Component
452  * Bootstrap Button class
453  * @cfg {String} html The button content
454  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
455  * @cfg {String} size ( lg | sm | xs)
456  * @cfg {String} tag ( a | input | submit)
457  * @cfg {String} href empty or href
458  * @cfg {Boolean} disabled default false;
459  * @cfg {Boolean} isClose default false;
460  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
461  * @cfg {String} badge text for badge
462  * @cfg {String} theme default 
463  * @cfg {Boolean} inverse 
464  * @cfg {Boolean} toggle 
465  * @cfg {String} ontext text for on toggle state
466  * @cfg {String} offtext text for off toggle state
467  * @cfg {Boolean} defaulton 
468  * @cfg {Boolean} preventDefault  default true
469  * @cfg {Boolean} removeClass remove the standard class..
470  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
471  * 
472  * @constructor
473  * Create a new button
474  * @param {Object} config The config object
475  */
476
477
478 Roo.bootstrap.Button = function(config){
479     Roo.bootstrap.Button.superclass.constructor.call(this, config);
480     this.addEvents({
481         // raw events
482         /**
483          * @event click
484          * When a butotn is pressed
485          * @param {Roo.EventObject} e
486          */
487         "click" : true,
488          /**
489          * @event toggle
490          * After the button has been toggles
491          * @param {Roo.EventObject} e
492          * @param {boolean} pressed (also available as button.pressed)
493          */
494         "toggle" : true
495     });
496 };
497
498 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
499     html: false,
500     active: false,
501     weight: '',
502     size: '',
503     tag: 'button',
504     href: '',
505     disabled: false,
506     isClose: false,
507     glyphicon: '',
508     badge: '',
509     theme: 'default',
510     inverse: false,
511     
512     toggle: false,
513     ontext: 'ON',
514     offtext: 'OFF',
515     defaulton: true,
516     preventDefault: true,
517     removeClass: false,
518     name: false,
519     target: false,
520     
521     
522     pressed : null,
523      
524     
525     getAutoCreate : function(){
526         
527         var cfg = {
528             tag : 'button',
529             cls : 'roo-button',
530             html: ''
531         };
532         
533         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
534             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
535             this.tag = 'button';
536         } else {
537             cfg.tag = this.tag;
538         }
539         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
540         
541         if (this.toggle == true) {
542             cfg={
543                 tag: 'div',
544                 cls: 'slider-frame roo-button',
545                 cn: [
546                     {
547                         tag: 'span',
548                         'data-on-text':'ON',
549                         'data-off-text':'OFF',
550                         cls: 'slider-button',
551                         html: this.offtext
552                     }
553                 ]
554             };
555             
556             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
557                 cfg.cls += ' '+this.weight;
558             }
559             
560             return cfg;
561         }
562         
563         if (this.isClose) {
564             cfg.cls += ' close';
565             
566             cfg["aria-hidden"] = true;
567             
568             cfg.html = "&times;";
569             
570             return cfg;
571         }
572         
573          
574         if (this.theme==='default') {
575             cfg.cls = 'btn roo-button';
576             
577             //if (this.parentType != 'Navbar') {
578             this.weight = this.weight.length ?  this.weight : 'default';
579             //}
580             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
581                 
582                 cfg.cls += ' btn-' + this.weight;
583             }
584         } else if (this.theme==='glow') {
585             
586             cfg.tag = 'a';
587             cfg.cls = 'btn-glow roo-button';
588             
589             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
590                 
591                 cfg.cls += ' ' + this.weight;
592             }
593         }
594    
595         
596         if (this.inverse) {
597             this.cls += ' inverse';
598         }
599         
600         
601         if (this.active) {
602             cfg.cls += ' active';
603         }
604         
605         if (this.disabled) {
606             cfg.disabled = 'disabled';
607         }
608         
609         if (this.items) {
610             Roo.log('changing to ul' );
611             cfg.tag = 'ul';
612             this.glyphicon = 'caret';
613         }
614         
615         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
616          
617         //gsRoo.log(this.parentType);
618         if (this.parentType === 'Navbar' && !this.parent().bar) {
619             Roo.log('changing to li?');
620             
621             cfg.tag = 'li';
622             
623             cfg.cls = '';
624             cfg.cn =  [{
625                 tag : 'a',
626                 cls : 'roo-button',
627                 html : this.html,
628                 href : this.href || '#'
629             }];
630             if (this.menu) {
631                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
632                 cfg.cls += ' dropdown';
633             }   
634             
635             delete cfg.html;
636             
637         }
638         
639        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
640         
641         if (this.glyphicon) {
642             cfg.html = ' ' + cfg.html;
643             
644             cfg.cn = [
645                 {
646                     tag: 'span',
647                     cls: 'glyphicon glyphicon-' + this.glyphicon
648                 }
649             ];
650         }
651         
652         if (this.badge) {
653             cfg.html += ' ';
654             
655             cfg.tag = 'a';
656             
657 //            cfg.cls='btn roo-button';
658             
659             cfg.href=this.href;
660             
661             var value = cfg.html;
662             
663             if(this.glyphicon){
664                 value = {
665                             tag: 'span',
666                             cls: 'glyphicon glyphicon-' + this.glyphicon,
667                             html: this.html
668                         };
669                 
670             }
671             
672             cfg.cn = [
673                 value,
674                 {
675                     tag: 'span',
676                     cls: 'badge',
677                     html: this.badge
678                 }
679             ];
680             
681             cfg.html='';
682         }
683         
684         if (this.menu) {
685             cfg.cls += ' dropdown';
686             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
687         }
688         
689         if (cfg.tag !== 'a' && this.href !== '') {
690             throw "Tag must be a to set href.";
691         } else if (this.href.length > 0) {
692             cfg.href = this.href;
693         }
694         
695         if(this.removeClass){
696             cfg.cls = '';
697         }
698         
699         if(this.target){
700             cfg.target = this.target;
701         }
702         
703         return cfg;
704     },
705     initEvents: function() {
706        // Roo.log('init events?');
707 //        Roo.log(this.el.dom);
708         // add the menu...
709         
710         if (typeof (this.menu) != 'undefined') {
711             this.menu.parentType = this.xtype;
712             this.menu.triggerEl = this.el;
713             this.addxtype(Roo.apply({}, this.menu));
714         }
715
716
717        if (this.el.hasClass('roo-button')) {
718             this.el.on('click', this.onClick, this);
719        } else {
720             this.el.select('.roo-button').on('click', this.onClick, this);
721        }
722        
723        if(this.removeClass){
724            this.el.on('click', this.onClick, this);
725        }
726        
727        this.el.enableDisplayMode();
728         
729     },
730     onClick : function(e)
731     {
732         if (this.disabled) {
733             return;
734         }
735         
736         Roo.log('button on click ');
737         if(this.preventDefault){
738             e.preventDefault();
739         }
740         if (this.pressed === true || this.pressed === false) {
741             this.pressed = !this.pressed;
742             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
743             this.fireEvent('toggle', this, e, this.pressed);
744         }
745         
746         
747         this.fireEvent('click', this, e);
748     },
749     
750     /**
751      * Enables this button
752      */
753     enable : function()
754     {
755         this.disabled = false;
756         this.el.removeClass('disabled');
757     },
758     
759     /**
760      * Disable this button
761      */
762     disable : function()
763     {
764         this.disabled = true;
765         this.el.addClass('disabled');
766     },
767      /**
768      * sets the active state on/off, 
769      * @param {Boolean} state (optional) Force a particular state
770      */
771     setActive : function(v) {
772         
773         this.el[v ? 'addClass' : 'removeClass']('active');
774     },
775      /**
776      * toggles the current active state 
777      */
778     toggleActive : function()
779     {
780        var active = this.el.hasClass('active');
781        this.setActive(!active);
782        
783         
784     },
785     setText : function(str)
786     {
787         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
788     },
789     getText : function()
790     {
791         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
792     },
793     hide: function() {
794        
795      
796         this.el.hide();   
797     },
798     show: function() {
799        
800         this.el.show();   
801     }
802     
803     
804 });
805
806  /*
807  * - LGPL
808  *
809  * column
810  * 
811  */
812
813 /**
814  * @class Roo.bootstrap.Column
815  * @extends Roo.bootstrap.Component
816  * Bootstrap Column class
817  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
818  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
819  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
820  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
821  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
822  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
823  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
824  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
825  *
826  * 
827  * @cfg {Boolean} hidden (true|false) hide the element
828  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
829  * @cfg {String} fa (ban|check|...) font awesome icon
830  * @cfg {Number} fasize (1|2|....) font awsome size
831
832  * @cfg {String} icon (info-sign|check|...) glyphicon name
833
834  * @cfg {String} html content of column.
835  * 
836  * @constructor
837  * Create a new Column
838  * @param {Object} config The config object
839  */
840
841 Roo.bootstrap.Column = function(config){
842     Roo.bootstrap.Column.superclass.constructor.call(this, config);
843 };
844
845 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
846     
847     xs: false,
848     sm: false,
849     md: false,
850     lg: false,
851     xsoff: false,
852     smoff: false,
853     mdoff: false,
854     lgoff: false,
855     html: '',
856     offset: 0,
857     alert: false,
858     fa: false,
859     icon : false,
860     hidden : false,
861     fasize : 1,
862     
863     getAutoCreate : function(){
864         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
865         
866         cfg = {
867             tag: 'div',
868             cls: 'column'
869         };
870         
871         var settings=this;
872         ['xs','sm','md','lg'].map(function(size){
873             //Roo.log( size + ':' + settings[size]);
874             
875             if (settings[size+'off'] !== false) {
876                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
877             }
878             
879             if (settings[size] === false) {
880                 return;
881             }
882             Roo.log(settings[size]);
883             if (!settings[size]) { // 0 = hidden
884                 cfg.cls += ' hidden-' + size;
885                 return;
886             }
887             cfg.cls += ' col-' + size + '-' + settings[size];
888             
889         });
890         
891         if (this.hidden) {
892             cfg.cls += ' hidden';
893         }
894         
895         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
896             cfg.cls +=' alert alert-' + this.alert;
897         }
898         
899         
900         if (this.html.length) {
901             cfg.html = this.html;
902         }
903         if (this.fa) {
904             var fasize = '';
905             if (this.fasize > 1) {
906                 fasize = ' fa-' + this.fasize + 'x';
907             }
908             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
909             
910             
911         }
912         if (this.icon) {
913             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
914         }
915         
916         return cfg;
917     }
918    
919 });
920
921  
922
923  /*
924  * - LGPL
925  *
926  * page container.
927  * 
928  */
929
930
931 /**
932  * @class Roo.bootstrap.Container
933  * @extends Roo.bootstrap.Component
934  * Bootstrap Container class
935  * @cfg {Boolean} jumbotron is it a jumbotron element
936  * @cfg {String} html content of element
937  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
938  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
939  * @cfg {String} header content of header (for panel)
940  * @cfg {String} footer content of footer (for panel)
941  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
942  * @cfg {String} tag (header|aside|section) type of HTML tag.
943  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
944  * @cfg {String} fa (ban|check|...) font awesome icon
945  * @cfg {String} icon (info-sign|check|...) glyphicon name
946  * @cfg {Boolean} hidden (true|false) hide the element
947
948  *     
949  * @constructor
950  * Create a new Container
951  * @param {Object} config The config object
952  */
953
954 Roo.bootstrap.Container = function(config){
955     Roo.bootstrap.Container.superclass.constructor.call(this, config);
956 };
957
958 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
959     
960     jumbotron : false,
961     well: '',
962     panel : '',
963     header: '',
964     footer : '',
965     sticky: '',
966     tag : false,
967     alert : false,
968     fa: false,
969     icon : false,
970   
971      
972     getChildContainer : function() {
973         
974         if(!this.el){
975             return false;
976         }
977         
978         if (this.panel.length) {
979             return this.el.select('.panel-body',true).first();
980         }
981         
982         return this.el;
983     },
984     
985     
986     getAutoCreate : function(){
987         
988         var cfg = {
989             tag : this.tag || 'div',
990             html : '',
991             cls : ''
992         };
993         if (this.jumbotron) {
994             cfg.cls = 'jumbotron';
995         }
996         
997         
998         
999         // - this is applied by the parent..
1000         //if (this.cls) {
1001         //    cfg.cls = this.cls + '';
1002         //}
1003         
1004         if (this.sticky.length) {
1005             
1006             var bd = Roo.get(document.body);
1007             if (!bd.hasClass('bootstrap-sticky')) {
1008                 bd.addClass('bootstrap-sticky');
1009                 Roo.select('html',true).setStyle('height', '100%');
1010             }
1011              
1012             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1013         }
1014         
1015         
1016         if (this.well.length) {
1017             switch (this.well) {
1018                 case 'lg':
1019                 case 'sm':
1020                     cfg.cls +=' well well-' +this.well;
1021                     break;
1022                 default:
1023                     cfg.cls +=' well';
1024                     break;
1025             }
1026         }
1027         
1028         if (this.hidden) {
1029             cfg.cls += ' hidden';
1030         }
1031         
1032         
1033         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034             cfg.cls +=' alert alert-' + this.alert;
1035         }
1036         
1037         var body = cfg;
1038         
1039         if (this.panel.length) {
1040             cfg.cls += ' panel panel-' + this.panel;
1041             cfg.cn = [];
1042             if (this.header.length) {
1043                 cfg.cn.push({
1044                     
1045                     cls : 'panel-heading',
1046                     cn : [{
1047                         tag: 'h3',
1048                         cls : 'panel-title',
1049                         html : this.header
1050                     }]
1051                     
1052                 });
1053             }
1054             body = false;
1055             cfg.cn.push({
1056                 cls : 'panel-body',
1057                 html : this.html
1058             });
1059             
1060             
1061             if (this.footer.length) {
1062                 cfg.cn.push({
1063                     cls : 'panel-footer',
1064                     html : this.footer
1065                     
1066                 });
1067             }
1068             
1069         }
1070         
1071         if (body) {
1072             body.html = this.html || cfg.html;
1073             // prefix with the icons..
1074             if (this.fa) {
1075                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1076             }
1077             if (this.icon) {
1078                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1079             }
1080             
1081             
1082         }
1083         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1084             cfg.cls =  'container';
1085         }
1086         
1087         return cfg;
1088     },
1089     
1090     titleEl : function()
1091     {
1092         if(!this.el || !this.panel.length || !this.header.length){
1093             return;
1094         }
1095         
1096         return this.el.select('.panel-title',true).first();
1097     },
1098     
1099     setTitle : function(v)
1100     {
1101         var titleEl = this.titleEl();
1102         
1103         if(!titleEl){
1104             return;
1105         }
1106         
1107         titleEl.dom.innerHTML = v;
1108     },
1109     
1110     getTitle : function()
1111     {
1112         
1113         var titleEl = this.titleEl();
1114         
1115         if(!titleEl){
1116             return '';
1117         }
1118         
1119         return titleEl.dom.innerHTML;
1120     }
1121    
1122 });
1123
1124  /*
1125  * - LGPL
1126  *
1127  * image
1128  * 
1129  */
1130
1131
1132 /**
1133  * @class Roo.bootstrap.Img
1134  * @extends Roo.bootstrap.Component
1135  * Bootstrap Img class
1136  * @cfg {Boolean} imgResponsive false | true
1137  * @cfg {String} border rounded | circle | thumbnail
1138  * @cfg {String} src image source
1139  * @cfg {String} alt image alternative text
1140  * @cfg {String} href a tag href
1141  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1142  * 
1143  * @constructor
1144  * Create a new Input
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Img = function(config){
1149     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // img events
1153         /**
1154          * @event click
1155          * The img click event for the img.
1156          * @param {Roo.EventObject} e
1157          */
1158         "click" : true
1159     });
1160 };
1161
1162 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1163     
1164     imgResponsive: true,
1165     border: '',
1166     src: '',
1167     href: false,
1168     target: false,
1169
1170     getAutoCreate : function(){
1171         
1172         var cfg = {
1173             tag: 'img',
1174             cls: (this.imgResponsive) ? 'img-responsive' : '',
1175             html : null
1176         }
1177         
1178         cfg.html = this.html || cfg.html;
1179         
1180         cfg.src = this.src || cfg.src;
1181         
1182         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1183             cfg.cls += ' img-' + this.border;
1184         }
1185         
1186         if(this.alt){
1187             cfg.alt = this.alt;
1188         }
1189         
1190         if(this.href){
1191             var a = {
1192                 tag: 'a',
1193                 href: this.href,
1194                 cn: [
1195                     cfg
1196                 ]
1197             }
1198             
1199             if(this.target){
1200                 a.target = this.target;
1201             }
1202             
1203         }
1204         
1205         
1206         return (this.href) ? a : cfg;
1207     },
1208     
1209     initEvents: function() {
1210         
1211         if(!this.href){
1212             this.el.on('click', this.onClick, this);
1213         }
1214     },
1215     
1216     onClick : function(e)
1217     {
1218         Roo.log('img onclick');
1219         this.fireEvent('click', this, e);
1220     }
1221    
1222 });
1223
1224  /*
1225  * - LGPL
1226  *
1227  * image
1228  * 
1229  */
1230
1231
1232 /**
1233  * @class Roo.bootstrap.Link
1234  * @extends Roo.bootstrap.Component
1235  * Bootstrap Link Class
1236  * @cfg {String} alt image alternative text
1237  * @cfg {String} href a tag href
1238  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1239  * @cfg {String} html the content of the link.
1240  * @cfg {String} anchor name for the anchor link
1241
1242  * @cfg {Boolean} preventDefault (true | false) default false
1243
1244  * 
1245  * @constructor
1246  * Create a new Input
1247  * @param {Object} config The config object
1248  */
1249
1250 Roo.bootstrap.Link = function(config){
1251     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1252     
1253     this.addEvents({
1254         // img events
1255         /**
1256          * @event click
1257          * The img click event for the img.
1258          * @param {Roo.EventObject} e
1259          */
1260         "click" : true
1261     });
1262 };
1263
1264 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1265     
1266     href: false,
1267     target: false,
1268     preventDefault: false,
1269     anchor : false,
1270     alt : false,
1271
1272     getAutoCreate : function()
1273     {
1274         
1275         var cfg = {
1276             tag: 'a'
1277         };
1278         // anchor's do not require html/href...
1279         if (this.anchor === false) {
1280             cfg.html = this.html || 'html-missing';
1281             cfg.href = this.href || '#';
1282         } else {
1283             cfg.name = this.anchor;
1284             if (this.html !== false) {
1285                 cfg.html = this.html;
1286             }
1287             if (this.href !== false) {
1288                 cfg.href = this.href;
1289             }
1290         }
1291         
1292         if(this.alt !== false){
1293             cfg.alt = this.alt;
1294         }
1295         
1296         
1297         if(this.target !== false) {
1298             cfg.target = this.target;
1299         }
1300         
1301         return cfg;
1302     },
1303     
1304     initEvents: function() {
1305         
1306         if(!this.href || this.preventDefault){
1307             this.el.on('click', this.onClick, this);
1308         }
1309     },
1310     
1311     onClick : function(e)
1312     {
1313         if(this.preventDefault){
1314             e.preventDefault();
1315         }
1316         //Roo.log('img onclick');
1317         this.fireEvent('click', this, e);
1318     }
1319    
1320 });
1321
1322  /*
1323  * - LGPL
1324  *
1325  * header
1326  * 
1327  */
1328
1329 /**
1330  * @class Roo.bootstrap.Header
1331  * @extends Roo.bootstrap.Component
1332  * Bootstrap Header class
1333  * @cfg {String} html content of header
1334  * @cfg {Number} level (1|2|3|4|5|6) default 1
1335  * 
1336  * @constructor
1337  * Create a new Header
1338  * @param {Object} config The config object
1339  */
1340
1341
1342 Roo.bootstrap.Header  = function(config){
1343     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1344 };
1345
1346 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1347     
1348     //href : false,
1349     html : false,
1350     level : 1,
1351     
1352     
1353     
1354     getAutoCreate : function(){
1355         
1356         var cfg = {
1357             tag: 'h' + (1 *this.level),
1358             html: this.html || 'fill in html'
1359         } ;
1360         
1361         return cfg;
1362     }
1363    
1364 });
1365
1366  
1367
1368  /*
1369  * Based on:
1370  * Ext JS Library 1.1.1
1371  * Copyright(c) 2006-2007, Ext JS, LLC.
1372  *
1373  * Originally Released Under LGPL - original licence link has changed is not relivant.
1374  *
1375  * Fork - LGPL
1376  * <script type="text/javascript">
1377  */
1378  
1379 /**
1380  * @class Roo.bootstrap.MenuMgr
1381  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1382  * @singleton
1383  */
1384 Roo.bootstrap.MenuMgr = function(){
1385    var menus, active, groups = {}, attached = false, lastShow = new Date();
1386
1387    // private - called when first menu is created
1388    function init(){
1389        menus = {};
1390        active = new Roo.util.MixedCollection();
1391        Roo.get(document).addKeyListener(27, function(){
1392            if(active.length > 0){
1393                hideAll();
1394            }
1395        });
1396    }
1397
1398    // private
1399    function hideAll(){
1400        if(active && active.length > 0){
1401            var c = active.clone();
1402            c.each(function(m){
1403                m.hide();
1404            });
1405        }
1406    }
1407
1408    // private
1409    function onHide(m){
1410        active.remove(m);
1411        if(active.length < 1){
1412            Roo.get(document).un("mouseup", onMouseDown);
1413             
1414            attached = false;
1415        }
1416    }
1417
1418    // private
1419    function onShow(m){
1420        var last = active.last();
1421        lastShow = new Date();
1422        active.add(m);
1423        if(!attached){
1424           Roo.get(document).on("mouseup", onMouseDown);
1425            
1426            attached = true;
1427        }
1428        if(m.parentMenu){
1429           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1430           m.parentMenu.activeChild = m;
1431        }else if(last && last.isVisible()){
1432           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1433        }
1434    }
1435
1436    // private
1437    function onBeforeHide(m){
1438        if(m.activeChild){
1439            m.activeChild.hide();
1440        }
1441        if(m.autoHideTimer){
1442            clearTimeout(m.autoHideTimer);
1443            delete m.autoHideTimer;
1444        }
1445    }
1446
1447    // private
1448    function onBeforeShow(m){
1449        var pm = m.parentMenu;
1450        if(!pm && !m.allowOtherMenus){
1451            hideAll();
1452        }else if(pm && pm.activeChild && active != m){
1453            pm.activeChild.hide();
1454        }
1455    }
1456
1457    // private
1458    function onMouseDown(e){
1459         Roo.log("on MouseDown");
1460         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1461            hideAll();
1462         }
1463         
1464         
1465    }
1466
1467    // private
1468    function onBeforeCheck(mi, state){
1469        if(state){
1470            var g = groups[mi.group];
1471            for(var i = 0, l = g.length; i < l; i++){
1472                if(g[i] != mi){
1473                    g[i].setChecked(false);
1474                }
1475            }
1476        }
1477    }
1478
1479    return {
1480
1481        /**
1482         * Hides all menus that are currently visible
1483         */
1484        hideAll : function(){
1485             hideAll();  
1486        },
1487
1488        // private
1489        register : function(menu){
1490            if(!menus){
1491                init();
1492            }
1493            menus[menu.id] = menu;
1494            menu.on("beforehide", onBeforeHide);
1495            menu.on("hide", onHide);
1496            menu.on("beforeshow", onBeforeShow);
1497            menu.on("show", onShow);
1498            var g = menu.group;
1499            if(g && menu.events["checkchange"]){
1500                if(!groups[g]){
1501                    groups[g] = [];
1502                }
1503                groups[g].push(menu);
1504                menu.on("checkchange", onCheck);
1505            }
1506        },
1507
1508         /**
1509          * Returns a {@link Roo.menu.Menu} object
1510          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1511          * be used to generate and return a new Menu instance.
1512          */
1513        get : function(menu){
1514            if(typeof menu == "string"){ // menu id
1515                return menus[menu];
1516            }else if(menu.events){  // menu instance
1517                return menu;
1518            }
1519            /*else if(typeof menu.length == 'number'){ // array of menu items?
1520                return new Roo.bootstrap.Menu({items:menu});
1521            }else{ // otherwise, must be a config
1522                return new Roo.bootstrap.Menu(menu);
1523            }
1524            */
1525            return false;
1526        },
1527
1528        // private
1529        unregister : function(menu){
1530            delete menus[menu.id];
1531            menu.un("beforehide", onBeforeHide);
1532            menu.un("hide", onHide);
1533            menu.un("beforeshow", onBeforeShow);
1534            menu.un("show", onShow);
1535            var g = menu.group;
1536            if(g && menu.events["checkchange"]){
1537                groups[g].remove(menu);
1538                menu.un("checkchange", onCheck);
1539            }
1540        },
1541
1542        // private
1543        registerCheckable : function(menuItem){
1544            var g = menuItem.group;
1545            if(g){
1546                if(!groups[g]){
1547                    groups[g] = [];
1548                }
1549                groups[g].push(menuItem);
1550                menuItem.on("beforecheckchange", onBeforeCheck);
1551            }
1552        },
1553
1554        // private
1555        unregisterCheckable : function(menuItem){
1556            var g = menuItem.group;
1557            if(g){
1558                groups[g].remove(menuItem);
1559                menuItem.un("beforecheckchange", onBeforeCheck);
1560            }
1561        }
1562    };
1563 }();/*
1564  * - LGPL
1565  *
1566  * menu
1567  * 
1568  */
1569
1570 /**
1571  * @class Roo.bootstrap.Menu
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Menu class - container for MenuItems
1574  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1575  * 
1576  * @constructor
1577  * Create a new Menu
1578  * @param {Object} config The config object
1579  */
1580
1581
1582 Roo.bootstrap.Menu = function(config){
1583     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1584     if (this.registerMenu) {
1585         Roo.bootstrap.MenuMgr.register(this);
1586     }
1587     this.addEvents({
1588         /**
1589          * @event beforeshow
1590          * Fires before this menu is displayed
1591          * @param {Roo.menu.Menu} this
1592          */
1593         beforeshow : true,
1594         /**
1595          * @event beforehide
1596          * Fires before this menu is hidden
1597          * @param {Roo.menu.Menu} this
1598          */
1599         beforehide : true,
1600         /**
1601          * @event show
1602          * Fires after this menu is displayed
1603          * @param {Roo.menu.Menu} this
1604          */
1605         show : true,
1606         /**
1607          * @event hide
1608          * Fires after this menu is hidden
1609          * @param {Roo.menu.Menu} this
1610          */
1611         hide : true,
1612         /**
1613          * @event click
1614          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1615          * @param {Roo.menu.Menu} this
1616          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1617          * @param {Roo.EventObject} e
1618          */
1619         click : true,
1620         /**
1621          * @event mouseover
1622          * Fires when the mouse is hovering over this menu
1623          * @param {Roo.menu.Menu} this
1624          * @param {Roo.EventObject} e
1625          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1626          */
1627         mouseover : true,
1628         /**
1629          * @event mouseout
1630          * Fires when the mouse exits this menu
1631          * @param {Roo.menu.Menu} this
1632          * @param {Roo.EventObject} e
1633          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1634          */
1635         mouseout : true,
1636         /**
1637          * @event itemclick
1638          * Fires when a menu item contained in this menu is clicked
1639          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1640          * @param {Roo.EventObject} e
1641          */
1642         itemclick: true
1643     });
1644     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1645 };
1646
1647 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1648     
1649    /// html : false,
1650     //align : '',
1651     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1652     type: false,
1653     /**
1654      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1655      */
1656     registerMenu : true,
1657     
1658     menuItems :false, // stores the menu items..
1659     
1660     hidden:true,
1661     
1662     parentMenu : false,
1663     
1664     getChildContainer : function() {
1665         return this.el;  
1666     },
1667     
1668     getAutoCreate : function(){
1669          
1670         //if (['right'].indexOf(this.align)!==-1) {
1671         //    cfg.cn[1].cls += ' pull-right'
1672         //}
1673         
1674         
1675         var cfg = {
1676             tag : 'ul',
1677             cls : 'dropdown-menu' ,
1678             style : 'z-index:1000'
1679             
1680         }
1681         
1682         if (this.type === 'submenu') {
1683             cfg.cls = 'submenu active';
1684         }
1685         if (this.type === 'treeview') {
1686             cfg.cls = 'treeview-menu';
1687         }
1688         
1689         return cfg;
1690     },
1691     initEvents : function() {
1692         
1693        // Roo.log("ADD event");
1694        // Roo.log(this.triggerEl.dom);
1695         this.triggerEl.on('click', this.onTriggerPress, this);
1696         this.triggerEl.addClass('dropdown-toggle');
1697         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1698
1699         this.el.on("mouseover", this.onMouseOver, this);
1700         this.el.on("mouseout", this.onMouseOut, this);
1701         
1702         
1703     },
1704     findTargetItem : function(e){
1705         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1706         if(!t){
1707             return false;
1708         }
1709         //Roo.log(t);         Roo.log(t.id);
1710         if(t && t.id){
1711             //Roo.log(this.menuitems);
1712             return this.menuitems.get(t.id);
1713             
1714             //return this.items.get(t.menuItemId);
1715         }
1716         
1717         return false;
1718     },
1719     onClick : function(e){
1720         Roo.log("menu.onClick");
1721         var t = this.findTargetItem(e);
1722         if(!t || t.isContainer){
1723             return;
1724         }
1725         Roo.log(e);
1726         /*
1727         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1728             if(t == this.activeItem && t.shouldDeactivate(e)){
1729                 this.activeItem.deactivate();
1730                 delete this.activeItem;
1731                 return;
1732             }
1733             if(t.canActivate){
1734                 this.setActiveItem(t, true);
1735             }
1736             return;
1737             
1738             
1739         }
1740         */
1741        
1742         Roo.log('pass click event');
1743         
1744         t.onClick(e);
1745         
1746         this.fireEvent("click", this, t, e);
1747         
1748         this.hide();
1749     },
1750      onMouseOver : function(e){
1751         var t  = this.findTargetItem(e);
1752         //Roo.log(t);
1753         //if(t){
1754         //    if(t.canActivate && !t.disabled){
1755         //        this.setActiveItem(t, true);
1756         //    }
1757         //}
1758         
1759         this.fireEvent("mouseover", this, e, t);
1760     },
1761     isVisible : function(){
1762         return !this.hidden;
1763     },
1764      onMouseOut : function(e){
1765         var t  = this.findTargetItem(e);
1766         
1767         //if(t ){
1768         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1769         //        this.activeItem.deactivate();
1770         //        delete this.activeItem;
1771         //    }
1772         //}
1773         this.fireEvent("mouseout", this, e, t);
1774     },
1775     
1776     
1777     /**
1778      * Displays this menu relative to another element
1779      * @param {String/HTMLElement/Roo.Element} element The element to align to
1780      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1781      * the element (defaults to this.defaultAlign)
1782      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1783      */
1784     show : function(el, pos, parentMenu){
1785         this.parentMenu = parentMenu;
1786         if(!this.el){
1787             this.render();
1788         }
1789         this.fireEvent("beforeshow", this);
1790         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1791     },
1792      /**
1793      * Displays this menu at a specific xy position
1794      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1795      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1796      */
1797     showAt : function(xy, parentMenu, /* private: */_e){
1798         this.parentMenu = parentMenu;
1799         if(!this.el){
1800             this.render();
1801         }
1802         if(_e !== false){
1803             this.fireEvent("beforeshow", this);
1804             
1805             //xy = this.el.adjustForConstraints(xy);
1806         }
1807         //this.el.setXY(xy);
1808         //this.el.show();
1809         this.hideMenuItems();
1810         this.hidden = false;
1811         this.triggerEl.addClass('open');
1812         this.focus();
1813         this.fireEvent("show", this);
1814     },
1815     
1816     focus : function(){
1817         return;
1818         if(!this.hidden){
1819             this.doFocus.defer(50, this);
1820         }
1821     },
1822
1823     doFocus : function(){
1824         if(!this.hidden){
1825             this.focusEl.focus();
1826         }
1827     },
1828
1829     /**
1830      * Hides this menu and optionally all parent menus
1831      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1832      */
1833     hide : function(deep){
1834         
1835         this.hideMenuItems();
1836         if(this.el && this.isVisible()){
1837             this.fireEvent("beforehide", this);
1838             if(this.activeItem){
1839                 this.activeItem.deactivate();
1840                 this.activeItem = null;
1841             }
1842             this.triggerEl.removeClass('open');;
1843             this.hidden = true;
1844             this.fireEvent("hide", this);
1845         }
1846         if(deep === true && this.parentMenu){
1847             this.parentMenu.hide(true);
1848         }
1849     },
1850     
1851     onTriggerPress  : function(e)
1852     {
1853         
1854         Roo.log('trigger press');
1855         //Roo.log(e.getTarget());
1856        // Roo.log(this.triggerEl.dom);
1857         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1858             return;
1859         }
1860         if (this.isVisible()) {
1861             Roo.log('hide');
1862             this.hide();
1863         } else {
1864             this.show(this.triggerEl, false, false);
1865         }
1866         
1867         
1868     },
1869     
1870          
1871        
1872     
1873     hideMenuItems : function()
1874     {
1875         //$(backdrop).remove()
1876         Roo.select('.open',true).each(function(aa) {
1877             
1878             aa.removeClass('open');
1879           //var parent = getParent($(this))
1880           //var relatedTarget = { relatedTarget: this }
1881           
1882            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1883           //if (e.isDefaultPrevented()) return
1884            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1885         })
1886     },
1887     addxtypeChild : function (tree, cntr) {
1888         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1889           
1890         this.menuitems.add(comp);
1891         return comp;
1892
1893     },
1894     getEl : function()
1895     {
1896         Roo.log(this.el);
1897         return this.el;
1898     }
1899 });
1900
1901  
1902  /*
1903  * - LGPL
1904  *
1905  * menu item
1906  * 
1907  */
1908
1909
1910 /**
1911  * @class Roo.bootstrap.MenuItem
1912  * @extends Roo.bootstrap.Component
1913  * Bootstrap MenuItem class
1914  * @cfg {String} html the menu label
1915  * @cfg {String} href the link
1916  * @cfg {Boolean} preventDefault (true | false) default true
1917  * @cfg {Boolean} isContainer (true | false) default false
1918  * 
1919  * 
1920  * @constructor
1921  * Create a new MenuItem
1922  * @param {Object} config The config object
1923  */
1924
1925
1926 Roo.bootstrap.MenuItem = function(config){
1927     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1928     this.addEvents({
1929         // raw events
1930         /**
1931          * @event click
1932          * The raw click event for the entire grid.
1933          * @param {Roo.EventObject} e
1934          */
1935         "click" : true
1936     });
1937 };
1938
1939 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1940     
1941     href : false,
1942     html : false,
1943     preventDefault: true,
1944     isContainer : false,
1945     
1946     getAutoCreate : function(){
1947         
1948         if(this.isContainer){
1949             return {
1950                 tag: 'li',
1951                 cls: 'dropdown-menu-item'
1952             };
1953         }
1954         
1955         var cfg= {
1956             tag: 'li',
1957             cls: 'dropdown-menu-item',
1958             cn: [
1959                     {
1960                         tag : 'a',
1961                         href : '#',
1962                         html : 'Link'
1963                     }
1964                 ]
1965         };
1966         if (this.parent().type == 'treeview') {
1967             cfg.cls = 'treeview-menu';
1968         }
1969         
1970         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1971         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1972         return cfg;
1973     },
1974     
1975     initEvents: function() {
1976         
1977         //this.el.select('a').on('click', this.onClick, this);
1978         
1979     },
1980     onClick : function(e)
1981     {
1982         Roo.log('item on click ');
1983         //if(this.preventDefault){
1984         //    e.preventDefault();
1985         //}
1986         //this.parent().hideMenuItems();
1987         
1988         this.fireEvent('click', this, e);
1989     },
1990     getEl : function()
1991     {
1992         return this.el;
1993     }
1994 });
1995
1996  
1997
1998  /*
1999  * - LGPL
2000  *
2001  * menu separator
2002  * 
2003  */
2004
2005
2006 /**
2007  * @class Roo.bootstrap.MenuSeparator
2008  * @extends Roo.bootstrap.Component
2009  * Bootstrap MenuSeparator class
2010  * 
2011  * @constructor
2012  * Create a new MenuItem
2013  * @param {Object} config The config object
2014  */
2015
2016
2017 Roo.bootstrap.MenuSeparator = function(config){
2018     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2019 };
2020
2021 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2022     
2023     getAutoCreate : function(){
2024         var cfg = {
2025             cls: 'divider',
2026             tag : 'li'
2027         };
2028         
2029         return cfg;
2030     }
2031    
2032 });
2033
2034  
2035
2036  
2037 /*
2038 <div class="modal fade">
2039   <div class="modal-dialog">
2040     <div class="modal-content">
2041       <div class="modal-header">
2042         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
2043         <h4 class="modal-title">Modal title</h4>
2044       </div>
2045       <div class="modal-body">
2046         <p>One fine body&hellip;</p>
2047       </div>
2048       <div class="modal-footer">
2049         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2050         <button type="button" class="btn btn-primary">Save changes</button>
2051       </div>
2052     </div><!-- /.modal-content -->
2053   </div><!-- /.modal-dialog -->
2054 </div><!-- /.modal -->
2055 */
2056 /*
2057  * - LGPL
2058  *
2059  * page contgainer.
2060  * 
2061  */
2062
2063 /**
2064  * @class Roo.bootstrap.Modal
2065  * @extends Roo.bootstrap.Component
2066  * Bootstrap Modal class
2067  * @cfg {String} title Title of dialog
2068  * @cfg {Boolean} specificTitle default false
2069  * @cfg {Array} buttons Array of buttons or standard button set..
2070  * @cfg {String} buttonPosition (left|right|center) default right
2071  * @cfg {Boolean} animate default true
2072  * @cfg {Boolean} allow_close default true
2073  * 
2074  * @constructor
2075  * Create a new Modal Dialog
2076  * @param {Object} config The config object
2077  */
2078
2079 Roo.bootstrap.Modal = function(config){
2080     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2081     this.addEvents({
2082         // raw events
2083         /**
2084          * @event btnclick
2085          * The raw btnclick event for the button
2086          * @param {Roo.EventObject} e
2087          */
2088         "btnclick" : true
2089     });
2090     this.buttons = this.buttons || [];
2091 };
2092
2093 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2094     
2095     title : 'test dialog',
2096    
2097     buttons : false,
2098     
2099     // set on load...
2100     body:  false,
2101     
2102     specificTitle: false,
2103     
2104     buttonPosition: 'right',
2105     
2106     allow_close : true,
2107     
2108     animate : true,
2109     
2110     onRender : function(ct, position)
2111     {
2112         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2113      
2114         if(!this.el){
2115             var cfg = Roo.apply({},  this.getAutoCreate());
2116             cfg.id = Roo.id();
2117             //if(!cfg.name){
2118             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2119             //}
2120             //if (!cfg.name.length) {
2121             //    delete cfg.name;
2122            // }
2123             if (this.cls) {
2124                 cfg.cls += ' ' + this.cls;
2125             }
2126             if (this.style) {
2127                 cfg.style = this.style;
2128             }
2129             this.el = Roo.get(document.body).createChild(cfg, position);
2130         }
2131         //var type = this.el.dom.type;
2132         
2133         if(this.tabIndex !== undefined){
2134             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2135         }
2136         
2137         
2138         
2139         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2140         this.maskEl.enableDisplayMode("block");
2141         this.maskEl.hide();
2142         //this.el.addClass("x-dlg-modal");
2143     
2144         if (this.buttons.length) {
2145             Roo.each(this.buttons, function(bb) {
2146                 b = Roo.apply({}, bb);
2147                 b.xns = b.xns || Roo.bootstrap;
2148                 b.xtype = b.xtype || 'Button';
2149                 if (typeof(b.listeners) == 'undefined') {
2150                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2151                 }
2152                 
2153                 var btn = Roo.factory(b);
2154                 
2155                 btn.onRender(this.el.select('.modal-footer div').first());
2156                 
2157             },this);
2158         }
2159         // render the children.
2160         var nitems = [];
2161         
2162         if(typeof(this.items) != 'undefined'){
2163             var items = this.items;
2164             delete this.items;
2165
2166             for(var i =0;i < items.length;i++) {
2167                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2168             }
2169         }
2170         
2171         this.items = nitems;
2172         
2173         this.body = this.el.select('.modal-body',true).first();
2174         this.close = this.el.select('.modal-header .close', true).first();
2175         this.footer = this.el.select('.modal-footer',true).first();
2176         this.initEvents();
2177         //this.el.addClass([this.fieldClass, this.cls]);
2178         
2179     },
2180     getAutoCreate : function(){
2181         
2182         
2183         var bdy = {
2184                 cls : 'modal-body',
2185                 html : this.html || ''
2186         };
2187         
2188         var title = {
2189             tag: 'h4',
2190             cls : 'modal-title',
2191             html : this.title
2192         };
2193         
2194         if(this.specificTitle){
2195             title = this.title;
2196             
2197         };
2198         
2199         var header = [];
2200         if (this.allow_close) {
2201             header.push({
2202                 tag: 'button',
2203                 cls : 'close',
2204                 html : '&times'
2205             });
2206         }
2207         header.push(title);
2208         
2209         var modal = {
2210             cls: "modal",
2211             style : 'display: none',
2212             cn : [
2213                 {
2214                     cls: "modal-dialog",
2215                     cn : [
2216                         {
2217                             cls : "modal-content",
2218                             cn : [
2219                                 {
2220                                     cls : 'modal-header',
2221                                     cn : header
2222                                 },
2223                                 bdy,
2224                                 {
2225                                     cls : 'modal-footer',
2226                                     cn : [
2227                                         {
2228                                             tag: 'div',
2229                                             cls: 'btn-' + this.buttonPosition
2230                                         }
2231                                     ]
2232                                     
2233                                 }
2234                                 
2235                                 
2236                             ]
2237                             
2238                         }
2239                     ]
2240                         
2241                 }
2242             ]
2243         };
2244         
2245         if(this.animate){
2246             modal.cls += ' fade';
2247         }
2248         
2249         return modal;
2250           
2251     },
2252     getChildContainer : function() {
2253          
2254          return this.el.select('.modal-body',true).first();
2255         
2256     },
2257     getButtonContainer : function() {
2258          return this.el.select('.modal-footer div',true).first();
2259         
2260     },
2261     initEvents : function()
2262     {
2263         this.el.select('.modal-header .close').on('click', this.hide, this);
2264 //        
2265 //        this.addxtype(this);
2266     },
2267     show : function() {
2268         
2269         if (!this.rendered) {
2270             this.render();
2271         }
2272         
2273         this.el.setStyle('display', 'block');
2274         
2275         if(this.animate){
2276             var _this = this;
2277             (function(){ _this.el.addClass('in'); }).defer(50);
2278         }else{
2279             this.el.addClass('in');
2280         }
2281         
2282         Roo.get(document.body).addClass("x-body-masked");
2283         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2284         this.maskEl.show();
2285         this.el.setStyle('zIndex', '10001');
2286         this.fireEvent('show', this);
2287         
2288         
2289     },
2290     hide : function()
2291     {
2292         this.maskEl.hide();
2293         Roo.get(document.body).removeClass("x-body-masked");
2294         this.el.removeClass('in');
2295         
2296         if(this.animate){
2297             var _this = this;
2298             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2299         }else{
2300             this.el.setStyle('display', 'none');
2301         }
2302         
2303         this.fireEvent('hide', this);
2304     },
2305     
2306     addButton : function(str, cb)
2307     {
2308          
2309         
2310         var b = Roo.apply({}, { html : str } );
2311         b.xns = b.xns || Roo.bootstrap;
2312         b.xtype = b.xtype || 'Button';
2313         if (typeof(b.listeners) == 'undefined') {
2314             b.listeners = { click : cb.createDelegate(this)  };
2315         }
2316         
2317         var btn = Roo.factory(b);
2318            
2319         btn.onRender(this.el.select('.modal-footer div').first());
2320         
2321         return btn;   
2322        
2323     },
2324     
2325     setDefaultButton : function(btn)
2326     {
2327         //this.el.select('.modal-footer').()
2328     },
2329     resizeTo: function(w,h)
2330     {
2331         // skip..
2332     },
2333     setContentSize  : function(w, h)
2334     {
2335         
2336     },
2337     onButtonClick: function(btn,e)
2338     {
2339         //Roo.log([a,b,c]);
2340         this.fireEvent('btnclick', btn.name, e);
2341     },
2342     setTitle: function(str) {
2343         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2344         
2345     }
2346 });
2347
2348
2349 Roo.apply(Roo.bootstrap.Modal,  {
2350     /**
2351          * Button config that displays a single OK button
2352          * @type Object
2353          */
2354         OK :  [{
2355             name : 'ok',
2356             weight : 'primary',
2357             html : 'OK'
2358         }], 
2359         /**
2360          * Button config that displays Yes and No buttons
2361          * @type Object
2362          */
2363         YESNO : [
2364             {
2365                 name  : 'no',
2366                 html : 'No'
2367             },
2368             {
2369                 name  :'yes',
2370                 weight : 'primary',
2371                 html : 'Yes'
2372             }
2373         ],
2374         
2375         /**
2376          * Button config that displays OK and Cancel buttons
2377          * @type Object
2378          */
2379         OKCANCEL : [
2380             {
2381                name : 'cancel',
2382                 html : 'Cancel'
2383             },
2384             {
2385                 name : 'ok',
2386                 weight : 'primary',
2387                 html : 'OK'
2388             }
2389         ],
2390         /**
2391          * Button config that displays Yes, No and Cancel buttons
2392          * @type Object
2393          */
2394         YESNOCANCEL : [
2395             {
2396                 name : 'yes',
2397                 weight : 'primary',
2398                 html : 'Yes'
2399             },
2400             {
2401                 name : 'no',
2402                 html : 'No'
2403             },
2404             {
2405                 name : 'cancel',
2406                 html : 'Cancel'
2407             }
2408         ]
2409 });
2410  /*
2411  * - LGPL
2412  *
2413  * messagebox - can be used as a replace
2414  * 
2415  */
2416 /**
2417  * @class Roo.MessageBox
2418  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2419  * Example usage:
2420  *<pre><code>
2421 // Basic alert:
2422 Roo.Msg.alert('Status', 'Changes saved successfully.');
2423
2424 // Prompt for user data:
2425 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2426     if (btn == 'ok'){
2427         // process text value...
2428     }
2429 });
2430
2431 // Show a dialog using config options:
2432 Roo.Msg.show({
2433    title:'Save Changes?',
2434    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2435    buttons: Roo.Msg.YESNOCANCEL,
2436    fn: processResult,
2437    animEl: 'elId'
2438 });
2439 </code></pre>
2440  * @singleton
2441  */
2442 Roo.bootstrap.MessageBox = function(){
2443     var dlg, opt, mask, waitTimer;
2444     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2445     var buttons, activeTextEl, bwidth;
2446
2447     
2448     // private
2449     var handleButton = function(button){
2450         dlg.hide();
2451         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2452     };
2453
2454     // private
2455     var handleHide = function(){
2456         if(opt && opt.cls){
2457             dlg.el.removeClass(opt.cls);
2458         }
2459         //if(waitTimer){
2460         //    Roo.TaskMgr.stop(waitTimer);
2461         //    waitTimer = null;
2462         //}
2463     };
2464
2465     // private
2466     var updateButtons = function(b){
2467         var width = 0;
2468         if(!b){
2469             buttons["ok"].hide();
2470             buttons["cancel"].hide();
2471             buttons["yes"].hide();
2472             buttons["no"].hide();
2473             //dlg.footer.dom.style.display = 'none';
2474             return width;
2475         }
2476         dlg.footer.dom.style.display = '';
2477         for(var k in buttons){
2478             if(typeof buttons[k] != "function"){
2479                 if(b[k]){
2480                     buttons[k].show();
2481                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2482                     width += buttons[k].el.getWidth()+15;
2483                 }else{
2484                     buttons[k].hide();
2485                 }
2486             }
2487         }
2488         return width;
2489     };
2490
2491     // private
2492     var handleEsc = function(d, k, e){
2493         if(opt && opt.closable !== false){
2494             dlg.hide();
2495         }
2496         if(e){
2497             e.stopEvent();
2498         }
2499     };
2500
2501     return {
2502         /**
2503          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2504          * @return {Roo.BasicDialog} The BasicDialog element
2505          */
2506         getDialog : function(){
2507            if(!dlg){
2508                 dlg = new Roo.bootstrap.Modal( {
2509                     //draggable: true,
2510                     //resizable:false,
2511                     //constraintoviewport:false,
2512                     //fixedcenter:true,
2513                     //collapsible : false,
2514                     //shim:true,
2515                     //modal: true,
2516                   //  width:400,
2517                   //  height:100,
2518                     //buttonAlign:"center",
2519                     closeClick : function(){
2520                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2521                             handleButton("no");
2522                         }else{
2523                             handleButton("cancel");
2524                         }
2525                     }
2526                 });
2527                 dlg.render();
2528                 dlg.on("hide", handleHide);
2529                 mask = dlg.mask;
2530                 //dlg.addKeyListener(27, handleEsc);
2531                 buttons = {};
2532                 this.buttons = buttons;
2533                 var bt = this.buttonText;
2534                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2535                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2536                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2537                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2538                 Roo.log(buttons)
2539                 bodyEl = dlg.body.createChild({
2540
2541                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2542                         '<textarea class="roo-mb-textarea"></textarea>' +
2543                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2544                 });
2545                 msgEl = bodyEl.dom.firstChild;
2546                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2547                 textboxEl.enableDisplayMode();
2548                 textboxEl.addKeyListener([10,13], function(){
2549                     if(dlg.isVisible() && opt && opt.buttons){
2550                         if(opt.buttons.ok){
2551                             handleButton("ok");
2552                         }else if(opt.buttons.yes){
2553                             handleButton("yes");
2554                         }
2555                     }
2556                 });
2557                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2558                 textareaEl.enableDisplayMode();
2559                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2560                 progressEl.enableDisplayMode();
2561                 var pf = progressEl.dom.firstChild;
2562                 if (pf) {
2563                     pp = Roo.get(pf.firstChild);
2564                     pp.setHeight(pf.offsetHeight);
2565                 }
2566                 
2567             }
2568             return dlg;
2569         },
2570
2571         /**
2572          * Updates the message box body text
2573          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2574          * the XHTML-compliant non-breaking space character '&amp;#160;')
2575          * @return {Roo.MessageBox} This message box
2576          */
2577         updateText : function(text){
2578             if(!dlg.isVisible() && !opt.width){
2579                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2580             }
2581             msgEl.innerHTML = text || '&#160;';
2582       
2583             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2584             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2585             var w = Math.max(
2586                     Math.min(opt.width || cw , this.maxWidth), 
2587                     Math.max(opt.minWidth || this.minWidth, bwidth)
2588             );
2589             if(opt.prompt){
2590                 activeTextEl.setWidth(w);
2591             }
2592             if(dlg.isVisible()){
2593                 dlg.fixedcenter = false;
2594             }
2595             // to big, make it scroll. = But as usual stupid IE does not support
2596             // !important..
2597             
2598             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2599                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2600                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2601             } else {
2602                 bodyEl.dom.style.height = '';
2603                 bodyEl.dom.style.overflowY = '';
2604             }
2605             if (cw > w) {
2606                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2607             } else {
2608                 bodyEl.dom.style.overflowX = '';
2609             }
2610             
2611             dlg.setContentSize(w, bodyEl.getHeight());
2612             if(dlg.isVisible()){
2613                 dlg.fixedcenter = true;
2614             }
2615             return this;
2616         },
2617
2618         /**
2619          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2620          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2621          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2622          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2623          * @return {Roo.MessageBox} This message box
2624          */
2625         updateProgress : function(value, text){
2626             if(text){
2627                 this.updateText(text);
2628             }
2629             if (pp) { // weird bug on my firefox - for some reason this is not defined
2630                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2631             }
2632             return this;
2633         },        
2634
2635         /**
2636          * Returns true if the message box is currently displayed
2637          * @return {Boolean} True if the message box is visible, else false
2638          */
2639         isVisible : function(){
2640             return dlg && dlg.isVisible();  
2641         },
2642
2643         /**
2644          * Hides the message box if it is displayed
2645          */
2646         hide : function(){
2647             if(this.isVisible()){
2648                 dlg.hide();
2649             }  
2650         },
2651
2652         /**
2653          * Displays a new message box, or reinitializes an existing message box, based on the config options
2654          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2655          * The following config object properties are supported:
2656          * <pre>
2657 Property    Type             Description
2658 ----------  ---------------  ------------------------------------------------------------------------------------
2659 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2660                                    closes (defaults to undefined)
2661 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2662                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2663 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2664                                    progress and wait dialogs will ignore this property and always hide the
2665                                    close button as they can only be closed programmatically.
2666 cls               String           A custom CSS class to apply to the message box element
2667 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2668                                    displayed (defaults to 75)
2669 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2670                                    function will be btn (the name of the button that was clicked, if applicable,
2671                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2672                                    Progress and wait dialogs will ignore this option since they do not respond to
2673                                    user actions and can only be closed programmatically, so any required function
2674                                    should be called by the same code after it closes the dialog.
2675 icon              String           A CSS class that provides a background image to be used as an icon for
2676                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2677 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2678 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2679 modal             Boolean          False to allow user interaction with the page while the message box is
2680                                    displayed (defaults to true)
2681 msg               String           A string that will replace the existing message box body text (defaults
2682                                    to the XHTML-compliant non-breaking space character '&#160;')
2683 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2684 progress          Boolean          True to display a progress bar (defaults to false)
2685 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2686 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2687 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2688 title             String           The title text
2689 value             String           The string value to set into the active textbox element if displayed
2690 wait              Boolean          True to display a progress bar (defaults to false)
2691 width             Number           The width of the dialog in pixels
2692 </pre>
2693          *
2694          * Example usage:
2695          * <pre><code>
2696 Roo.Msg.show({
2697    title: 'Address',
2698    msg: 'Please enter your address:',
2699    width: 300,
2700    buttons: Roo.MessageBox.OKCANCEL,
2701    multiline: true,
2702    fn: saveAddress,
2703    animEl: 'addAddressBtn'
2704 });
2705 </code></pre>
2706          * @param {Object} config Configuration options
2707          * @return {Roo.MessageBox} This message box
2708          */
2709         show : function(options)
2710         {
2711             
2712             // this causes nightmares if you show one dialog after another
2713             // especially on callbacks..
2714              
2715             if(this.isVisible()){
2716                 
2717                 this.hide();
2718                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2719                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2720                 Roo.log("New Dialog Message:" +  options.msg )
2721                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2722                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2723                 
2724             }
2725             var d = this.getDialog();
2726             opt = options;
2727             d.setTitle(opt.title || "&#160;");
2728             d.close.setDisplayed(opt.closable !== false);
2729             activeTextEl = textboxEl;
2730             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2731             if(opt.prompt){
2732                 if(opt.multiline){
2733                     textboxEl.hide();
2734                     textareaEl.show();
2735                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2736                         opt.multiline : this.defaultTextHeight);
2737                     activeTextEl = textareaEl;
2738                 }else{
2739                     textboxEl.show();
2740                     textareaEl.hide();
2741                 }
2742             }else{
2743                 textboxEl.hide();
2744                 textareaEl.hide();
2745             }
2746             progressEl.setDisplayed(opt.progress === true);
2747             this.updateProgress(0);
2748             activeTextEl.dom.value = opt.value || "";
2749             if(opt.prompt){
2750                 dlg.setDefaultButton(activeTextEl);
2751             }else{
2752                 var bs = opt.buttons;
2753                 var db = null;
2754                 if(bs && bs.ok){
2755                     db = buttons["ok"];
2756                 }else if(bs && bs.yes){
2757                     db = buttons["yes"];
2758                 }
2759                 dlg.setDefaultButton(db);
2760             }
2761             bwidth = updateButtons(opt.buttons);
2762             this.updateText(opt.msg);
2763             if(opt.cls){
2764                 d.el.addClass(opt.cls);
2765             }
2766             d.proxyDrag = opt.proxyDrag === true;
2767             d.modal = opt.modal !== false;
2768             d.mask = opt.modal !== false ? mask : false;
2769             if(!d.isVisible()){
2770                 // force it to the end of the z-index stack so it gets a cursor in FF
2771                 document.body.appendChild(dlg.el.dom);
2772                 d.animateTarget = null;
2773                 d.show(options.animEl);
2774             }
2775             return this;
2776         },
2777
2778         /**
2779          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2780          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2781          * and closing the message box when the process is complete.
2782          * @param {String} title The title bar text
2783          * @param {String} msg The message box body text
2784          * @return {Roo.MessageBox} This message box
2785          */
2786         progress : function(title, msg){
2787             this.show({
2788                 title : title,
2789                 msg : msg,
2790                 buttons: false,
2791                 progress:true,
2792                 closable:false,
2793                 minWidth: this.minProgressWidth,
2794                 modal : true
2795             });
2796             return this;
2797         },
2798
2799         /**
2800          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2801          * If a callback function is passed it will be called after the user clicks the button, and the
2802          * id of the button that was clicked will be passed as the only parameter to the callback
2803          * (could also be the top-right close button).
2804          * @param {String} title The title bar text
2805          * @param {String} msg The message box body text
2806          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2807          * @param {Object} scope (optional) The scope of the callback function
2808          * @return {Roo.MessageBox} This message box
2809          */
2810         alert : function(title, msg, fn, scope){
2811             this.show({
2812                 title : title,
2813                 msg : msg,
2814                 buttons: this.OK,
2815                 fn: fn,
2816                 scope : scope,
2817                 modal : true
2818             });
2819             return this;
2820         },
2821
2822         /**
2823          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2824          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2825          * You are responsible for closing the message box when the process is complete.
2826          * @param {String} msg The message box body text
2827          * @param {String} title (optional) The title bar text
2828          * @return {Roo.MessageBox} This message box
2829          */
2830         wait : function(msg, title){
2831             this.show({
2832                 title : title,
2833                 msg : msg,
2834                 buttons: false,
2835                 closable:false,
2836                 progress:true,
2837                 modal:true,
2838                 width:300,
2839                 wait:true
2840             });
2841             waitTimer = Roo.TaskMgr.start({
2842                 run: function(i){
2843                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2844                 },
2845                 interval: 1000
2846             });
2847             return this;
2848         },
2849
2850         /**
2851          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2852          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2853          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2854          * @param {String} title The title bar text
2855          * @param {String} msg The message box body text
2856          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2857          * @param {Object} scope (optional) The scope of the callback function
2858          * @return {Roo.MessageBox} This message box
2859          */
2860         confirm : function(title, msg, fn, scope){
2861             this.show({
2862                 title : title,
2863                 msg : msg,
2864                 buttons: this.YESNO,
2865                 fn: fn,
2866                 scope : scope,
2867                 modal : true
2868             });
2869             return this;
2870         },
2871
2872         /**
2873          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2874          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2875          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2876          * (could also be the top-right close button) and the text that was entered will be passed as the two
2877          * parameters to the callback.
2878          * @param {String} title The title bar text
2879          * @param {String} msg The message box body text
2880          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2881          * @param {Object} scope (optional) The scope of the callback function
2882          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2883          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2884          * @return {Roo.MessageBox} This message box
2885          */
2886         prompt : function(title, msg, fn, scope, multiline){
2887             this.show({
2888                 title : title,
2889                 msg : msg,
2890                 buttons: this.OKCANCEL,
2891                 fn: fn,
2892                 minWidth:250,
2893                 scope : scope,
2894                 prompt:true,
2895                 multiline: multiline,
2896                 modal : true
2897             });
2898             return this;
2899         },
2900
2901         /**
2902          * Button config that displays a single OK button
2903          * @type Object
2904          */
2905         OK : {ok:true},
2906         /**
2907          * Button config that displays Yes and No buttons
2908          * @type Object
2909          */
2910         YESNO : {yes:true, no:true},
2911         /**
2912          * Button config that displays OK and Cancel buttons
2913          * @type Object
2914          */
2915         OKCANCEL : {ok:true, cancel:true},
2916         /**
2917          * Button config that displays Yes, No and Cancel buttons
2918          * @type Object
2919          */
2920         YESNOCANCEL : {yes:true, no:true, cancel:true},
2921
2922         /**
2923          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2924          * @type Number
2925          */
2926         defaultTextHeight : 75,
2927         /**
2928          * The maximum width in pixels of the message box (defaults to 600)
2929          * @type Number
2930          */
2931         maxWidth : 600,
2932         /**
2933          * The minimum width in pixels of the message box (defaults to 100)
2934          * @type Number
2935          */
2936         minWidth : 100,
2937         /**
2938          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2939          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2940          * @type Number
2941          */
2942         minProgressWidth : 250,
2943         /**
2944          * An object containing the default button text strings that can be overriden for localized language support.
2945          * Supported properties are: ok, cancel, yes and no.
2946          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2947          * @type Object
2948          */
2949         buttonText : {
2950             ok : "OK",
2951             cancel : "Cancel",
2952             yes : "Yes",
2953             no : "No"
2954         }
2955     };
2956 }();
2957
2958 /**
2959  * Shorthand for {@link Roo.MessageBox}
2960  */
2961 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2962 Roo.Msg = Roo.Msg || Roo.MessageBox;
2963 /*
2964  * - LGPL
2965  *
2966  * navbar
2967  * 
2968  */
2969
2970 /**
2971  * @class Roo.bootstrap.Navbar
2972  * @extends Roo.bootstrap.Component
2973  * Bootstrap Navbar class
2974
2975  * @constructor
2976  * Create a new Navbar
2977  * @param {Object} config The config object
2978  */
2979
2980
2981 Roo.bootstrap.Navbar = function(config){
2982     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2983     
2984 };
2985
2986 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2987     
2988     
2989    
2990     // private
2991     navItems : false,
2992     loadMask : false,
2993     
2994     
2995     getAutoCreate : function(){
2996         
2997         
2998         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2999         
3000     },
3001     
3002     initEvents :function ()
3003     {
3004         //Roo.log(this.el.select('.navbar-toggle',true));
3005         this.el.select('.navbar-toggle',true).on('click', function() {
3006            // Roo.log('click');
3007             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3008         }, this);
3009         
3010         var mark = {
3011             tag: "div",
3012             cls:"x-dlg-mask"
3013         }
3014         
3015         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3016         
3017         var size = this.el.getSize();
3018         this.maskEl.setSize(size.width, size.height);
3019         this.maskEl.enableDisplayMode("block");
3020         this.maskEl.hide();
3021         
3022         if(this.loadMask){
3023             this.maskEl.show();
3024         }
3025     },
3026     
3027     
3028     getChildContainer : function()
3029     {
3030         if (this.el.select('.collapse').getCount()) {
3031             return this.el.select('.collapse',true).first();
3032         }
3033         
3034         return this.el;
3035     },
3036     
3037     mask : function()
3038     {
3039         this.maskEl.show();
3040     },
3041     
3042     unmask : function()
3043     {
3044         this.maskEl.hide();
3045     } 
3046     
3047     
3048     
3049     
3050 });
3051
3052
3053
3054  
3055
3056  /*
3057  * - LGPL
3058  *
3059  * navbar
3060  * 
3061  */
3062
3063 /**
3064  * @class Roo.bootstrap.NavSimplebar
3065  * @extends Roo.bootstrap.Navbar
3066  * Bootstrap Sidebar class
3067  *
3068  * @cfg {Boolean} inverse is inverted color
3069  * 
3070  * @cfg {String} type (nav | pills | tabs)
3071  * @cfg {Boolean} arrangement stacked | justified
3072  * @cfg {String} align (left | right) alignment
3073  * 
3074  * @cfg {Boolean} main (true|false) main nav bar? default false
3075  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3076  * 
3077  * @cfg {String} tag (header|footer|nav|div) default is nav 
3078
3079  * 
3080  * 
3081  * 
3082  * @constructor
3083  * Create a new Sidebar
3084  * @param {Object} config The config object
3085  */
3086
3087
3088 Roo.bootstrap.NavSimplebar = function(config){
3089     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3090 };
3091
3092 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3093     
3094     inverse: false,
3095     
3096     type: false,
3097     arrangement: '',
3098     align : false,
3099     
3100     
3101     
3102     main : false,
3103     
3104     
3105     tag : false,
3106     
3107     
3108     getAutoCreate : function(){
3109         
3110         
3111         var cfg = {
3112             tag : this.tag || 'div',
3113             cls : 'navbar'
3114         };
3115           
3116         
3117         cfg.cn = [
3118             {
3119                 cls: 'nav',
3120                 tag : 'ul'
3121             }
3122         ];
3123         
3124          
3125         this.type = this.type || 'nav';
3126         if (['tabs','pills'].indexOf(this.type)!==-1) {
3127             cfg.cn[0].cls += ' nav-' + this.type
3128         
3129         
3130         } else {
3131             if (this.type!=='nav') {
3132                 Roo.log('nav type must be nav/tabs/pills')
3133             }
3134             cfg.cn[0].cls += ' navbar-nav'
3135         }
3136         
3137         
3138         
3139         
3140         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3141             cfg.cn[0].cls += ' nav-' + this.arrangement;
3142         }
3143         
3144         
3145         if (this.align === 'right') {
3146             cfg.cn[0].cls += ' navbar-right';
3147         }
3148         
3149         if (this.inverse) {
3150             cfg.cls += ' navbar-inverse';
3151             
3152         }
3153         
3154         
3155         return cfg;
3156     
3157         
3158     }
3159     
3160     
3161     
3162 });
3163
3164
3165
3166  
3167
3168  
3169        /*
3170  * - LGPL
3171  *
3172  * navbar
3173  * 
3174  */
3175
3176 /**
3177  * @class Roo.bootstrap.NavHeaderbar
3178  * @extends Roo.bootstrap.NavSimplebar
3179  * Bootstrap Sidebar class
3180  *
3181  * @cfg {String} brand what is brand
3182  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3183  * @cfg {String} brand_href href of the brand
3184  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3185  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3186  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3187  * 
3188  * @constructor
3189  * Create a new Sidebar
3190  * @param {Object} config The config object
3191  */
3192
3193
3194 Roo.bootstrap.NavHeaderbar = function(config){
3195     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3196       
3197 };
3198
3199 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3200     
3201     position: '',
3202     brand: '',
3203     brand_href: false,
3204     srButton : true,
3205     autohide : false,
3206    
3207     
3208     getAutoCreate : function(){
3209         
3210         var   cfg = {
3211             tag: this.nav || 'nav',
3212             cls: 'navbar',
3213             role: 'navigation',
3214             cn: []
3215         };
3216         
3217         if(this.srButton){
3218             cfg.cn.push({
3219                 tag: 'div',
3220                 cls: 'navbar-header',
3221                 cn: [
3222                     {
3223                         tag: 'button',
3224                         type: 'button',
3225                         cls: 'navbar-toggle',
3226                         'data-toggle': 'collapse',
3227                         cn: [
3228                             {
3229                                 tag: 'span',
3230                                 cls: 'sr-only',
3231                                 html: 'Toggle navigation'
3232                             },
3233                             {
3234                                 tag: 'span',
3235                                 cls: 'icon-bar'
3236                             },
3237                             {
3238                                 tag: 'span',
3239                                 cls: 'icon-bar'
3240                             },
3241                             {
3242                                 tag: 'span',
3243                                 cls: 'icon-bar'
3244                             }
3245                         ]
3246                     }
3247                 ]
3248             });
3249         }
3250         
3251         cfg.cn.push({
3252             tag: 'div',
3253             cls: 'collapse navbar-collapse',
3254             cn : []
3255         });
3256         
3257         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3258         
3259         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3260             cfg.cls += ' navbar-' + this.position;
3261             
3262             // tag can override this..
3263             
3264             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3265         }
3266         
3267         if (this.brand !== '') {
3268             cfg.cn[0].cn.push({
3269                 tag: 'a',
3270                 href: this.brand_href ? this.brand_href : '#',
3271                 cls: 'navbar-brand',
3272                 cn: [
3273                 this.brand
3274                 ]
3275             });
3276         }
3277         
3278         if(this.main){
3279             cfg.cls += ' main-nav';
3280         }
3281         
3282         
3283         return cfg;
3284
3285         
3286     },
3287     getHeaderChildContainer : function()
3288     {
3289         if (this.el.select('.navbar-header').getCount()) {
3290             return this.el.select('.navbar-header',true).first();
3291         }
3292         
3293         return this.getChildContainer();
3294     },
3295     
3296     
3297     initEvents : function()
3298     {
3299         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3300         
3301         if (this.autohide) {
3302             
3303             var prevScroll = 0;
3304             var ft = this.el;
3305             
3306             Roo.get(document).on('scroll',function(e) {
3307                 var ns = Roo.get(document).getScroll().top;
3308                 var os = prevScroll;
3309                 prevScroll = ns;
3310                 
3311                 if(ns > os){
3312                     ft.removeClass('slideDown');
3313                     ft.addClass('slideUp');
3314                     return;
3315                 }
3316                 ft.removeClass('slideUp');
3317                 ft.addClass('slideDown');
3318                  
3319               
3320           },this);
3321         }
3322     }    
3323           
3324       
3325     
3326     
3327 });
3328
3329
3330
3331  
3332
3333  /*
3334  * - LGPL
3335  *
3336  * navbar
3337  * 
3338  */
3339
3340 /**
3341  * @class Roo.bootstrap.NavSidebar
3342  * @extends Roo.bootstrap.Navbar
3343  * Bootstrap Sidebar class
3344  * 
3345  * @constructor
3346  * Create a new Sidebar
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.NavSidebar = function(config){
3352     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3353 };
3354
3355 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3356     
3357     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3358     
3359     getAutoCreate : function(){
3360         
3361         
3362         return  {
3363             tag: 'div',
3364             cls: 'sidebar sidebar-nav'
3365         };
3366     
3367         
3368     }
3369     
3370     
3371     
3372 });
3373
3374
3375
3376  
3377
3378  /*
3379  * - LGPL
3380  *
3381  * nav group
3382  * 
3383  */
3384
3385 /**
3386  * @class Roo.bootstrap.NavGroup
3387  * @extends Roo.bootstrap.Component
3388  * Bootstrap NavGroup class
3389  * @cfg {String} align left | right
3390  * @cfg {Boolean} inverse false | true
3391  * @cfg {String} type (nav|pills|tab) default nav
3392  * @cfg {String} navId - reference Id for navbar.
3393
3394  * 
3395  * @constructor
3396  * Create a new nav group
3397  * @param {Object} config The config object
3398  */
3399
3400 Roo.bootstrap.NavGroup = function(config){
3401     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3402     this.navItems = [];
3403    
3404     Roo.bootstrap.NavGroup.register(this);
3405      this.addEvents({
3406         /**
3407              * @event changed
3408              * Fires when the active item changes
3409              * @param {Roo.bootstrap.NavGroup} this
3410              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3411              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3412          */
3413         'changed': true
3414      });
3415     
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3419     
3420     align: '',
3421     inverse: false,
3422     form: false,
3423     type: 'nav',
3424     navId : '',
3425     // private
3426     
3427     navItems : false, 
3428     
3429     getAutoCreate : function()
3430     {
3431         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3432         
3433         cfg = {
3434             tag : 'ul',
3435             cls: 'nav' 
3436         }
3437         
3438         if (['tabs','pills'].indexOf(this.type)!==-1) {
3439             cfg.cls += ' nav-' + this.type
3440         } else {
3441             if (this.type!=='nav') {
3442                 Roo.log('nav type must be nav/tabs/pills')
3443             }
3444             cfg.cls += ' navbar-nav'
3445         }
3446         
3447         if (this.parent().sidebar) {
3448             cfg = {
3449                 tag: 'ul',
3450                 cls: 'dashboard-menu sidebar-menu'
3451             }
3452             
3453             return cfg;
3454         }
3455         
3456         if (this.form === true) {
3457             cfg = {
3458                 tag: 'form',
3459                 cls: 'navbar-form'
3460             }
3461             
3462             if (this.align === 'right') {
3463                 cfg.cls += ' navbar-right';
3464             } else {
3465                 cfg.cls += ' navbar-left';
3466             }
3467         }
3468         
3469         if (this.align === 'right') {
3470             cfg.cls += ' navbar-right';
3471         }
3472         
3473         if (this.inverse) {
3474             cfg.cls += ' navbar-inverse';
3475             
3476         }
3477         
3478         
3479         return cfg;
3480     },
3481     /**
3482     * sets the active Navigation item
3483     * @param {Roo.bootstrap.NavItem} the new current navitem
3484     */
3485     setActiveItem : function(item)
3486     {
3487         var prev = false;
3488         Roo.each(this.navItems, function(v){
3489             if (v == item) {
3490                 return ;
3491             }
3492             if (v.isActive()) {
3493                 v.setActive(false, true);
3494                 prev = v;
3495                 
3496             }
3497             
3498         });
3499
3500         item.setActive(true, true);
3501         this.fireEvent('changed', this, item, prev);
3502         
3503         
3504     },
3505     /**
3506     * gets the active Navigation item
3507     * @return {Roo.bootstrap.NavItem} the current navitem
3508     */
3509     getActive : function()
3510     {
3511         
3512         var prev = false;
3513         Roo.each(this.navItems, function(v){
3514             
3515             if (v.isActive()) {
3516                 prev = v;
3517                 
3518             }
3519             
3520         });
3521         return prev;
3522     },
3523     
3524     indexOfNav : function()
3525     {
3526         
3527         var prev = false;
3528         Roo.each(this.navItems, function(v,i){
3529             
3530             if (v.isActive()) {
3531                 prev = i;
3532                 
3533             }
3534             
3535         });
3536         return prev;
3537     },
3538     /**
3539     * adds a Navigation item
3540     * @param {Roo.bootstrap.NavItem} the navitem to add
3541     */
3542     addItem : function(cfg)
3543     {
3544         var cn = new Roo.bootstrap.NavItem(cfg);
3545         this.register(cn);
3546         cn.parentId = this.id;
3547         cn.onRender(this.el, null);
3548         return cn;
3549     },
3550     /**
3551     * register a Navigation item
3552     * @param {Roo.bootstrap.NavItem} the navitem to add
3553     */
3554     register : function(item)
3555     {
3556         this.navItems.push( item);
3557         item.navId = this.navId;
3558     
3559     },
3560     
3561     /**
3562     * clear all the Navigation item
3563     */
3564    
3565     clearAll : function()
3566     {
3567         this.navItems = [];
3568         this.el.dom.innerHTML = '';
3569     },
3570     
3571     getNavItem: function(tabId)
3572     {
3573         var ret = false;
3574         Roo.each(this.navItems, function(e) {
3575             if (e.tabId == tabId) {
3576                ret =  e;
3577                return false;
3578             }
3579             return true;
3580             
3581         });
3582         return ret;
3583     },
3584     
3585     setActiveNext : function()
3586     {
3587         var i = this.indexOfNav(this.getActive());
3588         if (i > this.navItems.length) {
3589             return;
3590         }
3591         this.setActiveItem(this.navItems[i+1]);
3592     },
3593     setActivePrev : function()
3594     {
3595         var i = this.indexOfNav(this.getActive());
3596         if (i  < 1) {
3597             return;
3598         }
3599         this.setActiveItem(this.navItems[i-1]);
3600     },
3601     clearWasActive : function(except) {
3602         Roo.each(this.navItems, function(e) {
3603             if (e.tabId != except.tabId && e.was_active) {
3604                e.was_active = false;
3605                return false;
3606             }
3607             return true;
3608             
3609         });
3610     },
3611     getWasActive : function ()
3612     {
3613         var r = false;
3614         Roo.each(this.navItems, function(e) {
3615             if (e.was_active) {
3616                r = e;
3617                return false;
3618             }
3619             return true;
3620             
3621         });
3622         return r;
3623     }
3624     
3625     
3626 });
3627
3628  
3629 Roo.apply(Roo.bootstrap.NavGroup, {
3630     
3631     groups: {},
3632      /**
3633     * register a Navigation Group
3634     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3635     */
3636     register : function(navgrp)
3637     {
3638         this.groups[navgrp.navId] = navgrp;
3639         
3640     },
3641     /**
3642     * fetch a Navigation Group based on the navigation ID
3643     * @param {string} the navgroup to add
3644     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3645     */
3646     get: function(navId) {
3647         if (typeof(this.groups[navId]) == 'undefined') {
3648             return false;
3649             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3650         }
3651         return this.groups[navId] ;
3652     }
3653     
3654     
3655     
3656 });
3657
3658  /*
3659  * - LGPL
3660  *
3661  * row
3662  * 
3663  */
3664
3665 /**
3666  * @class Roo.bootstrap.NavItem
3667  * @extends Roo.bootstrap.Component
3668  * Bootstrap Navbar.NavItem class
3669  * @cfg {String} href  link to
3670  * @cfg {String} html content of button
3671  * @cfg {String} badge text inside badge
3672  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3673  * @cfg {String} glyphicon name of glyphicon
3674  * @cfg {String} icon name of font awesome icon
3675  * @cfg {Boolean} active Is item active
3676  * @cfg {Boolean} disabled Is item disabled
3677  
3678  * @cfg {Boolean} preventDefault (true | false) default false
3679  * @cfg {String} tabId the tab that this item activates.
3680  * @cfg {String} tagtype (a|span) render as a href or span?
3681   
3682  * @constructor
3683  * Create a new Navbar Item
3684  * @param {Object} config The config object
3685  */
3686 Roo.bootstrap.NavItem = function(config){
3687     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3688     this.addEvents({
3689         // raw events
3690         /**
3691          * @event click
3692          * The raw click event for the entire grid.
3693          * @param {Roo.EventObject} e
3694          */
3695         "click" : true,
3696          /**
3697             * @event changed
3698             * Fires when the active item active state changes
3699             * @param {Roo.bootstrap.NavItem} this
3700             * @param {boolean} state the new state
3701              
3702          */
3703         'changed': true
3704     });
3705    
3706 };
3707
3708 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3709     
3710     href: false,
3711     html: '',
3712     badge: '',
3713     icon: false,
3714     glyphicon: false,
3715     active: false,
3716     preventDefault : false,
3717     tabId : false,
3718     tagtype : 'a',
3719     disabled : false,
3720     
3721     was_active : false,
3722     
3723     getAutoCreate : function(){
3724          
3725         var cfg = {
3726             tag: 'li',
3727             cls: 'nav-item'
3728             
3729         }
3730         if (this.active) {
3731             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3732         }
3733         if (this.disabled) {
3734             cfg.cls += ' disabled';
3735         }
3736         
3737         if (this.href || this.html || this.glyphicon || this.icon) {
3738             cfg.cn = [
3739                 {
3740                     tag: this.tagtype,
3741                     href : this.href || "#",
3742                     html: this.html || ''
3743                 }
3744             ];
3745             
3746             if (this.icon) {
3747                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3748             }
3749
3750             if(this.glyphicon) {
3751                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3752             }
3753             
3754             if (this.menu) {
3755                 
3756                 cfg.cn[0].html += " <span class='caret'></span>";
3757              
3758             }
3759             
3760             if (this.badge !== '') {
3761                  
3762                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3763             }
3764         }
3765         
3766         
3767         
3768         return cfg;
3769     },
3770     initEvents: function() 
3771     {
3772         if (typeof (this.menu) != 'undefined') {
3773             this.menu.parentType = this.xtype;
3774             this.menu.triggerEl = this.el;
3775             this.addxtype(Roo.apply({}, this.menu));
3776         }
3777         
3778         this.el.select('a',true).on('click', this.onClick, this);
3779         
3780         if(this.tagtype == 'span'){
3781             this.el.select('span',true).on('click', this.onClick, this);
3782         }
3783        
3784         // at this point parent should be available..
3785         this.parent().register(this);
3786     },
3787     
3788     onClick : function(e)
3789     {
3790          
3791         if(this.preventDefault){
3792             e.preventDefault();
3793         }
3794         if (this.disabled) {
3795             return;
3796         }
3797         
3798         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3799         if (tg && tg.transition) {
3800             Roo.log("waiting for the transitionend");
3801             return;
3802         }
3803         
3804         Roo.log("fire event clicked");
3805         if(this.fireEvent('click', this, e) === false){
3806             return;
3807         };
3808         
3809         if(this.tagtype == 'span'){
3810             return;
3811         }
3812         
3813         var p = this.parent();
3814         if (['tabs','pills'].indexOf(p.type)!==-1) {
3815             if (typeof(p.setActiveItem) !== 'undefined') {
3816                 p.setActiveItem(this);
3817             }
3818         }
3819         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3820         if (p.parentType == 'NavHeaderbar' && !this.menu) {
3821             // remove the collapsed menu expand...
3822             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
3823         }
3824         
3825     },
3826     
3827     isActive: function () {
3828         return this.active
3829     },
3830     setActive : function(state, fire, is_was_active)
3831     {
3832         if (this.active && !state & this.navId) {
3833             this.was_active = true;
3834             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3835             if (nv) {
3836                 nv.clearWasActive(this);
3837             }
3838             
3839         }
3840         this.active = state;
3841         
3842         if (!state ) {
3843             this.el.removeClass('active');
3844         } else if (!this.el.hasClass('active')) {
3845             this.el.addClass('active');
3846         }
3847         if (fire) {
3848             this.fireEvent('changed', this, state);
3849         }
3850         
3851         // show a panel if it's registered and related..
3852         
3853         if (!this.navId || !this.tabId || !state || is_was_active) {
3854             return;
3855         }
3856         
3857         var tg = Roo.bootstrap.TabGroup.get(this.navId);
3858         if (!tg) {
3859             return;
3860         }
3861         var pan = tg.getPanelByName(this.tabId);
3862         if (!pan) {
3863             return;
3864         }
3865         // if we can not flip to new panel - go back to old nav highlight..
3866         if (false == tg.showPanel(pan)) {
3867             var nv = Roo.bootstrap.NavGroup.get(this.navId);
3868             if (nv) {
3869                 var onav = nv.getWasActive();
3870                 if (onav) {
3871                     onav.setActive(true, false, true);
3872                 }
3873             }
3874             
3875         }
3876         
3877         
3878         
3879     },
3880      // this should not be here...
3881     setDisabled : function(state)
3882     {
3883         this.disabled = state;
3884         if (!state ) {
3885             this.el.removeClass('disabled');
3886         } else if (!this.el.hasClass('disabled')) {
3887             this.el.addClass('disabled');
3888         }
3889         
3890     },
3891     
3892     /**
3893      * Fetch the element to display the tooltip on.
3894      * @return {Roo.Element} defaults to this.el
3895      */
3896     tooltipEl : function()
3897     {
3898         return this.el.select('' + this.tagtype + '', true).first();
3899     }
3900 });
3901  
3902
3903  /*
3904  * - LGPL
3905  *
3906  * sidebar item
3907  *
3908  *  li
3909  *    <span> icon </span>
3910  *    <span> text </span>
3911  *    <span>badge </span>
3912  */
3913
3914 /**
3915  * @class Roo.bootstrap.NavSidebarItem
3916  * @extends Roo.bootstrap.NavItem
3917  * Bootstrap Navbar.NavSidebarItem class
3918  * @constructor
3919  * Create a new Navbar Button
3920  * @param {Object} config The config object
3921  */
3922 Roo.bootstrap.NavSidebarItem = function(config){
3923     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3924     this.addEvents({
3925         // raw events
3926         /**
3927          * @event click
3928          * The raw click event for the entire grid.
3929          * @param {Roo.EventObject} e
3930          */
3931         "click" : true,
3932          /**
3933             * @event changed
3934             * Fires when the active item active state changes
3935             * @param {Roo.bootstrap.NavSidebarItem} this
3936             * @param {boolean} state the new state
3937              
3938          */
3939         'changed': true
3940     });
3941    
3942 };
3943
3944 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3945     
3946     
3947     getAutoCreate : function(){
3948         
3949         
3950         var a = {
3951                 tag: 'a',
3952                 href : this.href || '#',
3953                 cls: '',
3954                 html : '',
3955                 cn : []
3956         };
3957         var cfg = {
3958             tag: 'li',
3959             cls: '',
3960             cn: [ a ]
3961         }
3962         var span = {
3963             tag: 'span',
3964             html : this.html || ''
3965         }
3966         
3967         
3968         if (this.active) {
3969             cfg.cls += ' active';
3970         }
3971         
3972         // left icon..
3973         if (this.glyphicon || this.icon) {
3974             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3975             a.cn.push({ tag : 'i', cls : c }) ;
3976         }
3977         // html..
3978         a.cn.push(span);
3979         // then badge..
3980         if (this.badge !== '') {
3981             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3982         }
3983         // fi
3984         if (this.menu) {
3985             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3986             a.cls += 'dropdown-toggle treeview' ;
3987             
3988         }
3989         
3990         
3991         
3992         return cfg;
3993          
3994            
3995     }
3996    
3997      
3998  
3999 });
4000  
4001
4002  /*
4003  * - LGPL
4004  *
4005  * row
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.Row
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap Row class (contains columns...)
4013  * 
4014  * @constructor
4015  * Create a new Row
4016  * @param {Object} config The config object
4017  */
4018
4019 Roo.bootstrap.Row = function(config){
4020     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4021 };
4022
4023 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4024     
4025     getAutoCreate : function(){
4026        return {
4027             cls: 'row clearfix'
4028        };
4029     }
4030     
4031     
4032 });
4033
4034  
4035
4036  /*
4037  * - LGPL
4038  *
4039  * element
4040  * 
4041  */
4042
4043 /**
4044  * @class Roo.bootstrap.Element
4045  * @extends Roo.bootstrap.Component
4046  * Bootstrap Element class
4047  * @cfg {String} html contents of the element
4048  * @cfg {String} tag tag of the element
4049  * @cfg {String} cls class of the element
4050  * 
4051  * @constructor
4052  * Create a new Element
4053  * @param {Object} config The config object
4054  */
4055
4056 Roo.bootstrap.Element = function(config){
4057     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4058 };
4059
4060 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4061     
4062     tag: 'div',
4063     cls: '',
4064     html: '',
4065      
4066     
4067     getAutoCreate : function(){
4068         
4069         var cfg = {
4070             tag: this.tag,
4071             cls: this.cls,
4072             html: this.html
4073         }
4074         
4075         
4076         
4077         return cfg;
4078     }
4079    
4080 });
4081
4082  
4083
4084  /*
4085  * - LGPL
4086  *
4087  * pagination
4088  * 
4089  */
4090
4091 /**
4092  * @class Roo.bootstrap.Pagination
4093  * @extends Roo.bootstrap.Component
4094  * Bootstrap Pagination class
4095  * @cfg {String} size xs | sm | md | lg
4096  * @cfg {Boolean} inverse false | true
4097  * 
4098  * @constructor
4099  * Create a new Pagination
4100  * @param {Object} config The config object
4101  */
4102
4103 Roo.bootstrap.Pagination = function(config){
4104     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4105 };
4106
4107 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4108     
4109     cls: false,
4110     size: false,
4111     inverse: false,
4112     
4113     getAutoCreate : function(){
4114         var cfg = {
4115             tag: 'ul',
4116                 cls: 'pagination'
4117         };
4118         if (this.inverse) {
4119             cfg.cls += ' inverse';
4120         }
4121         if (this.html) {
4122             cfg.html=this.html;
4123         }
4124         if (this.cls) {
4125             cfg.cls += " " + this.cls;
4126         }
4127         return cfg;
4128     }
4129    
4130 });
4131
4132  
4133
4134  /*
4135  * - LGPL
4136  *
4137  * Pagination item
4138  * 
4139  */
4140
4141
4142 /**
4143  * @class Roo.bootstrap.PaginationItem
4144  * @extends Roo.bootstrap.Component
4145  * Bootstrap PaginationItem class
4146  * @cfg {String} html text
4147  * @cfg {String} href the link
4148  * @cfg {Boolean} preventDefault (true | false) default true
4149  * @cfg {Boolean} active (true | false) default false
4150  * 
4151  * 
4152  * @constructor
4153  * Create a new PaginationItem
4154  * @param {Object} config The config object
4155  */
4156
4157
4158 Roo.bootstrap.PaginationItem = function(config){
4159     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4160     this.addEvents({
4161         // raw events
4162         /**
4163          * @event click
4164          * The raw click event for the entire grid.
4165          * @param {Roo.EventObject} e
4166          */
4167         "click" : true
4168     });
4169 };
4170
4171 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4172     
4173     href : false,
4174     html : false,
4175     preventDefault: true,
4176     active : false,
4177     cls : false,
4178     
4179     getAutoCreate : function(){
4180         var cfg= {
4181             tag: 'li',
4182             cn: [
4183                 {
4184                     tag : 'a',
4185                     href : this.href ? this.href : '#',
4186                     html : this.html ? this.html : ''
4187                 }
4188             ]
4189         };
4190         
4191         if(this.cls){
4192             cfg.cls = this.cls;
4193         }
4194         
4195         if(this.active){
4196             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4197         }
4198         
4199         return cfg;
4200     },
4201     
4202     initEvents: function() {
4203         
4204         this.el.on('click', this.onClick, this);
4205         
4206     },
4207     onClick : function(e)
4208     {
4209         Roo.log('PaginationItem on click ');
4210         if(this.preventDefault){
4211             e.preventDefault();
4212         }
4213         
4214         this.fireEvent('click', this, e);
4215     }
4216    
4217 });
4218
4219  
4220
4221  /*
4222  * - LGPL
4223  *
4224  * slider
4225  * 
4226  */
4227
4228
4229 /**
4230  * @class Roo.bootstrap.Slider
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap Slider class
4233  *    
4234  * @constructor
4235  * Create a new Slider
4236  * @param {Object} config The config object
4237  */
4238
4239 Roo.bootstrap.Slider = function(config){
4240     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4241 };
4242
4243 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4244     
4245     getAutoCreate : function(){
4246         
4247         var cfg = {
4248             tag: 'div',
4249             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4250             cn: [
4251                 {
4252                     tag: 'a',
4253                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4254                 }
4255             ]
4256         }
4257         
4258         return cfg;
4259     }
4260    
4261 });
4262
4263  /*
4264  * Based on:
4265  * Ext JS Library 1.1.1
4266  * Copyright(c) 2006-2007, Ext JS, LLC.
4267  *
4268  * Originally Released Under LGPL - original licence link has changed is not relivant.
4269  *
4270  * Fork - LGPL
4271  * <script type="text/javascript">
4272  */
4273  
4274
4275 /**
4276  * @class Roo.grid.ColumnModel
4277  * @extends Roo.util.Observable
4278  * This is the default implementation of a ColumnModel used by the Grid. It defines
4279  * the columns in the grid.
4280  * <br>Usage:<br>
4281  <pre><code>
4282  var colModel = new Roo.grid.ColumnModel([
4283         {header: "Ticker", width: 60, sortable: true, locked: true},
4284         {header: "Company Name", width: 150, sortable: true},
4285         {header: "Market Cap.", width: 100, sortable: true},
4286         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4287         {header: "Employees", width: 100, sortable: true, resizable: false}
4288  ]);
4289  </code></pre>
4290  * <p>
4291  
4292  * The config options listed for this class are options which may appear in each
4293  * individual column definition.
4294  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4295  * @constructor
4296  * @param {Object} config An Array of column config objects. See this class's
4297  * config objects for details.
4298 */
4299 Roo.grid.ColumnModel = function(config){
4300         /**
4301      * The config passed into the constructor
4302      */
4303     this.config = config;
4304     this.lookup = {};
4305
4306     // if no id, create one
4307     // if the column does not have a dataIndex mapping,
4308     // map it to the order it is in the config
4309     for(var i = 0, len = config.length; i < len; i++){
4310         var c = config[i];
4311         if(typeof c.dataIndex == "undefined"){
4312             c.dataIndex = i;
4313         }
4314         if(typeof c.renderer == "string"){
4315             c.renderer = Roo.util.Format[c.renderer];
4316         }
4317         if(typeof c.id == "undefined"){
4318             c.id = Roo.id();
4319         }
4320         if(c.editor && c.editor.xtype){
4321             c.editor  = Roo.factory(c.editor, Roo.grid);
4322         }
4323         if(c.editor && c.editor.isFormField){
4324             c.editor = new Roo.grid.GridEditor(c.editor);
4325         }
4326         this.lookup[c.id] = c;
4327     }
4328
4329     /**
4330      * The width of columns which have no width specified (defaults to 100)
4331      * @type Number
4332      */
4333     this.defaultWidth = 100;
4334
4335     /**
4336      * Default sortable of columns which have no sortable specified (defaults to false)
4337      * @type Boolean
4338      */
4339     this.defaultSortable = false;
4340
4341     this.addEvents({
4342         /**
4343              * @event widthchange
4344              * Fires when the width of a column changes.
4345              * @param {ColumnModel} this
4346              * @param {Number} columnIndex The column index
4347              * @param {Number} newWidth The new width
4348              */
4349             "widthchange": true,
4350         /**
4351              * @event headerchange
4352              * Fires when the text of a header changes.
4353              * @param {ColumnModel} this
4354              * @param {Number} columnIndex The column index
4355              * @param {Number} newText The new header text
4356              */
4357             "headerchange": true,
4358         /**
4359              * @event hiddenchange
4360              * Fires when a column is hidden or "unhidden".
4361              * @param {ColumnModel} this
4362              * @param {Number} columnIndex The column index
4363              * @param {Boolean} hidden true if hidden, false otherwise
4364              */
4365             "hiddenchange": true,
4366             /**
4367          * @event columnmoved
4368          * Fires when a column is moved.
4369          * @param {ColumnModel} this
4370          * @param {Number} oldIndex
4371          * @param {Number} newIndex
4372          */
4373         "columnmoved" : true,
4374         /**
4375          * @event columlockchange
4376          * Fires when a column's locked state is changed
4377          * @param {ColumnModel} this
4378          * @param {Number} colIndex
4379          * @param {Boolean} locked true if locked
4380          */
4381         "columnlockchange" : true
4382     });
4383     Roo.grid.ColumnModel.superclass.constructor.call(this);
4384 };
4385 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4386     /**
4387      * @cfg {String} header The header text to display in the Grid view.
4388      */
4389     /**
4390      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4391      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4392      * specified, the column's index is used as an index into the Record's data Array.
4393      */
4394     /**
4395      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4396      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4397      */
4398     /**
4399      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4400      * Defaults to the value of the {@link #defaultSortable} property.
4401      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4402      */
4403     /**
4404      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4405      */
4406     /**
4407      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4408      */
4409     /**
4410      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4411      */
4412     /**
4413      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4414      */
4415     /**
4416      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4417      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4418      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4419      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4420      */
4421        /**
4422      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4423      */
4424     /**
4425      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4426      */
4427     /**
4428      * @cfg {String} cursor (Optional)
4429      */
4430     /**
4431      * Returns the id of the column at the specified index.
4432      * @param {Number} index The column index
4433      * @return {String} the id
4434      */
4435     getColumnId : function(index){
4436         return this.config[index].id;
4437     },
4438
4439     /**
4440      * Returns the column for a specified id.
4441      * @param {String} id The column id
4442      * @return {Object} the column
4443      */
4444     getColumnById : function(id){
4445         return this.lookup[id];
4446     },
4447
4448     
4449     /**
4450      * Returns the column for a specified dataIndex.
4451      * @param {String} dataIndex The column dataIndex
4452      * @return {Object|Boolean} the column or false if not found
4453      */
4454     getColumnByDataIndex: function(dataIndex){
4455         var index = this.findColumnIndex(dataIndex);
4456         return index > -1 ? this.config[index] : false;
4457     },
4458     
4459     /**
4460      * Returns the index for a specified column id.
4461      * @param {String} id The column id
4462      * @return {Number} the index, or -1 if not found
4463      */
4464     getIndexById : function(id){
4465         for(var i = 0, len = this.config.length; i < len; i++){
4466             if(this.config[i].id == id){
4467                 return i;
4468             }
4469         }
4470         return -1;
4471     },
4472     
4473     /**
4474      * Returns the index for a specified column dataIndex.
4475      * @param {String} dataIndex The column dataIndex
4476      * @return {Number} the index, or -1 if not found
4477      */
4478     
4479     findColumnIndex : function(dataIndex){
4480         for(var i = 0, len = this.config.length; i < len; i++){
4481             if(this.config[i].dataIndex == dataIndex){
4482                 return i;
4483             }
4484         }
4485         return -1;
4486     },
4487     
4488     
4489     moveColumn : function(oldIndex, newIndex){
4490         var c = this.config[oldIndex];
4491         this.config.splice(oldIndex, 1);
4492         this.config.splice(newIndex, 0, c);
4493         this.dataMap = null;
4494         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4495     },
4496
4497     isLocked : function(colIndex){
4498         return this.config[colIndex].locked === true;
4499     },
4500
4501     setLocked : function(colIndex, value, suppressEvent){
4502         if(this.isLocked(colIndex) == value){
4503             return;
4504         }
4505         this.config[colIndex].locked = value;
4506         if(!suppressEvent){
4507             this.fireEvent("columnlockchange", this, colIndex, value);
4508         }
4509     },
4510
4511     getTotalLockedWidth : function(){
4512         var totalWidth = 0;
4513         for(var i = 0; i < this.config.length; i++){
4514             if(this.isLocked(i) && !this.isHidden(i)){
4515                 this.totalWidth += this.getColumnWidth(i);
4516             }
4517         }
4518         return totalWidth;
4519     },
4520
4521     getLockedCount : function(){
4522         for(var i = 0, len = this.config.length; i < len; i++){
4523             if(!this.isLocked(i)){
4524                 return i;
4525             }
4526         }
4527     },
4528
4529     /**
4530      * Returns the number of columns.
4531      * @return {Number}
4532      */
4533     getColumnCount : function(visibleOnly){
4534         if(visibleOnly === true){
4535             var c = 0;
4536             for(var i = 0, len = this.config.length; i < len; i++){
4537                 if(!this.isHidden(i)){
4538                     c++;
4539                 }
4540             }
4541             return c;
4542         }
4543         return this.config.length;
4544     },
4545
4546     /**
4547      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4548      * @param {Function} fn
4549      * @param {Object} scope (optional)
4550      * @return {Array} result
4551      */
4552     getColumnsBy : function(fn, scope){
4553         var r = [];
4554         for(var i = 0, len = this.config.length; i < len; i++){
4555             var c = this.config[i];
4556             if(fn.call(scope||this, c, i) === true){
4557                 r[r.length] = c;
4558             }
4559         }
4560         return r;
4561     },
4562
4563     /**
4564      * Returns true if the specified column is sortable.
4565      * @param {Number} col The column index
4566      * @return {Boolean}
4567      */
4568     isSortable : function(col){
4569         if(typeof this.config[col].sortable == "undefined"){
4570             return this.defaultSortable;
4571         }
4572         return this.config[col].sortable;
4573     },
4574
4575     /**
4576      * Returns the rendering (formatting) function defined for the column.
4577      * @param {Number} col The column index.
4578      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4579      */
4580     getRenderer : function(col){
4581         if(!this.config[col].renderer){
4582             return Roo.grid.ColumnModel.defaultRenderer;
4583         }
4584         return this.config[col].renderer;
4585     },
4586
4587     /**
4588      * Sets the rendering (formatting) function for a column.
4589      * @param {Number} col The column index
4590      * @param {Function} fn The function to use to process the cell's raw data
4591      * to return HTML markup for the grid view. The render function is called with
4592      * the following parameters:<ul>
4593      * <li>Data value.</li>
4594      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4595      * <li>css A CSS style string to apply to the table cell.</li>
4596      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4597      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4598      * <li>Row index</li>
4599      * <li>Column index</li>
4600      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4601      */
4602     setRenderer : function(col, fn){
4603         this.config[col].renderer = fn;
4604     },
4605
4606     /**
4607      * Returns the width for the specified column.
4608      * @param {Number} col The column index
4609      * @return {Number}
4610      */
4611     getColumnWidth : function(col){
4612         return this.config[col].width * 1 || this.defaultWidth;
4613     },
4614
4615     /**
4616      * Sets the width for a column.
4617      * @param {Number} col The column index
4618      * @param {Number} width The new width
4619      */
4620     setColumnWidth : function(col, width, suppressEvent){
4621         this.config[col].width = width;
4622         this.totalWidth = null;
4623         if(!suppressEvent){
4624              this.fireEvent("widthchange", this, col, width);
4625         }
4626     },
4627
4628     /**
4629      * Returns the total width of all columns.
4630      * @param {Boolean} includeHidden True to include hidden column widths
4631      * @return {Number}
4632      */
4633     getTotalWidth : function(includeHidden){
4634         if(!this.totalWidth){
4635             this.totalWidth = 0;
4636             for(var i = 0, len = this.config.length; i < len; i++){
4637                 if(includeHidden || !this.isHidden(i)){
4638                     this.totalWidth += this.getColumnWidth(i);
4639                 }
4640             }
4641         }
4642         return this.totalWidth;
4643     },
4644
4645     /**
4646      * Returns the header for the specified column.
4647      * @param {Number} col The column index
4648      * @return {String}
4649      */
4650     getColumnHeader : function(col){
4651         return this.config[col].header;
4652     },
4653
4654     /**
4655      * Sets the header for a column.
4656      * @param {Number} col The column index
4657      * @param {String} header The new header
4658      */
4659     setColumnHeader : function(col, header){
4660         this.config[col].header = header;
4661         this.fireEvent("headerchange", this, col, header);
4662     },
4663
4664     /**
4665      * Returns the tooltip for the specified column.
4666      * @param {Number} col The column index
4667      * @return {String}
4668      */
4669     getColumnTooltip : function(col){
4670             return this.config[col].tooltip;
4671     },
4672     /**
4673      * Sets the tooltip for a column.
4674      * @param {Number} col The column index
4675      * @param {String} tooltip The new tooltip
4676      */
4677     setColumnTooltip : function(col, tooltip){
4678             this.config[col].tooltip = tooltip;
4679     },
4680
4681     /**
4682      * Returns the dataIndex for the specified column.
4683      * @param {Number} col The column index
4684      * @return {Number}
4685      */
4686     getDataIndex : function(col){
4687         return this.config[col].dataIndex;
4688     },
4689
4690     /**
4691      * Sets the dataIndex for a column.
4692      * @param {Number} col The column index
4693      * @param {Number} dataIndex The new dataIndex
4694      */
4695     setDataIndex : function(col, dataIndex){
4696         this.config[col].dataIndex = dataIndex;
4697     },
4698
4699     
4700     
4701     /**
4702      * Returns true if the cell is editable.
4703      * @param {Number} colIndex The column index
4704      * @param {Number} rowIndex The row index
4705      * @return {Boolean}
4706      */
4707     isCellEditable : function(colIndex, rowIndex){
4708         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4709     },
4710
4711     /**
4712      * Returns the editor defined for the cell/column.
4713      * return false or null to disable editing.
4714      * @param {Number} colIndex The column index
4715      * @param {Number} rowIndex The row index
4716      * @return {Object}
4717      */
4718     getCellEditor : function(colIndex, rowIndex){
4719         return this.config[colIndex].editor;
4720     },
4721
4722     /**
4723      * Sets if a column is editable.
4724      * @param {Number} col The column index
4725      * @param {Boolean} editable True if the column is editable
4726      */
4727     setEditable : function(col, editable){
4728         this.config[col].editable = editable;
4729     },
4730
4731
4732     /**
4733      * Returns true if the column is hidden.
4734      * @param {Number} colIndex The column index
4735      * @return {Boolean}
4736      */
4737     isHidden : function(colIndex){
4738         return this.config[colIndex].hidden;
4739     },
4740
4741
4742     /**
4743      * Returns true if the column width cannot be changed
4744      */
4745     isFixed : function(colIndex){
4746         return this.config[colIndex].fixed;
4747     },
4748
4749     /**
4750      * Returns true if the column can be resized
4751      * @return {Boolean}
4752      */
4753     isResizable : function(colIndex){
4754         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4755     },
4756     /**
4757      * Sets if a column is hidden.
4758      * @param {Number} colIndex The column index
4759      * @param {Boolean} hidden True if the column is hidden
4760      */
4761     setHidden : function(colIndex, hidden){
4762         this.config[colIndex].hidden = hidden;
4763         this.totalWidth = null;
4764         this.fireEvent("hiddenchange", this, colIndex, hidden);
4765     },
4766
4767     /**
4768      * Sets the editor for a column.
4769      * @param {Number} col The column index
4770      * @param {Object} editor The editor object
4771      */
4772     setEditor : function(col, editor){
4773         this.config[col].editor = editor;
4774     }
4775 });
4776
4777 Roo.grid.ColumnModel.defaultRenderer = function(value){
4778         if(typeof value == "string" && value.length < 1){
4779             return "&#160;";
4780         }
4781         return value;
4782 };
4783
4784 // Alias for backwards compatibility
4785 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4786 /*
4787  * Based on:
4788  * Ext JS Library 1.1.1
4789  * Copyright(c) 2006-2007, Ext JS, LLC.
4790  *
4791  * Originally Released Under LGPL - original licence link has changed is not relivant.
4792  *
4793  * Fork - LGPL
4794  * <script type="text/javascript">
4795  */
4796  
4797 /**
4798  * @class Roo.LoadMask
4799  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4800  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4801  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4802  * element's UpdateManager load indicator and will be destroyed after the initial load.
4803  * @constructor
4804  * Create a new LoadMask
4805  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4806  * @param {Object} config The config object
4807  */
4808 Roo.LoadMask = function(el, config){
4809     this.el = Roo.get(el);
4810     Roo.apply(this, config);
4811     if(this.store){
4812         this.store.on('beforeload', this.onBeforeLoad, this);
4813         this.store.on('load', this.onLoad, this);
4814         this.store.on('loadexception', this.onLoadException, this);
4815         this.removeMask = false;
4816     }else{
4817         var um = this.el.getUpdateManager();
4818         um.showLoadIndicator = false; // disable the default indicator
4819         um.on('beforeupdate', this.onBeforeLoad, this);
4820         um.on('update', this.onLoad, this);
4821         um.on('failure', this.onLoad, this);
4822         this.removeMask = true;
4823     }
4824 };
4825
4826 Roo.LoadMask.prototype = {
4827     /**
4828      * @cfg {Boolean} removeMask
4829      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4830      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4831      */
4832     /**
4833      * @cfg {String} msg
4834      * The text to display in a centered loading message box (defaults to 'Loading...')
4835      */
4836     msg : 'Loading...',
4837     /**
4838      * @cfg {String} msgCls
4839      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4840      */
4841     msgCls : 'x-mask-loading',
4842
4843     /**
4844      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4845      * @type Boolean
4846      */
4847     disabled: false,
4848
4849     /**
4850      * Disables the mask to prevent it from being displayed
4851      */
4852     disable : function(){
4853        this.disabled = true;
4854     },
4855
4856     /**
4857      * Enables the mask so that it can be displayed
4858      */
4859     enable : function(){
4860         this.disabled = false;
4861     },
4862     
4863     onLoadException : function()
4864     {
4865         Roo.log(arguments);
4866         
4867         if (typeof(arguments[3]) != 'undefined') {
4868             Roo.MessageBox.alert("Error loading",arguments[3]);
4869         } 
4870         /*
4871         try {
4872             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4873                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4874             }   
4875         } catch(e) {
4876             
4877         }
4878         */
4879     
4880         
4881         
4882         this.el.unmask(this.removeMask);
4883     },
4884     // private
4885     onLoad : function()
4886     {
4887         this.el.unmask(this.removeMask);
4888     },
4889
4890     // private
4891     onBeforeLoad : function(){
4892         if(!this.disabled){
4893             this.el.mask(this.msg, this.msgCls);
4894         }
4895     },
4896
4897     // private
4898     destroy : function(){
4899         if(this.store){
4900             this.store.un('beforeload', this.onBeforeLoad, this);
4901             this.store.un('load', this.onLoad, this);
4902             this.store.un('loadexception', this.onLoadException, this);
4903         }else{
4904             var um = this.el.getUpdateManager();
4905             um.un('beforeupdate', this.onBeforeLoad, this);
4906             um.un('update', this.onLoad, this);
4907             um.un('failure', this.onLoad, this);
4908         }
4909     }
4910 };/*
4911  * - LGPL
4912  *
4913  * table
4914  * 
4915  */
4916
4917 /**
4918  * @class Roo.bootstrap.Table
4919  * @extends Roo.bootstrap.Component
4920  * Bootstrap Table class
4921  * @cfg {String} cls table class
4922  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4923  * @cfg {String} bgcolor Specifies the background color for a table
4924  * @cfg {Number} border Specifies whether the table cells should have borders or not
4925  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4926  * @cfg {Number} cellspacing Specifies the space between cells
4927  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4928  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4929  * @cfg {String} sortable Specifies that the table should be sortable
4930  * @cfg {String} summary Specifies a summary of the content of a table
4931  * @cfg {Number} width Specifies the width of a table
4932  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4933  * 
4934  * @cfg {boolean} striped Should the rows be alternative striped
4935  * @cfg {boolean} bordered Add borders to the table
4936  * @cfg {boolean} hover Add hover highlighting
4937  * @cfg {boolean} condensed Format condensed
4938  * @cfg {boolean} responsive Format condensed
4939  * @cfg {Boolean} loadMask (true|false) default false
4940  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4941  * @cfg {Boolean} thead (true|false) generate thead, default true
4942  * @cfg {Boolean} RowSelection (true|false) default false
4943  * @cfg {Boolean} CellSelection (true|false) default false
4944  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4945  
4946  * 
4947  * @constructor
4948  * Create a new Table
4949  * @param {Object} config The config object
4950  */
4951
4952 Roo.bootstrap.Table = function(config){
4953     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4954     
4955     if (this.sm) {
4956         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4957         this.sm = this.selModel;
4958         this.sm.xmodule = this.xmodule || false;
4959     }
4960     if (this.cm && typeof(this.cm.config) == 'undefined') {
4961         this.colModel = new Roo.grid.ColumnModel(this.cm);
4962         this.cm = this.colModel;
4963         this.cm.xmodule = this.xmodule || false;
4964     }
4965     if (this.store) {
4966         this.store= Roo.factory(this.store, Roo.data);
4967         this.ds = this.store;
4968         this.ds.xmodule = this.xmodule || false;
4969          
4970     }
4971     if (this.footer && this.store) {
4972         this.footer.dataSource = this.ds;
4973         this.footer = Roo.factory(this.footer);
4974     }
4975     
4976     /** @private */
4977     this.addEvents({
4978         /**
4979          * @event cellclick
4980          * Fires when a cell is clicked
4981          * @param {Roo.bootstrap.Table} this
4982          * @param {Roo.Element} el
4983          * @param {Number} rowIndex
4984          * @param {Number} columnIndex
4985          * @param {Roo.EventObject} e
4986          */
4987         "cellclick" : true,
4988         /**
4989          * @event celldblclick
4990          * Fires when a cell is double clicked
4991          * @param {Roo.bootstrap.Table} this
4992          * @param {Roo.Element} el
4993          * @param {Number} rowIndex
4994          * @param {Number} columnIndex
4995          * @param {Roo.EventObject} e
4996          */
4997         "celldblclick" : true,
4998         /**
4999          * @event rowclick
5000          * Fires when a row is clicked
5001          * @param {Roo.bootstrap.Table} this
5002          * @param {Roo.Element} el
5003          * @param {Number} rowIndex
5004          * @param {Roo.EventObject} e
5005          */
5006         "rowclick" : true,
5007         /**
5008          * @event rowdblclick
5009          * Fires when a row is double clicked
5010          * @param {Roo.bootstrap.Table} this
5011          * @param {Roo.Element} el
5012          * @param {Number} rowIndex
5013          * @param {Roo.EventObject} e
5014          */
5015         "rowdblclick" : true,
5016         /**
5017          * @event mouseover
5018          * Fires when a mouseover occur
5019          * @param {Roo.bootstrap.Table} this
5020          * @param {Roo.Element} el
5021          * @param {Number} rowIndex
5022          * @param {Number} columnIndex
5023          * @param {Roo.EventObject} e
5024          */
5025         "mouseover" : true,
5026         /**
5027          * @event mouseout
5028          * Fires when a mouseout occur
5029          * @param {Roo.bootstrap.Table} this
5030          * @param {Roo.Element} el
5031          * @param {Number} rowIndex
5032          * @param {Number} columnIndex
5033          * @param {Roo.EventObject} e
5034          */
5035         "mouseout" : true,
5036         /**
5037          * @event rowclass
5038          * Fires when a row is rendered, so you can change add a style to it.
5039          * @param {Roo.bootstrap.Table} this
5040          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5041          */
5042         'rowclass' : true
5043         
5044     });
5045 };
5046
5047 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5048     
5049     cls: false,
5050     align: false,
5051     bgcolor: false,
5052     border: false,
5053     cellpadding: false,
5054     cellspacing: false,
5055     frame: false,
5056     rules: false,
5057     sortable: false,
5058     summary: false,
5059     width: false,
5060     striped : false,
5061     bordered: false,
5062     hover:  false,
5063     condensed : false,
5064     responsive : false,
5065     sm : false,
5066     cm : false,
5067     store : false,
5068     loadMask : false,
5069     tfoot : true,
5070     thead : true,
5071     RowSelection : false,
5072     CellSelection : false,
5073     layout : false,
5074     
5075     // Roo.Element - the tbody
5076     mainBody: false, 
5077     
5078     getAutoCreate : function(){
5079         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5080         
5081         cfg = {
5082             tag: 'table',
5083             cls : 'table',
5084             cn : []
5085         }
5086             
5087         if (this.striped) {
5088             cfg.cls += ' table-striped';
5089         }
5090         
5091         if (this.hover) {
5092             cfg.cls += ' table-hover';
5093         }
5094         if (this.bordered) {
5095             cfg.cls += ' table-bordered';
5096         }
5097         if (this.condensed) {
5098             cfg.cls += ' table-condensed';
5099         }
5100         if (this.responsive) {
5101             cfg.cls += ' table-responsive';
5102         }
5103         
5104         if (this.cls) {
5105             cfg.cls+=  ' ' +this.cls;
5106         }
5107         
5108         // this lot should be simplifed...
5109         
5110         if (this.align) {
5111             cfg.align=this.align;
5112         }
5113         if (this.bgcolor) {
5114             cfg.bgcolor=this.bgcolor;
5115         }
5116         if (this.border) {
5117             cfg.border=this.border;
5118         }
5119         if (this.cellpadding) {
5120             cfg.cellpadding=this.cellpadding;
5121         }
5122         if (this.cellspacing) {
5123             cfg.cellspacing=this.cellspacing;
5124         }
5125         if (this.frame) {
5126             cfg.frame=this.frame;
5127         }
5128         if (this.rules) {
5129             cfg.rules=this.rules;
5130         }
5131         if (this.sortable) {
5132             cfg.sortable=this.sortable;
5133         }
5134         if (this.summary) {
5135             cfg.summary=this.summary;
5136         }
5137         if (this.width) {
5138             cfg.width=this.width;
5139         }
5140         if (this.layout) {
5141             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5142         }
5143         
5144         if(this.store || this.cm){
5145             if(this.thead){
5146                 cfg.cn.push(this.renderHeader());
5147             }
5148             
5149             cfg.cn.push(this.renderBody());
5150             
5151             if(this.tfoot){
5152                 cfg.cn.push(this.renderFooter());
5153             }
5154             
5155             cfg.cls+=  ' TableGrid';
5156         }
5157         
5158         return { cn : [ cfg ] };
5159     },
5160     
5161     initEvents : function()
5162     {   
5163         if(!this.store || !this.cm){
5164             return;
5165         }
5166         
5167         //Roo.log('initEvents with ds!!!!');
5168         
5169         this.mainBody = this.el.select('tbody', true).first();
5170         
5171         
5172         var _this = this;
5173         
5174         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5175             e.on('click', _this.sort, _this);
5176         });
5177         
5178         this.el.on("click", this.onClick, this);
5179         this.el.on("dblclick", this.onDblClick, this);
5180         
5181         this.parent().el.setStyle('position', 'relative');
5182         if (this.footer) {
5183             this.footer.parentId = this.id;
5184             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5185         }
5186         
5187         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5188         
5189         this.store.on('load', this.onLoad, this);
5190         this.store.on('beforeload', this.onBeforeLoad, this);
5191         this.store.on('update', this.onUpdate, this);
5192         
5193     },
5194     
5195     onMouseover : function(e, el)
5196     {
5197         var cell = Roo.get(el);
5198         
5199         if(!cell){
5200             return;
5201         }
5202         
5203         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5204             cell = cell.findParent('td', false, true);
5205         }
5206         
5207         var row = cell.findParent('tr', false, true);
5208         var cellIndex = cell.dom.cellIndex;
5209         var rowIndex = row.dom.rowIndex - 1; // start from 0
5210         
5211         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5212         
5213     },
5214     
5215     onMouseout : function(e, el)
5216     {
5217         var cell = Roo.get(el);
5218         
5219         if(!cell){
5220             return;
5221         }
5222         
5223         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5224             cell = cell.findParent('td', false, true);
5225         }
5226         
5227         var row = cell.findParent('tr', false, true);
5228         var cellIndex = cell.dom.cellIndex;
5229         var rowIndex = row.dom.rowIndex - 1; // start from 0
5230         
5231         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5232         
5233     },
5234     
5235     onClick : function(e, el)
5236     {
5237         var cell = Roo.get(el);
5238         
5239         if(!cell || (!this.CellSelection && !this.RowSelection)){
5240             return;
5241         }
5242         
5243         
5244         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5245             cell = cell.findParent('td', false, true);
5246         }
5247         
5248         var row = cell.findParent('tr', false, true);
5249         var cellIndex = cell.dom.cellIndex;
5250         var rowIndex = row.dom.rowIndex - 1;
5251         
5252         if(this.CellSelection){
5253             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5254         }
5255         
5256         if(this.RowSelection){
5257             this.fireEvent('rowclick', this, row, rowIndex, e);
5258         }
5259         
5260         
5261     },
5262     
5263     onDblClick : function(e,el)
5264     {
5265         var cell = Roo.get(el);
5266         
5267         if(!cell || (!this.CellSelection && !this.RowSelection)){
5268             return;
5269         }
5270         
5271         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5272             cell = cell.findParent('td', false, true);
5273         }
5274         
5275         var row = cell.findParent('tr', false, true);
5276         var cellIndex = cell.dom.cellIndex;
5277         var rowIndex = row.dom.rowIndex - 1;
5278         
5279         if(this.CellSelection){
5280             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5281         }
5282         
5283         if(this.RowSelection){
5284             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5285         }
5286     },
5287     
5288     sort : function(e,el)
5289     {
5290         var col = Roo.get(el)
5291         
5292         if(!col.hasClass('sortable')){
5293             return;
5294         }
5295         
5296         var sort = col.attr('sort');
5297         var dir = 'ASC';
5298         
5299         if(col.hasClass('glyphicon-arrow-up')){
5300             dir = 'DESC';
5301         }
5302         
5303         this.store.sortInfo = {field : sort, direction : dir};
5304         
5305         if (this.footer) {
5306             Roo.log("calling footer first");
5307             this.footer.onClick('first');
5308         } else {
5309         
5310             this.store.load({ params : { start : 0 } });
5311         }
5312     },
5313     
5314     renderHeader : function()
5315     {
5316         var header = {
5317             tag: 'thead',
5318             cn : []
5319         };
5320         
5321         var cm = this.cm;
5322         
5323         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5324             
5325             var config = cm.config[i];
5326                     
5327             var c = {
5328                 tag: 'th',
5329                 style : '',
5330                 html: cm.getColumnHeader(i)
5331             };
5332             
5333             if(typeof(config.hidden) != 'undefined' && config.hidden){
5334                 c.style += ' display:none;';
5335             }
5336             
5337             if(typeof(config.dataIndex) != 'undefined'){
5338                 c.sort = config.dataIndex;
5339             }
5340             
5341             if(typeof(config.sortable) != 'undefined' && config.sortable){
5342                 c.cls = 'sortable';
5343             }
5344             
5345             if(typeof(config.align) != 'undefined' && config.align.length){
5346                 c.style += ' text-align:' + config.align + ';';
5347             }
5348             
5349             if(typeof(config.width) != 'undefined'){
5350                 c.style += ' width:' + config.width + 'px;';
5351             }
5352             
5353             header.cn.push(c)
5354         }
5355         
5356         return header;
5357     },
5358     
5359     renderBody : function()
5360     {
5361         var body = {
5362             tag: 'tbody',
5363             cn : [
5364                 {
5365                     tag: 'tr',
5366                     cn : [
5367                         {
5368                             tag : 'td',
5369                             colspan :  this.cm.getColumnCount()
5370                         }
5371                     ]
5372                 }
5373             ]
5374         };
5375         
5376         return body;
5377     },
5378     
5379     renderFooter : function()
5380     {
5381         var footer = {
5382             tag: 'tfoot',
5383             cn : [
5384                 {
5385                     tag: 'tr',
5386                     cn : [
5387                         {
5388                             tag : 'td',
5389                             colspan :  this.cm.getColumnCount()
5390                         }
5391                     ]
5392                 }
5393             ]
5394         };
5395         
5396         return footer;
5397     },
5398     
5399     
5400     
5401     onLoad : function()
5402     {
5403         Roo.log('ds onload');
5404         this.clear();
5405         
5406         var _this = this;
5407         var cm = this.cm;
5408         var ds = this.store;
5409         
5410         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5411             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5412             
5413             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5414                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5415             }
5416             
5417             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5418                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5419             }
5420         });
5421         
5422         var tbody =  this.mainBody;
5423               
5424         if(ds.getCount() > 0){
5425             ds.data.each(function(d,rowIndex){
5426                 var row =  this.renderRow(cm, ds, rowIndex);
5427                 
5428                 tbody.createChild(row);
5429                 
5430                 var _this = this;
5431                 
5432                 if(row.cellObjects.length){
5433                     Roo.each(row.cellObjects, function(r){
5434                         _this.renderCellObject(r);
5435                     })
5436                 }
5437                 
5438             }, this);
5439         }
5440         
5441         Roo.each(this.el.select('tbody td', true).elements, function(e){
5442             e.on('mouseover', _this.onMouseover, _this);
5443         });
5444         
5445         Roo.each(this.el.select('tbody td', true).elements, function(e){
5446             e.on('mouseout', _this.onMouseout, _this);
5447         });
5448
5449         //if(this.loadMask){
5450         //    this.maskEl.hide();
5451         //}
5452     },
5453     
5454     
5455     onUpdate : function(ds,record)
5456     {
5457         this.refreshRow(record);
5458     },
5459     onRemove : function(ds, record, index, isUpdate){
5460         if(isUpdate !== true){
5461             this.fireEvent("beforerowremoved", this, index, record);
5462         }
5463         var bt = this.mainBody.dom;
5464         if(bt.rows[index]){
5465             bt.removeChild(bt.rows[index]);
5466         }
5467         
5468         if(isUpdate !== true){
5469             //this.stripeRows(index);
5470             //this.syncRowHeights(index, index);
5471             //this.layout();
5472             this.fireEvent("rowremoved", this, index, record);
5473         }
5474     },
5475     
5476     
5477     refreshRow : function(record){
5478         var ds = this.store, index;
5479         if(typeof record == 'number'){
5480             index = record;
5481             record = ds.getAt(index);
5482         }else{
5483             index = ds.indexOf(record);
5484         }
5485         this.insertRow(ds, index, true);
5486         this.onRemove(ds, record, index+1, true);
5487         //this.syncRowHeights(index, index);
5488         //this.layout();
5489         this.fireEvent("rowupdated", this, index, record);
5490     },
5491     
5492     insertRow : function(dm, rowIndex, isUpdate){
5493         
5494         if(!isUpdate){
5495             this.fireEvent("beforerowsinserted", this, rowIndex);
5496         }
5497             //var s = this.getScrollState();
5498         var row = this.renderRow(this.cm, this.store, rowIndex);
5499         // insert before rowIndex..
5500         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5501         
5502         var _this = this;
5503                 
5504         if(row.cellObjects.length){
5505             Roo.each(row.cellObjects, function(r){
5506                 _this.renderCellObject(r);
5507             })
5508         }
5509             
5510         if(!isUpdate){
5511             this.fireEvent("rowsinserted", this, rowIndex);
5512             //this.syncRowHeights(firstRow, lastRow);
5513             //this.stripeRows(firstRow);
5514             //this.layout();
5515         }
5516         
5517     },
5518     
5519     
5520     getRowDom : function(rowIndex)
5521     {
5522         // not sure if I need to check this.. but let's do it anyway..
5523         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5524                 this.mainBody.dom.rows[rowIndex] : false
5525     },
5526     // returns the object tree for a tr..
5527   
5528     
5529     renderRow : function(cm, ds, rowIndex) {
5530         
5531         var d = ds.getAt(rowIndex);
5532         
5533         var row = {
5534             tag : 'tr',
5535             cn : []
5536         };
5537             
5538         var cellObjects = [];
5539         
5540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5541             var config = cm.config[i];
5542             
5543             var renderer = cm.getRenderer(i);
5544             var value = '';
5545             var id = false;
5546             
5547             if(typeof(renderer) !== 'undefined'){
5548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5549             }
5550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5551             // and are rendered into the cells after the row is rendered - using the id for the element.
5552             
5553             if(typeof(value) === 'object'){
5554                 id = Roo.id();
5555                 cellObjects.push({
5556                     container : id,
5557                     cfg : value 
5558                 })
5559             }
5560             
5561             var rowcfg = {
5562                 record: d,
5563                 rowIndex : rowIndex,
5564                 colIndex : i,
5565                 rowClass : ''
5566             }
5567
5568             this.fireEvent('rowclass', this, rowcfg);
5569             
5570             var td = {
5571                 tag: 'td',
5572                 cls : rowcfg.rowClass,
5573                 style: '',
5574                 html: (typeof(value) === 'object') ? '' : value
5575             };
5576             
5577             if (id) {
5578                 td.id = id;
5579             }
5580             
5581             if(typeof(config.hidden) != 'undefined' && config.hidden){
5582                 td.style += ' display:none;';
5583             }
5584             
5585             if(typeof(config.align) != 'undefined' && config.align.length){
5586                 td.style += ' text-align:' + config.align + ';';
5587             }
5588             
5589             if(typeof(config.width) != 'undefined'){
5590                 td.style += ' width:' +  config.width + 'px;';
5591             }
5592             
5593             if(typeof(config.cursor) != 'undefined'){
5594                 td.style += ' cursor:' +  config.cursor + ';';
5595             }
5596              
5597             row.cn.push(td);
5598            
5599         }
5600         
5601         row.cellObjects = cellObjects;
5602         
5603         return row;
5604           
5605     },
5606     
5607     
5608     
5609     onBeforeLoad : function()
5610     {
5611         //Roo.log('ds onBeforeLoad');
5612         
5613         //this.clear();
5614         
5615         //if(this.loadMask){
5616         //    this.maskEl.show();
5617         //}
5618     },
5619     
5620     clear : function()
5621     {
5622         this.el.select('tbody', true).first().dom.innerHTML = '';
5623     },
5624     
5625     getSelectionModel : function(){
5626         if(!this.selModel){
5627             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5628         }
5629         return this.selModel;
5630     },
5631     /*
5632      * Render the Roo.bootstrap object from renderder
5633      */
5634     renderCellObject : function(r)
5635     {
5636         var _this = this;
5637         
5638         var t = r.cfg.render(r.container);
5639         
5640         if(r.cfg.cn){
5641             Roo.each(r.cfg.cn, function(c){
5642                 var child = {
5643                     container: t.getChildContainer(),
5644                     cfg: c
5645                 }
5646                 _this.renderCellObject(child);
5647             })
5648         }
5649     }
5650    
5651 });
5652
5653  
5654
5655  /*
5656  * - LGPL
5657  *
5658  * table cell
5659  * 
5660  */
5661
5662 /**
5663  * @class Roo.bootstrap.TableCell
5664  * @extends Roo.bootstrap.Component
5665  * Bootstrap TableCell class
5666  * @cfg {String} html cell contain text
5667  * @cfg {String} cls cell class
5668  * @cfg {String} tag cell tag (td|th) default td
5669  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5670  * @cfg {String} align Aligns the content in a cell
5671  * @cfg {String} axis Categorizes cells
5672  * @cfg {String} bgcolor Specifies the background color of a cell
5673  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5674  * @cfg {Number} colspan Specifies the number of columns a cell should span
5675  * @cfg {String} headers Specifies one or more header cells a cell is related to
5676  * @cfg {Number} height Sets the height of a cell
5677  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5678  * @cfg {Number} rowspan Sets the number of rows a cell should span
5679  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5680  * @cfg {String} valign Vertical aligns the content in a cell
5681  * @cfg {Number} width Specifies the width of a cell
5682  * 
5683  * @constructor
5684  * Create a new TableCell
5685  * @param {Object} config The config object
5686  */
5687
5688 Roo.bootstrap.TableCell = function(config){
5689     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5693     
5694     html: false,
5695     cls: false,
5696     tag: false,
5697     abbr: false,
5698     align: false,
5699     axis: false,
5700     bgcolor: false,
5701     charoff: false,
5702     colspan: false,
5703     headers: false,
5704     height: false,
5705     nowrap: false,
5706     rowspan: false,
5707     scope: false,
5708     valign: false,
5709     width: false,
5710     
5711     
5712     getAutoCreate : function(){
5713         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5714         
5715         cfg = {
5716             tag: 'td'
5717         }
5718         
5719         if(this.tag){
5720             cfg.tag = this.tag;
5721         }
5722         
5723         if (this.html) {
5724             cfg.html=this.html
5725         }
5726         if (this.cls) {
5727             cfg.cls=this.cls
5728         }
5729         if (this.abbr) {
5730             cfg.abbr=this.abbr
5731         }
5732         if (this.align) {
5733             cfg.align=this.align
5734         }
5735         if (this.axis) {
5736             cfg.axis=this.axis
5737         }
5738         if (this.bgcolor) {
5739             cfg.bgcolor=this.bgcolor
5740         }
5741         if (this.charoff) {
5742             cfg.charoff=this.charoff
5743         }
5744         if (this.colspan) {
5745             cfg.colspan=this.colspan
5746         }
5747         if (this.headers) {
5748             cfg.headers=this.headers
5749         }
5750         if (this.height) {
5751             cfg.height=this.height
5752         }
5753         if (this.nowrap) {
5754             cfg.nowrap=this.nowrap
5755         }
5756         if (this.rowspan) {
5757             cfg.rowspan=this.rowspan
5758         }
5759         if (this.scope) {
5760             cfg.scope=this.scope
5761         }
5762         if (this.valign) {
5763             cfg.valign=this.valign
5764         }
5765         if (this.width) {
5766             cfg.width=this.width
5767         }
5768         
5769         
5770         return cfg;
5771     }
5772    
5773 });
5774
5775  
5776
5777  /*
5778  * - LGPL
5779  *
5780  * table row
5781  * 
5782  */
5783
5784 /**
5785  * @class Roo.bootstrap.TableRow
5786  * @extends Roo.bootstrap.Component
5787  * Bootstrap TableRow class
5788  * @cfg {String} cls row class
5789  * @cfg {String} align Aligns the content in a table row
5790  * @cfg {String} bgcolor Specifies a background color for a table row
5791  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5792  * @cfg {String} valign Vertical aligns the content in a table row
5793  * 
5794  * @constructor
5795  * Create a new TableRow
5796  * @param {Object} config The config object
5797  */
5798
5799 Roo.bootstrap.TableRow = function(config){
5800     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5801 };
5802
5803 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5804     
5805     cls: false,
5806     align: false,
5807     bgcolor: false,
5808     charoff: false,
5809     valign: false,
5810     
5811     getAutoCreate : function(){
5812         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5813         
5814         cfg = {
5815             tag: 'tr'
5816         }
5817             
5818         if(this.cls){
5819             cfg.cls = this.cls;
5820         }
5821         if(this.align){
5822             cfg.align = this.align;
5823         }
5824         if(this.bgcolor){
5825             cfg.bgcolor = this.bgcolor;
5826         }
5827         if(this.charoff){
5828             cfg.charoff = this.charoff;
5829         }
5830         if(this.valign){
5831             cfg.valign = this.valign;
5832         }
5833         
5834         return cfg;
5835     }
5836    
5837 });
5838
5839  
5840
5841  /*
5842  * - LGPL
5843  *
5844  * table body
5845  * 
5846  */
5847
5848 /**
5849  * @class Roo.bootstrap.TableBody
5850  * @extends Roo.bootstrap.Component
5851  * Bootstrap TableBody class
5852  * @cfg {String} cls element class
5853  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5854  * @cfg {String} align Aligns the content inside the element
5855  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5856  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5857  * 
5858  * @constructor
5859  * Create a new TableBody
5860  * @param {Object} config The config object
5861  */
5862
5863 Roo.bootstrap.TableBody = function(config){
5864     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5865 };
5866
5867 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5868     
5869     cls: false,
5870     tag: false,
5871     align: false,
5872     charoff: false,
5873     valign: false,
5874     
5875     getAutoCreate : function(){
5876         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5877         
5878         cfg = {
5879             tag: 'tbody'
5880         }
5881             
5882         if (this.cls) {
5883             cfg.cls=this.cls
5884         }
5885         if(this.tag){
5886             cfg.tag = this.tag;
5887         }
5888         
5889         if(this.align){
5890             cfg.align = this.align;
5891         }
5892         if(this.charoff){
5893             cfg.charoff = this.charoff;
5894         }
5895         if(this.valign){
5896             cfg.valign = this.valign;
5897         }
5898         
5899         return cfg;
5900     }
5901     
5902     
5903 //    initEvents : function()
5904 //    {
5905 //        
5906 //        if(!this.store){
5907 //            return;
5908 //        }
5909 //        
5910 //        this.store = Roo.factory(this.store, Roo.data);
5911 //        this.store.on('load', this.onLoad, this);
5912 //        
5913 //        this.store.load();
5914 //        
5915 //    },
5916 //    
5917 //    onLoad: function () 
5918 //    {   
5919 //        this.fireEvent('load', this);
5920 //    }
5921 //    
5922 //   
5923 });
5924
5925  
5926
5927  /*
5928  * Based on:
5929  * Ext JS Library 1.1.1
5930  * Copyright(c) 2006-2007, Ext JS, LLC.
5931  *
5932  * Originally Released Under LGPL - original licence link has changed is not relivant.
5933  *
5934  * Fork - LGPL
5935  * <script type="text/javascript">
5936  */
5937
5938 // as we use this in bootstrap.
5939 Roo.namespace('Roo.form');
5940  /**
5941  * @class Roo.form.Action
5942  * Internal Class used to handle form actions
5943  * @constructor
5944  * @param {Roo.form.BasicForm} el The form element or its id
5945  * @param {Object} config Configuration options
5946  */
5947
5948  
5949  
5950 // define the action interface
5951 Roo.form.Action = function(form, options){
5952     this.form = form;
5953     this.options = options || {};
5954 };
5955 /**
5956  * Client Validation Failed
5957  * @const 
5958  */
5959 Roo.form.Action.CLIENT_INVALID = 'client';
5960 /**
5961  * Server Validation Failed
5962  * @const 
5963  */
5964 Roo.form.Action.SERVER_INVALID = 'server';
5965  /**
5966  * Connect to Server Failed
5967  * @const 
5968  */
5969 Roo.form.Action.CONNECT_FAILURE = 'connect';
5970 /**
5971  * Reading Data from Server Failed
5972  * @const 
5973  */
5974 Roo.form.Action.LOAD_FAILURE = 'load';
5975
5976 Roo.form.Action.prototype = {
5977     type : 'default',
5978     failureType : undefined,
5979     response : undefined,
5980     result : undefined,
5981
5982     // interface method
5983     run : function(options){
5984
5985     },
5986
5987     // interface method
5988     success : function(response){
5989
5990     },
5991
5992     // interface method
5993     handleResponse : function(response){
5994
5995     },
5996
5997     // default connection failure
5998     failure : function(response){
5999         
6000         this.response = response;
6001         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6002         this.form.afterAction(this, false);
6003     },
6004
6005     processResponse : function(response){
6006         this.response = response;
6007         if(!response.responseText){
6008             return true;
6009         }
6010         this.result = this.handleResponse(response);
6011         return this.result;
6012     },
6013
6014     // utility functions used internally
6015     getUrl : function(appendParams){
6016         var url = this.options.url || this.form.url || this.form.el.dom.action;
6017         if(appendParams){
6018             var p = this.getParams();
6019             if(p){
6020                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6021             }
6022         }
6023         return url;
6024     },
6025
6026     getMethod : function(){
6027         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6028     },
6029
6030     getParams : function(){
6031         var bp = this.form.baseParams;
6032         var p = this.options.params;
6033         if(p){
6034             if(typeof p == "object"){
6035                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6036             }else if(typeof p == 'string' && bp){
6037                 p += '&' + Roo.urlEncode(bp);
6038             }
6039         }else if(bp){
6040             p = Roo.urlEncode(bp);
6041         }
6042         return p;
6043     },
6044
6045     createCallback : function(){
6046         return {
6047             success: this.success,
6048             failure: this.failure,
6049             scope: this,
6050             timeout: (this.form.timeout*1000),
6051             upload: this.form.fileUpload ? this.success : undefined
6052         };
6053     }
6054 };
6055
6056 Roo.form.Action.Submit = function(form, options){
6057     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6058 };
6059
6060 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6061     type : 'submit',
6062
6063     haveProgress : false,
6064     uploadComplete : false,
6065     
6066     // uploadProgress indicator.
6067     uploadProgress : function()
6068     {
6069         if (!this.form.progressUrl) {
6070             return;
6071         }
6072         
6073         if (!this.haveProgress) {
6074             Roo.MessageBox.progress("Uploading", "Uploading");
6075         }
6076         if (this.uploadComplete) {
6077            Roo.MessageBox.hide();
6078            return;
6079         }
6080         
6081         this.haveProgress = true;
6082    
6083         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6084         
6085         var c = new Roo.data.Connection();
6086         c.request({
6087             url : this.form.progressUrl,
6088             params: {
6089                 id : uid
6090             },
6091             method: 'GET',
6092             success : function(req){
6093                //console.log(data);
6094                 var rdata = false;
6095                 var edata;
6096                 try  {
6097                    rdata = Roo.decode(req.responseText)
6098                 } catch (e) {
6099                     Roo.log("Invalid data from server..");
6100                     Roo.log(edata);
6101                     return;
6102                 }
6103                 if (!rdata || !rdata.success) {
6104                     Roo.log(rdata);
6105                     Roo.MessageBox.alert(Roo.encode(rdata));
6106                     return;
6107                 }
6108                 var data = rdata.data;
6109                 
6110                 if (this.uploadComplete) {
6111                    Roo.MessageBox.hide();
6112                    return;
6113                 }
6114                    
6115                 if (data){
6116                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6117                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6118                     );
6119                 }
6120                 this.uploadProgress.defer(2000,this);
6121             },
6122        
6123             failure: function(data) {
6124                 Roo.log('progress url failed ');
6125                 Roo.log(data);
6126             },
6127             scope : this
6128         });
6129            
6130     },
6131     
6132     
6133     run : function()
6134     {
6135         // run get Values on the form, so it syncs any secondary forms.
6136         this.form.getValues();
6137         
6138         var o = this.options;
6139         var method = this.getMethod();
6140         var isPost = method == 'POST';
6141         if(o.clientValidation === false || this.form.isValid()){
6142             
6143             if (this.form.progressUrl) {
6144                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6145                     (new Date() * 1) + '' + Math.random());
6146                     
6147             } 
6148             
6149             
6150             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6151                 form:this.form.el.dom,
6152                 url:this.getUrl(!isPost),
6153                 method: method,
6154                 params:isPost ? this.getParams() : null,
6155                 isUpload: this.form.fileUpload
6156             }));
6157             
6158             this.uploadProgress();
6159
6160         }else if (o.clientValidation !== false){ // client validation failed
6161             this.failureType = Roo.form.Action.CLIENT_INVALID;
6162             this.form.afterAction(this, false);
6163         }
6164     },
6165
6166     success : function(response)
6167     {
6168         this.uploadComplete= true;
6169         if (this.haveProgress) {
6170             Roo.MessageBox.hide();
6171         }
6172         
6173         
6174         var result = this.processResponse(response);
6175         if(result === true || result.success){
6176             this.form.afterAction(this, true);
6177             return;
6178         }
6179         if(result.errors){
6180             this.form.markInvalid(result.errors);
6181             this.failureType = Roo.form.Action.SERVER_INVALID;
6182         }
6183         this.form.afterAction(this, false);
6184     },
6185     failure : function(response)
6186     {
6187         this.uploadComplete= true;
6188         if (this.haveProgress) {
6189             Roo.MessageBox.hide();
6190         }
6191         
6192         this.response = response;
6193         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6194         this.form.afterAction(this, false);
6195     },
6196     
6197     handleResponse : function(response){
6198         if(this.form.errorReader){
6199             var rs = this.form.errorReader.read(response);
6200             var errors = [];
6201             if(rs.records){
6202                 for(var i = 0, len = rs.records.length; i < len; i++) {
6203                     var r = rs.records[i];
6204                     errors[i] = r.data;
6205                 }
6206             }
6207             if(errors.length < 1){
6208                 errors = null;
6209             }
6210             return {
6211                 success : rs.success,
6212                 errors : errors
6213             };
6214         }
6215         var ret = false;
6216         try {
6217             ret = Roo.decode(response.responseText);
6218         } catch (e) {
6219             ret = {
6220                 success: false,
6221                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6222                 errors : []
6223             };
6224         }
6225         return ret;
6226         
6227     }
6228 });
6229
6230
6231 Roo.form.Action.Load = function(form, options){
6232     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6233     this.reader = this.form.reader;
6234 };
6235
6236 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6237     type : 'load',
6238
6239     run : function(){
6240         
6241         Roo.Ajax.request(Roo.apply(
6242                 this.createCallback(), {
6243                     method:this.getMethod(),
6244                     url:this.getUrl(false),
6245                     params:this.getParams()
6246         }));
6247     },
6248
6249     success : function(response){
6250         
6251         var result = this.processResponse(response);
6252         if(result === true || !result.success || !result.data){
6253             this.failureType = Roo.form.Action.LOAD_FAILURE;
6254             this.form.afterAction(this, false);
6255             return;
6256         }
6257         this.form.clearInvalid();
6258         this.form.setValues(result.data);
6259         this.form.afterAction(this, true);
6260     },
6261
6262     handleResponse : function(response){
6263         if(this.form.reader){
6264             var rs = this.form.reader.read(response);
6265             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6266             return {
6267                 success : rs.success,
6268                 data : data
6269             };
6270         }
6271         return Roo.decode(response.responseText);
6272     }
6273 });
6274
6275 Roo.form.Action.ACTION_TYPES = {
6276     'load' : Roo.form.Action.Load,
6277     'submit' : Roo.form.Action.Submit
6278 };/*
6279  * - LGPL
6280  *
6281  * form
6282  * 
6283  */
6284
6285 /**
6286  * @class Roo.bootstrap.Form
6287  * @extends Roo.bootstrap.Component
6288  * Bootstrap Form class
6289  * @cfg {String} method  GET | POST (default POST)
6290  * @cfg {String} labelAlign top | left (default top)
6291  * @cfg {String} align left  | right - for navbars
6292  * @cfg {Boolean} loadMask load mask when submit (default true)
6293
6294  * 
6295  * @constructor
6296  * Create a new Form
6297  * @param {Object} config The config object
6298  */
6299
6300
6301 Roo.bootstrap.Form = function(config){
6302     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6303     this.addEvents({
6304         /**
6305          * @event clientvalidation
6306          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6307          * @param {Form} this
6308          * @param {Boolean} valid true if the form has passed client-side validation
6309          */
6310         clientvalidation: true,
6311         /**
6312          * @event beforeaction
6313          * Fires before any action is performed. Return false to cancel the action.
6314          * @param {Form} this
6315          * @param {Action} action The action to be performed
6316          */
6317         beforeaction: true,
6318         /**
6319          * @event actionfailed
6320          * Fires when an action fails.
6321          * @param {Form} this
6322          * @param {Action} action The action that failed
6323          */
6324         actionfailed : true,
6325         /**
6326          * @event actioncomplete
6327          * Fires when an action is completed.
6328          * @param {Form} this
6329          * @param {Action} action The action that completed
6330          */
6331         actioncomplete : true
6332     });
6333     
6334 };
6335
6336 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6337       
6338      /**
6339      * @cfg {String} method
6340      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6341      */
6342     method : 'POST',
6343     /**
6344      * @cfg {String} url
6345      * The URL to use for form actions if one isn't supplied in the action options.
6346      */
6347     /**
6348      * @cfg {Boolean} fileUpload
6349      * Set to true if this form is a file upload.
6350      */
6351      
6352     /**
6353      * @cfg {Object} baseParams
6354      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6355      */
6356       
6357     /**
6358      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6359      */
6360     timeout: 30,
6361     /**
6362      * @cfg {Sting} align (left|right) for navbar forms
6363      */
6364     align : 'left',
6365
6366     // private
6367     activeAction : null,
6368  
6369     /**
6370      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6371      * element by passing it or its id or mask the form itself by passing in true.
6372      * @type Mixed
6373      */
6374     waitMsgTarget : false,
6375     
6376     loadMask : true,
6377     
6378     getAutoCreate : function(){
6379         
6380         var cfg = {
6381             tag: 'form',
6382             method : this.method || 'POST',
6383             id : this.id || Roo.id(),
6384             cls : ''
6385         }
6386         if (this.parent().xtype.match(/^Nav/)) {
6387             cfg.cls = 'navbar-form navbar-' + this.align;
6388             
6389         }
6390         
6391         if (this.labelAlign == 'left' ) {
6392             cfg.cls += ' form-horizontal';
6393         }
6394         
6395         
6396         return cfg;
6397     },
6398     initEvents : function()
6399     {
6400         this.el.on('submit', this.onSubmit, this);
6401         // this was added as random key presses on the form where triggering form submit.
6402         this.el.on('keypress', function(e) {
6403             if (e.getCharCode() != 13) {
6404                 return true;
6405             }
6406             // we might need to allow it for textareas.. and some other items.
6407             // check e.getTarget().
6408             
6409             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6410                 return true;
6411             }
6412         
6413             Roo.log("keypress blocked");
6414             
6415             e.preventDefault();
6416             return false;
6417         });
6418         
6419     },
6420     // private
6421     onSubmit : function(e){
6422         e.stopEvent();
6423     },
6424     
6425      /**
6426      * Returns true if client-side validation on the form is successful.
6427      * @return Boolean
6428      */
6429     isValid : function(){
6430         var items = this.getItems();
6431         var valid = true;
6432         items.each(function(f){
6433            if(!f.validate()){
6434                valid = false;
6435                
6436            }
6437         });
6438         return valid;
6439     },
6440     /**
6441      * Returns true if any fields in this form have changed since their original load.
6442      * @return Boolean
6443      */
6444     isDirty : function(){
6445         var dirty = false;
6446         var items = this.getItems();
6447         items.each(function(f){
6448            if(f.isDirty()){
6449                dirty = true;
6450                return false;
6451            }
6452            return true;
6453         });
6454         return dirty;
6455     },
6456      /**
6457      * Performs a predefined action (submit or load) or custom actions you define on this form.
6458      * @param {String} actionName The name of the action type
6459      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6460      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6461      * accept other config options):
6462      * <pre>
6463 Property          Type             Description
6464 ----------------  ---------------  ----------------------------------------------------------------------------------
6465 url               String           The url for the action (defaults to the form's url)
6466 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6467 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6468 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6469                                    validate the form on the client (defaults to false)
6470      * </pre>
6471      * @return {BasicForm} this
6472      */
6473     doAction : function(action, options){
6474         if(typeof action == 'string'){
6475             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6476         }
6477         if(this.fireEvent('beforeaction', this, action) !== false){
6478             this.beforeAction(action);
6479             action.run.defer(100, action);
6480         }
6481         return this;
6482     },
6483     
6484     // private
6485     beforeAction : function(action){
6486         var o = action.options;
6487         
6488         if(this.loadMask){
6489             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6490         }
6491         // not really supported yet.. ??
6492         
6493         //if(this.waitMsgTarget === true){
6494         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6495         //}else if(this.waitMsgTarget){
6496         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6497         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6498         //}else {
6499         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6500        // }
6501          
6502     },
6503
6504     // private
6505     afterAction : function(action, success){
6506         this.activeAction = null;
6507         var o = action.options;
6508         
6509         //if(this.waitMsgTarget === true){
6510             this.el.unmask();
6511         //}else if(this.waitMsgTarget){
6512         //    this.waitMsgTarget.unmask();
6513         //}else{
6514         //    Roo.MessageBox.updateProgress(1);
6515         //    Roo.MessageBox.hide();
6516        // }
6517         // 
6518         if(success){
6519             if(o.reset){
6520                 this.reset();
6521             }
6522             Roo.callback(o.success, o.scope, [this, action]);
6523             this.fireEvent('actioncomplete', this, action);
6524             
6525         }else{
6526             
6527             // failure condition..
6528             // we have a scenario where updates need confirming.
6529             // eg. if a locking scenario exists..
6530             // we look for { errors : { needs_confirm : true }} in the response.
6531             if (
6532                 (typeof(action.result) != 'undefined')  &&
6533                 (typeof(action.result.errors) != 'undefined')  &&
6534                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6535            ){
6536                 var _t = this;
6537                 Roo.log("not supported yet");
6538                  /*
6539                 
6540                 Roo.MessageBox.confirm(
6541                     "Change requires confirmation",
6542                     action.result.errorMsg,
6543                     function(r) {
6544                         if (r != 'yes') {
6545                             return;
6546                         }
6547                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6548                     }
6549                     
6550                 );
6551                 */
6552                 
6553                 
6554                 return;
6555             }
6556             
6557             Roo.callback(o.failure, o.scope, [this, action]);
6558             // show an error message if no failed handler is set..
6559             if (!this.hasListener('actionfailed')) {
6560                 Roo.log("need to add dialog support");
6561                 /*
6562                 Roo.MessageBox.alert("Error",
6563                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6564                         action.result.errorMsg :
6565                         "Saving Failed, please check your entries or try again"
6566                 );
6567                 */
6568             }
6569             
6570             this.fireEvent('actionfailed', this, action);
6571         }
6572         
6573     },
6574     /**
6575      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6576      * @param {String} id The value to search for
6577      * @return Field
6578      */
6579     findField : function(id){
6580         var items = this.getItems();
6581         var field = items.get(id);
6582         if(!field){
6583              items.each(function(f){
6584                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6585                     field = f;
6586                     return false;
6587                 }
6588                 return true;
6589             });
6590         }
6591         return field || null;
6592     },
6593      /**
6594      * Mark fields in this form invalid in bulk.
6595      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6596      * @return {BasicForm} this
6597      */
6598     markInvalid : function(errors){
6599         if(errors instanceof Array){
6600             for(var i = 0, len = errors.length; i < len; i++){
6601                 var fieldError = errors[i];
6602                 var f = this.findField(fieldError.id);
6603                 if(f){
6604                     f.markInvalid(fieldError.msg);
6605                 }
6606             }
6607         }else{
6608             var field, id;
6609             for(id in errors){
6610                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6611                     field.markInvalid(errors[id]);
6612                 }
6613             }
6614         }
6615         //Roo.each(this.childForms || [], function (f) {
6616         //    f.markInvalid(errors);
6617         //});
6618         
6619         return this;
6620     },
6621
6622     /**
6623      * Set values for fields in this form in bulk.
6624      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6625      * @return {BasicForm} this
6626      */
6627     setValues : function(values){
6628         if(values instanceof Array){ // array of objects
6629             for(var i = 0, len = values.length; i < len; i++){
6630                 var v = values[i];
6631                 var f = this.findField(v.id);
6632                 if(f){
6633                     f.setValue(v.value);
6634                     if(this.trackResetOnLoad){
6635                         f.originalValue = f.getValue();
6636                     }
6637                 }
6638             }
6639         }else{ // object hash
6640             var field, id;
6641             for(id in values){
6642                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6643                     
6644                     if (field.setFromData && 
6645                         field.valueField && 
6646                         field.displayField &&
6647                         // combos' with local stores can 
6648                         // be queried via setValue()
6649                         // to set their value..
6650                         (field.store && !field.store.isLocal)
6651                         ) {
6652                         // it's a combo
6653                         var sd = { };
6654                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6655                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6656                         field.setFromData(sd);
6657                         
6658                     } else {
6659                         field.setValue(values[id]);
6660                     }
6661                     
6662                     
6663                     if(this.trackResetOnLoad){
6664                         field.originalValue = field.getValue();
6665                     }
6666                 }
6667             }
6668         }
6669          
6670         //Roo.each(this.childForms || [], function (f) {
6671         //    f.setValues(values);
6672         //});
6673                 
6674         return this;
6675     },
6676
6677     /**
6678      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6679      * they are returned as an array.
6680      * @param {Boolean} asString
6681      * @return {Object}
6682      */
6683     getValues : function(asString){
6684         //if (this.childForms) {
6685             // copy values from the child forms
6686         //    Roo.each(this.childForms, function (f) {
6687         //        this.setValues(f.getValues());
6688         //    }, this);
6689         //}
6690         
6691         
6692         
6693         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6694         if(asString === true){
6695             return fs;
6696         }
6697         return Roo.urlDecode(fs);
6698     },
6699     
6700     /**
6701      * Returns the fields in this form as an object with key/value pairs. 
6702      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6703      * @return {Object}
6704      */
6705     getFieldValues : function(with_hidden)
6706     {
6707         var items = this.getItems();
6708         var ret = {};
6709         items.each(function(f){
6710             if (!f.getName()) {
6711                 return;
6712             }
6713             var v = f.getValue();
6714             if (f.inputType =='radio') {
6715                 if (typeof(ret[f.getName()]) == 'undefined') {
6716                     ret[f.getName()] = ''; // empty..
6717                 }
6718                 
6719                 if (!f.el.dom.checked) {
6720                     return;
6721                     
6722                 }
6723                 v = f.el.dom.value;
6724                 
6725             }
6726             
6727             // not sure if this supported any more..
6728             if ((typeof(v) == 'object') && f.getRawValue) {
6729                 v = f.getRawValue() ; // dates..
6730             }
6731             // combo boxes where name != hiddenName...
6732             if (f.name != f.getName()) {
6733                 ret[f.name] = f.getRawValue();
6734             }
6735             ret[f.getName()] = v;
6736         });
6737         
6738         return ret;
6739     },
6740
6741     /**
6742      * Clears all invalid messages in this form.
6743      * @return {BasicForm} this
6744      */
6745     clearInvalid : function(){
6746         var items = this.getItems();
6747         
6748         items.each(function(f){
6749            f.clearInvalid();
6750         });
6751         
6752         
6753         
6754         return this;
6755     },
6756
6757     /**
6758      * Resets this form.
6759      * @return {BasicForm} this
6760      */
6761     reset : function(){
6762         var items = this.getItems();
6763         items.each(function(f){
6764             f.reset();
6765         });
6766         
6767         Roo.each(this.childForms || [], function (f) {
6768             f.reset();
6769         });
6770        
6771         
6772         return this;
6773     },
6774     getItems : function()
6775     {
6776         var r=new Roo.util.MixedCollection(false, function(o){
6777             return o.id || (o.id = Roo.id());
6778         });
6779         var iter = function(el) {
6780             if (el.inputEl) {
6781                 r.add(el);
6782             }
6783             if (!el.items) {
6784                 return;
6785             }
6786             Roo.each(el.items,function(e) {
6787                 iter(e);
6788             });
6789             
6790             
6791         };
6792         iter(this);
6793         return r;
6794         
6795         
6796         
6797         
6798     }
6799     
6800 });
6801
6802  
6803 /*
6804  * Based on:
6805  * Ext JS Library 1.1.1
6806  * Copyright(c) 2006-2007, Ext JS, LLC.
6807  *
6808  * Originally Released Under LGPL - original licence link has changed is not relivant.
6809  *
6810  * Fork - LGPL
6811  * <script type="text/javascript">
6812  */
6813 /**
6814  * @class Roo.form.VTypes
6815  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6816  * @singleton
6817  */
6818 Roo.form.VTypes = function(){
6819     // closure these in so they are only created once.
6820     var alpha = /^[a-zA-Z_]+$/;
6821     var alphanum = /^[a-zA-Z0-9_]+$/;
6822     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6823     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6824
6825     // All these messages and functions are configurable
6826     return {
6827         /**
6828          * The function used to validate email addresses
6829          * @param {String} value The email address
6830          */
6831         'email' : function(v){
6832             return email.test(v);
6833         },
6834         /**
6835          * The error text to display when the email validation function returns false
6836          * @type String
6837          */
6838         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6839         /**
6840          * The keystroke filter mask to be applied on email input
6841          * @type RegExp
6842          */
6843         'emailMask' : /[a-z0-9_\.\-@]/i,
6844
6845         /**
6846          * The function used to validate URLs
6847          * @param {String} value The URL
6848          */
6849         'url' : function(v){
6850             return url.test(v);
6851         },
6852         /**
6853          * The error text to display when the url validation function returns false
6854          * @type String
6855          */
6856         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6857         
6858         /**
6859          * The function used to validate alpha values
6860          * @param {String} value The value
6861          */
6862         'alpha' : function(v){
6863             return alpha.test(v);
6864         },
6865         /**
6866          * The error text to display when the alpha validation function returns false
6867          * @type String
6868          */
6869         'alphaText' : 'This field should only contain letters and _',
6870         /**
6871          * The keystroke filter mask to be applied on alpha input
6872          * @type RegExp
6873          */
6874         'alphaMask' : /[a-z_]/i,
6875
6876         /**
6877          * The function used to validate alphanumeric values
6878          * @param {String} value The value
6879          */
6880         'alphanum' : function(v){
6881             return alphanum.test(v);
6882         },
6883         /**
6884          * The error text to display when the alphanumeric validation function returns false
6885          * @type String
6886          */
6887         'alphanumText' : 'This field should only contain letters, numbers and _',
6888         /**
6889          * The keystroke filter mask to be applied on alphanumeric input
6890          * @type RegExp
6891          */
6892         'alphanumMask' : /[a-z0-9_]/i
6893     };
6894 }();/*
6895  * - LGPL
6896  *
6897  * Input
6898  * 
6899  */
6900
6901 /**
6902  * @class Roo.bootstrap.Input
6903  * @extends Roo.bootstrap.Component
6904  * Bootstrap Input class
6905  * @cfg {Boolean} disabled is it disabled
6906  * @cfg {String} fieldLabel - the label associated
6907  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6908  * @cfg {String} name name of the input
6909  * @cfg {string} fieldLabel - the label associated
6910  * @cfg {string}  inputType - input / file submit ...
6911  * @cfg {string} placeholder - placeholder to put in text.
6912  * @cfg {string}  before - input group add on before
6913  * @cfg {string} after - input group add on after
6914  * @cfg {string} size - (lg|sm) or leave empty..
6915  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6916  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6917  * @cfg {Number} md colspan out of 12 for computer-sized screens
6918  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6919  * @cfg {string} value default value of the input
6920  * @cfg {Number} labelWidth set the width of label (0-12)
6921  * @cfg {String} labelAlign (top|left)
6922  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6923  * @cfg {String} align (left|center|right) Default left
6924  * 
6925  * 
6926  * @constructor
6927  * Create a new Input
6928  * @param {Object} config The config object
6929  */
6930
6931 Roo.bootstrap.Input = function(config){
6932     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6933    
6934         this.addEvents({
6935             /**
6936              * @event focus
6937              * Fires when this field receives input focus.
6938              * @param {Roo.form.Field} this
6939              */
6940             focus : true,
6941             /**
6942              * @event blur
6943              * Fires when this field loses input focus.
6944              * @param {Roo.form.Field} this
6945              */
6946             blur : true,
6947             /**
6948              * @event specialkey
6949              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6950              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6951              * @param {Roo.form.Field} this
6952              * @param {Roo.EventObject} e The event object
6953              */
6954             specialkey : true,
6955             /**
6956              * @event change
6957              * Fires just before the field blurs if the field value has changed.
6958              * @param {Roo.form.Field} this
6959              * @param {Mixed} newValue The new value
6960              * @param {Mixed} oldValue The original value
6961              */
6962             change : true,
6963             /**
6964              * @event invalid
6965              * Fires after the field has been marked as invalid.
6966              * @param {Roo.form.Field} this
6967              * @param {String} msg The validation message
6968              */
6969             invalid : true,
6970             /**
6971              * @event valid
6972              * Fires after the field has been validated with no errors.
6973              * @param {Roo.form.Field} this
6974              */
6975             valid : true,
6976              /**
6977              * @event keyup
6978              * Fires after the key up
6979              * @param {Roo.form.Field} this
6980              * @param {Roo.EventObject}  e The event Object
6981              */
6982             keyup : true
6983         });
6984 };
6985
6986 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6987      /**
6988      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6989       automatic validation (defaults to "keyup").
6990      */
6991     validationEvent : "keyup",
6992      /**
6993      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6994      */
6995     validateOnBlur : true,
6996     /**
6997      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6998      */
6999     validationDelay : 250,
7000      /**
7001      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7002      */
7003     focusClass : "x-form-focus",  // not needed???
7004     
7005        
7006     /**
7007      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7008      */
7009     invalidClass : "has-error",
7010     
7011     /**
7012      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7013      */
7014     selectOnFocus : false,
7015     
7016      /**
7017      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7018      */
7019     maskRe : null,
7020        /**
7021      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7022      */
7023     vtype : null,
7024     
7025       /**
7026      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7027      */
7028     disableKeyFilter : false,
7029     
7030        /**
7031      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7032      */
7033     disabled : false,
7034      /**
7035      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7036      */
7037     allowBlank : true,
7038     /**
7039      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7040      */
7041     blankText : "This field is required",
7042     
7043      /**
7044      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7045      */
7046     minLength : 0,
7047     /**
7048      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7049      */
7050     maxLength : Number.MAX_VALUE,
7051     /**
7052      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7053      */
7054     minLengthText : "The minimum length for this field is {0}",
7055     /**
7056      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7057      */
7058     maxLengthText : "The maximum length for this field is {0}",
7059   
7060     
7061     /**
7062      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7063      * If available, this function will be called only after the basic validators all return true, and will be passed the
7064      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7065      */
7066     validator : null,
7067     /**
7068      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7069      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7070      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7071      */
7072     regex : null,
7073     /**
7074      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7075      */
7076     regexText : "",
7077     
7078     
7079     
7080     fieldLabel : '',
7081     inputType : 'text',
7082     
7083     name : false,
7084     placeholder: false,
7085     before : false,
7086     after : false,
7087     size : false,
7088     // private
7089     hasFocus : false,
7090     preventMark: false,
7091     isFormField : true,
7092     value : '',
7093     labelWidth : 2,
7094     labelAlign : false,
7095     readOnly : false,
7096     align : false,
7097     formatedValue : false,
7098     
7099     parentLabelAlign : function()
7100     {
7101         var parent = this;
7102         while (parent.parent()) {
7103             parent = parent.parent();
7104             if (typeof(parent.labelAlign) !='undefined') {
7105                 return parent.labelAlign;
7106             }
7107         }
7108         return 'left';
7109         
7110     },
7111     
7112     getAutoCreate : function(){
7113         
7114         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7115         
7116         var id = Roo.id();
7117         
7118         var cfg = {};
7119         
7120         if(this.inputType != 'hidden'){
7121             cfg.cls = 'form-group' //input-group
7122         }
7123         
7124         var input =  {
7125             tag: 'input',
7126             id : id,
7127             type : this.inputType,
7128             value : this.value,
7129             cls : 'form-control',
7130             placeholder : this.placeholder || ''
7131             
7132         };
7133         
7134         if(this.align){
7135             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7136         }
7137         
7138         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7139             input.maxLength = this.maxLength;
7140         }
7141         
7142         if (this.disabled) {
7143             input.disabled=true;
7144         }
7145         
7146         if (this.readOnly) {
7147             input.readonly=true;
7148         }
7149         
7150         if (this.name) {
7151             input.name = this.name;
7152         }
7153         if (this.size) {
7154             input.cls += ' input-' + this.size;
7155         }
7156         var settings=this;
7157         ['xs','sm','md','lg'].map(function(size){
7158             if (settings[size]) {
7159                 cfg.cls += ' col-' + size + '-' + settings[size];
7160             }
7161         });
7162         
7163         var inputblock = input;
7164         
7165         if (this.before || this.after) {
7166             
7167             inputblock = {
7168                 cls : 'input-group',
7169                 cn :  [] 
7170             };
7171             if (this.before && typeof(this.before) == 'string') {
7172                 
7173                 inputblock.cn.push({
7174                     tag :'span',
7175                     cls : 'roo-input-before input-group-addon',
7176                     html : this.before
7177                 });
7178             }
7179             if (this.before && typeof(this.before) == 'object') {
7180                 this.before = Roo.factory(this.before);
7181                 Roo.log(this.before);
7182                 inputblock.cn.push({
7183                     tag :'span',
7184                     cls : 'roo-input-before input-group-' +
7185                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7186                 });
7187             }
7188             
7189             inputblock.cn.push(input);
7190             
7191             if (this.after && typeof(this.after) == 'string') {
7192                 inputblock.cn.push({
7193                     tag :'span',
7194                     cls : 'roo-input-after input-group-addon',
7195                     html : this.after
7196                 });
7197             }
7198             if (this.after && typeof(this.after) == 'object') {
7199                 this.after = Roo.factory(this.after);
7200                 Roo.log(this.after);
7201                 inputblock.cn.push({
7202                     tag :'span',
7203                     cls : 'roo-input-after input-group-' +
7204                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7205                 });
7206             }
7207         };
7208         
7209         if (align ==='left' && this.fieldLabel.length) {
7210                 Roo.log("left and has label");
7211                 cfg.cn = [
7212                     
7213                     {
7214                         tag: 'label',
7215                         'for' :  id,
7216                         cls : 'control-label col-sm-' + this.labelWidth,
7217                         html : this.fieldLabel
7218                         
7219                     },
7220                     {
7221                         cls : "col-sm-" + (12 - this.labelWidth), 
7222                         cn: [
7223                             inputblock
7224                         ]
7225                     }
7226                     
7227                 ];
7228         } else if ( this.fieldLabel.length) {
7229                 Roo.log(" label");
7230                  cfg.cn = [
7231                    
7232                     {
7233                         tag: 'label',
7234                         //cls : 'input-group-addon',
7235                         html : this.fieldLabel
7236                         
7237                     },
7238                     
7239                     inputblock
7240                     
7241                 ];
7242
7243         } else {
7244             
7245                 Roo.log(" no label && no align");
7246                 cfg.cn = [
7247                     
7248                         inputblock
7249                     
7250                 ];
7251                 
7252                 
7253         };
7254         Roo.log('input-parentType: ' + this.parentType);
7255         
7256         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7257            cfg.cls += ' navbar-form';
7258            Roo.log(cfg);
7259         }
7260         
7261         return cfg;
7262         
7263     },
7264     /**
7265      * return the real input element.
7266      */
7267     inputEl: function ()
7268     {
7269         return this.el.select('input.form-control',true).first();
7270     },
7271     
7272     tooltipEl : function()
7273     {
7274         return this.inputEl();
7275     },
7276     
7277     setDisabled : function(v)
7278     {
7279         var i  = this.inputEl().dom;
7280         if (!v) {
7281             i.removeAttribute('disabled');
7282             return;
7283             
7284         }
7285         i.setAttribute('disabled','true');
7286     },
7287     initEvents : function()
7288     {
7289           
7290         this.inputEl().on("keydown" , this.fireKey,  this);
7291         this.inputEl().on("focus", this.onFocus,  this);
7292         this.inputEl().on("blur", this.onBlur,  this);
7293         
7294         this.inputEl().relayEvent('keyup', this);
7295
7296         // reference to original value for reset
7297         this.originalValue = this.getValue();
7298         //Roo.form.TextField.superclass.initEvents.call(this);
7299         if(this.validationEvent == 'keyup'){
7300             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7301             this.inputEl().on('keyup', this.filterValidation, this);
7302         }
7303         else if(this.validationEvent !== false){
7304             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7305         }
7306         
7307         if(this.selectOnFocus){
7308             this.on("focus", this.preFocus, this);
7309             
7310         }
7311         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7312             this.inputEl().on("keypress", this.filterKeys, this);
7313         }
7314        /* if(this.grow){
7315             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7316             this.el.on("click", this.autoSize,  this);
7317         }
7318         */
7319         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7320             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7321         }
7322         
7323         if (typeof(this.before) == 'object') {
7324             this.before.render(this.el.select('.roo-input-before',true).first());
7325         }
7326         if (typeof(this.after) == 'object') {
7327             this.after.render(this.el.select('.roo-input-after',true).first());
7328         }
7329         
7330         
7331     },
7332     filterValidation : function(e){
7333         if(!e.isNavKeyPress()){
7334             this.validationTask.delay(this.validationDelay);
7335         }
7336     },
7337      /**
7338      * Validates the field value
7339      * @return {Boolean} True if the value is valid, else false
7340      */
7341     validate : function(){
7342         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7343         if(this.disabled || this.validateValue(this.getRawValue())){
7344             this.clearInvalid();
7345             return true;
7346         }
7347         return false;
7348     },
7349     
7350     
7351     /**
7352      * Validates a value according to the field's validation rules and marks the field as invalid
7353      * if the validation fails
7354      * @param {Mixed} value The value to validate
7355      * @return {Boolean} True if the value is valid, else false
7356      */
7357     validateValue : function(value){
7358         if(value.length < 1)  { // if it's blank
7359              if(this.allowBlank){
7360                 this.clearInvalid();
7361                 return true;
7362              }else{
7363                 this.markInvalid(this.blankText);
7364                 return false;
7365              }
7366         }
7367         if(value.length < this.minLength){
7368             this.markInvalid(String.format(this.minLengthText, this.minLength));
7369             return false;
7370         }
7371         if(value.length > this.maxLength){
7372             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7373             return false;
7374         }
7375         if(this.vtype){
7376             var vt = Roo.form.VTypes;
7377             if(!vt[this.vtype](value, this)){
7378                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7379                 return false;
7380             }
7381         }
7382         if(typeof this.validator == "function"){
7383             var msg = this.validator(value);
7384             if(msg !== true){
7385                 this.markInvalid(msg);
7386                 return false;
7387             }
7388         }
7389         if(this.regex && !this.regex.test(value)){
7390             this.markInvalid(this.regexText);
7391             return false;
7392         }
7393         return true;
7394     },
7395
7396     
7397     
7398      // private
7399     fireKey : function(e){
7400         //Roo.log('field ' + e.getKey());
7401         if(e.isNavKeyPress()){
7402             this.fireEvent("specialkey", this, e);
7403         }
7404     },
7405     focus : function (selectText){
7406         if(this.rendered){
7407             this.inputEl().focus();
7408             if(selectText === true){
7409                 this.inputEl().dom.select();
7410             }
7411         }
7412         return this;
7413     } ,
7414     
7415     onFocus : function(){
7416         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7417            // this.el.addClass(this.focusClass);
7418         }
7419         if(!this.hasFocus){
7420             this.hasFocus = true;
7421             this.startValue = this.getValue();
7422             this.fireEvent("focus", this);
7423         }
7424     },
7425     
7426     beforeBlur : Roo.emptyFn,
7427
7428     
7429     // private
7430     onBlur : function(){
7431         this.beforeBlur();
7432         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7433             //this.el.removeClass(this.focusClass);
7434         }
7435         this.hasFocus = false;
7436         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7437             this.validate();
7438         }
7439         var v = this.getValue();
7440         if(String(v) !== String(this.startValue)){
7441             this.fireEvent('change', this, v, this.startValue);
7442         }
7443         this.fireEvent("blur", this);
7444     },
7445     
7446     /**
7447      * Resets the current field value to the originally loaded value and clears any validation messages
7448      */
7449     reset : function(){
7450         this.setValue(this.originalValue);
7451         this.clearInvalid();
7452     },
7453      /**
7454      * Returns the name of the field
7455      * @return {Mixed} name The name field
7456      */
7457     getName: function(){
7458         return this.name;
7459     },
7460      /**
7461      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7462      * @return {Mixed} value The field value
7463      */
7464     getValue : function(){
7465         
7466         var v = this.inputEl().getValue();
7467         
7468         return v;
7469     },
7470     /**
7471      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7472      * @return {Mixed} value The field value
7473      */
7474     getRawValue : function(){
7475         var v = this.inputEl().getValue();
7476         
7477         return v;
7478     },
7479     
7480     /**
7481      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7482      * @param {Mixed} value The value to set
7483      */
7484     setRawValue : function(v){
7485         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7486     },
7487     
7488     selectText : function(start, end){
7489         var v = this.getRawValue();
7490         if(v.length > 0){
7491             start = start === undefined ? 0 : start;
7492             end = end === undefined ? v.length : end;
7493             var d = this.inputEl().dom;
7494             if(d.setSelectionRange){
7495                 d.setSelectionRange(start, end);
7496             }else if(d.createTextRange){
7497                 var range = d.createTextRange();
7498                 range.moveStart("character", start);
7499                 range.moveEnd("character", v.length-end);
7500                 range.select();
7501             }
7502         }
7503     },
7504     
7505     /**
7506      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7507      * @param {Mixed} value The value to set
7508      */
7509     setValue : function(v){
7510         this.value = v;
7511         if(this.rendered){
7512             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7513             this.validate();
7514         }
7515     },
7516     
7517     /*
7518     processValue : function(value){
7519         if(this.stripCharsRe){
7520             var newValue = value.replace(this.stripCharsRe, '');
7521             if(newValue !== value){
7522                 this.setRawValue(newValue);
7523                 return newValue;
7524             }
7525         }
7526         return value;
7527     },
7528   */
7529     preFocus : function(){
7530         
7531         if(this.selectOnFocus){
7532             this.inputEl().dom.select();
7533         }
7534     },
7535     filterKeys : function(e){
7536         var k = e.getKey();
7537         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7538             return;
7539         }
7540         var c = e.getCharCode(), cc = String.fromCharCode(c);
7541         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7542             return;
7543         }
7544         if(!this.maskRe.test(cc)){
7545             e.stopEvent();
7546         }
7547     },
7548      /**
7549      * Clear any invalid styles/messages for this field
7550      */
7551     clearInvalid : function(){
7552         
7553         if(!this.el || this.preventMark){ // not rendered
7554             return;
7555         }
7556         this.el.removeClass(this.invalidClass);
7557         /*
7558         switch(this.msgTarget){
7559             case 'qtip':
7560                 this.el.dom.qtip = '';
7561                 break;
7562             case 'title':
7563                 this.el.dom.title = '';
7564                 break;
7565             case 'under':
7566                 if(this.errorEl){
7567                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7568                 }
7569                 break;
7570             case 'side':
7571                 if(this.errorIcon){
7572                     this.errorIcon.dom.qtip = '';
7573                     this.errorIcon.hide();
7574                     this.un('resize', this.alignErrorIcon, this);
7575                 }
7576                 break;
7577             default:
7578                 var t = Roo.getDom(this.msgTarget);
7579                 t.innerHTML = '';
7580                 t.style.display = 'none';
7581                 break;
7582         }
7583         */
7584         this.fireEvent('valid', this);
7585     },
7586      /**
7587      * Mark this field as invalid
7588      * @param {String} msg The validation message
7589      */
7590     markInvalid : function(msg){
7591         if(!this.el  || this.preventMark){ // not rendered
7592             return;
7593         }
7594         this.el.addClass(this.invalidClass);
7595         /*
7596         msg = msg || this.invalidText;
7597         switch(this.msgTarget){
7598             case 'qtip':
7599                 this.el.dom.qtip = msg;
7600                 this.el.dom.qclass = 'x-form-invalid-tip';
7601                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7602                     Roo.QuickTips.enable();
7603                 }
7604                 break;
7605             case 'title':
7606                 this.el.dom.title = msg;
7607                 break;
7608             case 'under':
7609                 if(!this.errorEl){
7610                     var elp = this.el.findParent('.x-form-element', 5, true);
7611                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7612                     this.errorEl.setWidth(elp.getWidth(true)-20);
7613                 }
7614                 this.errorEl.update(msg);
7615                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7616                 break;
7617             case 'side':
7618                 if(!this.errorIcon){
7619                     var elp = this.el.findParent('.x-form-element', 5, true);
7620                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7621                 }
7622                 this.alignErrorIcon();
7623                 this.errorIcon.dom.qtip = msg;
7624                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7625                 this.errorIcon.show();
7626                 this.on('resize', this.alignErrorIcon, this);
7627                 break;
7628             default:
7629                 var t = Roo.getDom(this.msgTarget);
7630                 t.innerHTML = msg;
7631                 t.style.display = this.msgDisplay;
7632                 break;
7633         }
7634         */
7635         this.fireEvent('invalid', this, msg);
7636     },
7637     // private
7638     SafariOnKeyDown : function(event)
7639     {
7640         // this is a workaround for a password hang bug on chrome/ webkit.
7641         
7642         var isSelectAll = false;
7643         
7644         if(this.inputEl().dom.selectionEnd > 0){
7645             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7646         }
7647         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7648             event.preventDefault();
7649             this.setValue('');
7650             return;
7651         }
7652         
7653         if(isSelectAll){ // backspace and delete key
7654             
7655             event.preventDefault();
7656             // this is very hacky as keydown always get's upper case.
7657             //
7658             var cc = String.fromCharCode(event.getCharCode());
7659             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7660             
7661         }
7662     },
7663     adjustWidth : function(tag, w){
7664         tag = tag.toLowerCase();
7665         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7666             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7667                 if(tag == 'input'){
7668                     return w + 2;
7669                 }
7670                 if(tag == 'textarea'){
7671                     return w-2;
7672                 }
7673             }else if(Roo.isOpera){
7674                 if(tag == 'input'){
7675                     return w + 2;
7676                 }
7677                 if(tag == 'textarea'){
7678                     return w-2;
7679                 }
7680             }
7681         }
7682         return w;
7683     }
7684     
7685 });
7686
7687  
7688 /*
7689  * - LGPL
7690  *
7691  * Input
7692  * 
7693  */
7694
7695 /**
7696  * @class Roo.bootstrap.TextArea
7697  * @extends Roo.bootstrap.Input
7698  * Bootstrap TextArea class
7699  * @cfg {Number} cols Specifies the visible width of a text area
7700  * @cfg {Number} rows Specifies the visible number of lines in a text area
7701  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7702  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7703  * @cfg {string} html text
7704  * 
7705  * @constructor
7706  * Create a new TextArea
7707  * @param {Object} config The config object
7708  */
7709
7710 Roo.bootstrap.TextArea = function(config){
7711     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7712    
7713 };
7714
7715 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7716      
7717     cols : false,
7718     rows : 5,
7719     readOnly : false,
7720     warp : 'soft',
7721     resize : false,
7722     value: false,
7723     html: false,
7724     
7725     getAutoCreate : function(){
7726         
7727         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7728         
7729         var id = Roo.id();
7730         
7731         var cfg = {};
7732         
7733         var input =  {
7734             tag: 'textarea',
7735             id : id,
7736             warp : this.warp,
7737             rows : this.rows,
7738             value : this.value || '',
7739             html: this.html || '',
7740             cls : 'form-control',
7741             placeholder : this.placeholder || '' 
7742             
7743         };
7744         
7745         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7746             input.maxLength = this.maxLength;
7747         }
7748         
7749         if(this.resize){
7750             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7751         }
7752         
7753         if(this.cols){
7754             input.cols = this.cols;
7755         }
7756         
7757         if (this.readOnly) {
7758             input.readonly = true;
7759         }
7760         
7761         if (this.name) {
7762             input.name = this.name;
7763         }
7764         
7765         if (this.size) {
7766             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7767         }
7768         
7769         var settings=this;
7770         ['xs','sm','md','lg'].map(function(size){
7771             if (settings[size]) {
7772                 cfg.cls += ' col-' + size + '-' + settings[size];
7773             }
7774         });
7775         
7776         var inputblock = input;
7777         
7778         if (this.before || this.after) {
7779             
7780             inputblock = {
7781                 cls : 'input-group',
7782                 cn :  [] 
7783             };
7784             if (this.before) {
7785                 inputblock.cn.push({
7786                     tag :'span',
7787                     cls : 'input-group-addon',
7788                     html : this.before
7789                 });
7790             }
7791             inputblock.cn.push(input);
7792             if (this.after) {
7793                 inputblock.cn.push({
7794                     tag :'span',
7795                     cls : 'input-group-addon',
7796                     html : this.after
7797                 });
7798             }
7799             
7800         }
7801         
7802         if (align ==='left' && this.fieldLabel.length) {
7803                 Roo.log("left and has label");
7804                 cfg.cn = [
7805                     
7806                     {
7807                         tag: 'label',
7808                         'for' :  id,
7809                         cls : 'control-label col-sm-' + this.labelWidth,
7810                         html : this.fieldLabel
7811                         
7812                     },
7813                     {
7814                         cls : "col-sm-" + (12 - this.labelWidth), 
7815                         cn: [
7816                             inputblock
7817                         ]
7818                     }
7819                     
7820                 ];
7821         } else if ( this.fieldLabel.length) {
7822                 Roo.log(" label");
7823                  cfg.cn = [
7824                    
7825                     {
7826                         tag: 'label',
7827                         //cls : 'input-group-addon',
7828                         html : this.fieldLabel
7829                         
7830                     },
7831                     
7832                     inputblock
7833                     
7834                 ];
7835
7836         } else {
7837             
7838                    Roo.log(" no label && no align");
7839                 cfg.cn = [
7840                     
7841                         inputblock
7842                     
7843                 ];
7844                 
7845                 
7846         }
7847         
7848         if (this.disabled) {
7849             input.disabled=true;
7850         }
7851         
7852         return cfg;
7853         
7854     },
7855     /**
7856      * return the real textarea element.
7857      */
7858     inputEl: function ()
7859     {
7860         return this.el.select('textarea.form-control',true).first();
7861     }
7862 });
7863
7864  
7865 /*
7866  * - LGPL
7867  *
7868  * trigger field - base class for combo..
7869  * 
7870  */
7871  
7872 /**
7873  * @class Roo.bootstrap.TriggerField
7874  * @extends Roo.bootstrap.Input
7875  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7876  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7877  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7878  * for which you can provide a custom implementation.  For example:
7879  * <pre><code>
7880 var trigger = new Roo.bootstrap.TriggerField();
7881 trigger.onTriggerClick = myTriggerFn;
7882 trigger.applyTo('my-field');
7883 </code></pre>
7884  *
7885  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7886  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7887  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7888  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7889  * @constructor
7890  * Create a new TriggerField.
7891  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7892  * to the base TextField)
7893  */
7894 Roo.bootstrap.TriggerField = function(config){
7895     this.mimicing = false;
7896     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7897 };
7898
7899 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7900     /**
7901      * @cfg {String} triggerClass A CSS class to apply to the trigger
7902      */
7903      /**
7904      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7905      */
7906     hideTrigger:false,
7907
7908     /** @cfg {Boolean} grow @hide */
7909     /** @cfg {Number} growMin @hide */
7910     /** @cfg {Number} growMax @hide */
7911
7912     /**
7913      * @hide 
7914      * @method
7915      */
7916     autoSize: Roo.emptyFn,
7917     // private
7918     monitorTab : true,
7919     // private
7920     deferHeight : true,
7921
7922     
7923     actionMode : 'wrap',
7924     
7925     
7926     
7927     getAutoCreate : function(){
7928        
7929         var align = this.labelAlign || this.parentLabelAlign();
7930         
7931         var id = Roo.id();
7932         
7933         var cfg = {
7934             cls: 'form-group' //input-group
7935         };
7936         
7937         
7938         var input =  {
7939             tag: 'input',
7940             id : id,
7941             type : this.inputType,
7942             cls : 'form-control',
7943             autocomplete: 'false',
7944             placeholder : this.placeholder || '' 
7945             
7946         };
7947         if (this.name) {
7948             input.name = this.name;
7949         }
7950         if (this.size) {
7951             input.cls += ' input-' + this.size;
7952         }
7953         
7954         if (this.disabled) {
7955             input.disabled=true;
7956         }
7957         
7958         var inputblock = input;
7959         
7960         if (this.before || this.after) {
7961             
7962             inputblock = {
7963                 cls : 'input-group',
7964                 cn :  [] 
7965             };
7966             if (this.before) {
7967                 inputblock.cn.push({
7968                     tag :'span',
7969                     cls : 'input-group-addon',
7970                     html : this.before
7971                 });
7972             }
7973             inputblock.cn.push(input);
7974             if (this.after) {
7975                 inputblock.cn.push({
7976                     tag :'span',
7977                     cls : 'input-group-addon',
7978                     html : this.after
7979                 });
7980             }
7981             
7982         };
7983         
7984         var box = {
7985             tag: 'div',
7986             cn: [
7987                 {
7988                     tag: 'input',
7989                     type : 'hidden',
7990                     cls: 'form-hidden-field'
7991                 },
7992                 inputblock
7993             ]
7994             
7995         };
7996         
7997         if(this.multiple){
7998             Roo.log('multiple');
7999             
8000             box = {
8001                 tag: 'div',
8002                 cn: [
8003                     {
8004                         tag: 'input',
8005                         type : 'hidden',
8006                         cls: 'form-hidden-field'
8007                     },
8008                     {
8009                         tag: 'ul',
8010                         cls: 'select2-choices',
8011                         cn:[
8012                             {
8013                                 tag: 'li',
8014                                 cls: 'select2-search-field',
8015                                 cn: [
8016
8017                                     inputblock
8018                                 ]
8019                             }
8020                         ]
8021                     }
8022                 ]
8023             }
8024         };
8025         
8026         var combobox = {
8027             cls: 'select2-container input-group',
8028             cn: [
8029                 box
8030 //                {
8031 //                    tag: 'ul',
8032 //                    cls: 'typeahead typeahead-long dropdown-menu',
8033 //                    style: 'display:none'
8034 //                }
8035             ]
8036         };
8037         
8038         if(!this.multiple && this.showToggleBtn){
8039             combobox.cn.push({
8040                 tag :'span',
8041                 cls : 'input-group-addon btn dropdown-toggle',
8042                 cn : [
8043                     {
8044                         tag: 'span',
8045                         cls: 'caret'
8046                     },
8047                     {
8048                         tag: 'span',
8049                         cls: 'combobox-clear',
8050                         cn  : [
8051                             {
8052                                 tag : 'i',
8053                                 cls: 'icon-remove'
8054                             }
8055                         ]
8056                     }
8057                 ]
8058
8059             })
8060         }
8061         
8062         if(this.multiple){
8063             combobox.cls += ' select2-container-multi';
8064         }
8065         
8066         if (align ==='left' && this.fieldLabel.length) {
8067             
8068                 Roo.log("left and has label");
8069                 cfg.cn = [
8070                     
8071                     {
8072                         tag: 'label',
8073                         'for' :  id,
8074                         cls : 'control-label col-sm-' + this.labelWidth,
8075                         html : this.fieldLabel
8076                         
8077                     },
8078                     {
8079                         cls : "col-sm-" + (12 - this.labelWidth), 
8080                         cn: [
8081                             combobox
8082                         ]
8083                     }
8084                     
8085                 ];
8086         } else if ( this.fieldLabel.length) {
8087                 Roo.log(" label");
8088                  cfg.cn = [
8089                    
8090                     {
8091                         tag: 'label',
8092                         //cls : 'input-group-addon',
8093                         html : this.fieldLabel
8094                         
8095                     },
8096                     
8097                     combobox
8098                     
8099                 ];
8100
8101         } else {
8102             
8103                 Roo.log(" no label && no align");
8104                 cfg = combobox
8105                      
8106                 
8107         }
8108          
8109         var settings=this;
8110         ['xs','sm','md','lg'].map(function(size){
8111             if (settings[size]) {
8112                 cfg.cls += ' col-' + size + '-' + settings[size];
8113             }
8114         });
8115         
8116         return cfg;
8117         
8118     },
8119     
8120     
8121     
8122     // private
8123     onResize : function(w, h){
8124 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8125 //        if(typeof w == 'number'){
8126 //            var x = w - this.trigger.getWidth();
8127 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8128 //            this.trigger.setStyle('left', x+'px');
8129 //        }
8130     },
8131
8132     // private
8133     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8134
8135     // private
8136     getResizeEl : function(){
8137         return this.inputEl();
8138     },
8139
8140     // private
8141     getPositionEl : function(){
8142         return this.inputEl();
8143     },
8144
8145     // private
8146     alignErrorIcon : function(){
8147         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8148     },
8149
8150     // private
8151     initEvents : function(){
8152         
8153         this.createList();
8154         
8155         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8156         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8157         if(!this.multiple && this.showToggleBtn){
8158             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8159             if(this.hideTrigger){
8160                 this.trigger.setDisplayed(false);
8161             }
8162             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8163         }
8164         
8165         if(this.multiple){
8166             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8167         }
8168         
8169         //this.trigger.addClassOnOver('x-form-trigger-over');
8170         //this.trigger.addClassOnClick('x-form-trigger-click');
8171         
8172         //if(!this.width){
8173         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8174         //}
8175     },
8176     
8177     createList : function()
8178     {
8179         this.list = Roo.get(document.body).createChild({
8180             tag: 'ul',
8181             cls: 'typeahead typeahead-long dropdown-menu',
8182             style: 'display:none'
8183         });
8184         
8185         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8186         
8187     },
8188
8189     // private
8190     initTrigger : function(){
8191        
8192     },
8193
8194     // private
8195     onDestroy : function(){
8196         if(this.trigger){
8197             this.trigger.removeAllListeners();
8198           //  this.trigger.remove();
8199         }
8200         //if(this.wrap){
8201         //    this.wrap.remove();
8202         //}
8203         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8204     },
8205
8206     // private
8207     onFocus : function(){
8208         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8209         /*
8210         if(!this.mimicing){
8211             this.wrap.addClass('x-trigger-wrap-focus');
8212             this.mimicing = true;
8213             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8214             if(this.monitorTab){
8215                 this.el.on("keydown", this.checkTab, this);
8216             }
8217         }
8218         */
8219     },
8220
8221     // private
8222     checkTab : function(e){
8223         if(e.getKey() == e.TAB){
8224             this.triggerBlur();
8225         }
8226     },
8227
8228     // private
8229     onBlur : function(){
8230         // do nothing
8231     },
8232
8233     // private
8234     mimicBlur : function(e, t){
8235         /*
8236         if(!this.wrap.contains(t) && this.validateBlur()){
8237             this.triggerBlur();
8238         }
8239         */
8240     },
8241
8242     // private
8243     triggerBlur : function(){
8244         this.mimicing = false;
8245         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8246         if(this.monitorTab){
8247             this.el.un("keydown", this.checkTab, this);
8248         }
8249         //this.wrap.removeClass('x-trigger-wrap-focus');
8250         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8251     },
8252
8253     // private
8254     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8255     validateBlur : function(e, t){
8256         return true;
8257     },
8258
8259     // private
8260     onDisable : function(){
8261         this.inputEl().dom.disabled = true;
8262         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8263         //if(this.wrap){
8264         //    this.wrap.addClass('x-item-disabled');
8265         //}
8266     },
8267
8268     // private
8269     onEnable : function(){
8270         this.inputEl().dom.disabled = false;
8271         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8272         //if(this.wrap){
8273         //    this.el.removeClass('x-item-disabled');
8274         //}
8275     },
8276
8277     // private
8278     onShow : function(){
8279         var ae = this.getActionEl();
8280         
8281         if(ae){
8282             ae.dom.style.display = '';
8283             ae.dom.style.visibility = 'visible';
8284         }
8285     },
8286
8287     // private
8288     
8289     onHide : function(){
8290         var ae = this.getActionEl();
8291         ae.dom.style.display = 'none';
8292     },
8293
8294     /**
8295      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
8296      * by an implementing function.
8297      * @method
8298      * @param {EventObject} e
8299      */
8300     onTriggerClick : Roo.emptyFn
8301 });
8302  /*
8303  * Based on:
8304  * Ext JS Library 1.1.1
8305  * Copyright(c) 2006-2007, Ext JS, LLC.
8306  *
8307  * Originally Released Under LGPL - original licence link has changed is not relivant.
8308  *
8309  * Fork - LGPL
8310  * <script type="text/javascript">
8311  */
8312
8313
8314 /**
8315  * @class Roo.data.SortTypes
8316  * @singleton
8317  * Defines the default sorting (casting?) comparison functions used when sorting data.
8318  */
8319 Roo.data.SortTypes = {
8320     /**
8321      * Default sort that does nothing
8322      * @param {Mixed} s The value being converted
8323      * @return {Mixed} The comparison value
8324      */
8325     none : function(s){
8326         return s;
8327     },
8328     
8329     /**
8330      * The regular expression used to strip tags
8331      * @type {RegExp}
8332      * @property
8333      */
8334     stripTagsRE : /<\/?[^>]+>/gi,
8335     
8336     /**
8337      * Strips all HTML tags to sort on text only
8338      * @param {Mixed} s The value being converted
8339      * @return {String} The comparison value
8340      */
8341     asText : function(s){
8342         return String(s).replace(this.stripTagsRE, "");
8343     },
8344     
8345     /**
8346      * Strips all HTML tags to sort on text only - Case insensitive
8347      * @param {Mixed} s The value being converted
8348      * @return {String} The comparison value
8349      */
8350     asUCText : function(s){
8351         return String(s).toUpperCase().replace(this.stripTagsRE, "");
8352     },
8353     
8354     /**
8355      * Case insensitive string
8356      * @param {Mixed} s The value being converted
8357      * @return {String} The comparison value
8358      */
8359     asUCString : function(s) {
8360         return String(s).toUpperCase();
8361     },
8362     
8363     /**
8364      * Date sorting
8365      * @param {Mixed} s The value being converted
8366      * @return {Number} The comparison value
8367      */
8368     asDate : function(s) {
8369         if(!s){
8370             return 0;
8371         }
8372         if(s instanceof Date){
8373             return s.getTime();
8374         }
8375         return Date.parse(String(s));
8376     },
8377     
8378     /**
8379      * Float sorting
8380      * @param {Mixed} s The value being converted
8381      * @return {Float} The comparison value
8382      */
8383     asFloat : function(s) {
8384         var val = parseFloat(String(s).replace(/,/g, ""));
8385         if(isNaN(val)) val = 0;
8386         return val;
8387     },
8388     
8389     /**
8390      * Integer sorting
8391      * @param {Mixed} s The value being converted
8392      * @return {Number} The comparison value
8393      */
8394     asInt : function(s) {
8395         var val = parseInt(String(s).replace(/,/g, ""));
8396         if(isNaN(val)) val = 0;
8397         return val;
8398     }
8399 };/*
8400  * Based on:
8401  * Ext JS Library 1.1.1
8402  * Copyright(c) 2006-2007, Ext JS, LLC.
8403  *
8404  * Originally Released Under LGPL - original licence link has changed is not relivant.
8405  *
8406  * Fork - LGPL
8407  * <script type="text/javascript">
8408  */
8409
8410 /**
8411 * @class Roo.data.Record
8412  * Instances of this class encapsulate both record <em>definition</em> information, and record
8413  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8414  * to access Records cached in an {@link Roo.data.Store} object.<br>
8415  * <p>
8416  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8417  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8418  * objects.<br>
8419  * <p>
8420  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8421  * @constructor
8422  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8423  * {@link #create}. The parameters are the same.
8424  * @param {Array} data An associative Array of data values keyed by the field name.
8425  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8426  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8427  * not specified an integer id is generated.
8428  */
8429 Roo.data.Record = function(data, id){
8430     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8431     this.data = data;
8432 };
8433
8434 /**
8435  * Generate a constructor for a specific record layout.
8436  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8437  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8438  * Each field definition object may contain the following properties: <ul>
8439  * <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,
8440  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8441  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8442  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8443  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8444  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8445  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8446  * this may be omitted.</p></li>
8447  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8448  * <ul><li>auto (Default, implies no conversion)</li>
8449  * <li>string</li>
8450  * <li>int</li>
8451  * <li>float</li>
8452  * <li>boolean</li>
8453  * <li>date</li></ul></p></li>
8454  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8455  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8456  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8457  * by the Reader into an object that will be stored in the Record. It is passed the
8458  * following parameters:<ul>
8459  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8460  * </ul></p></li>
8461  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8462  * </ul>
8463  * <br>usage:<br><pre><code>
8464 var TopicRecord = Roo.data.Record.create(
8465     {name: 'title', mapping: 'topic_title'},
8466     {name: 'author', mapping: 'username'},
8467     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8468     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8469     {name: 'lastPoster', mapping: 'user2'},
8470     {name: 'excerpt', mapping: 'post_text'}
8471 );
8472
8473 var myNewRecord = new TopicRecord({
8474     title: 'Do my job please',
8475     author: 'noobie',
8476     totalPosts: 1,
8477     lastPost: new Date(),
8478     lastPoster: 'Animal',
8479     excerpt: 'No way dude!'
8480 });
8481 myStore.add(myNewRecord);
8482 </code></pre>
8483  * @method create
8484  * @static
8485  */
8486 Roo.data.Record.create = function(o){
8487     var f = function(){
8488         f.superclass.constructor.apply(this, arguments);
8489     };
8490     Roo.extend(f, Roo.data.Record);
8491     var p = f.prototype;
8492     p.fields = new Roo.util.MixedCollection(false, function(field){
8493         return field.name;
8494     });
8495     for(var i = 0, len = o.length; i < len; i++){
8496         p.fields.add(new Roo.data.Field(o[i]));
8497     }
8498     f.getField = function(name){
8499         return p.fields.get(name);  
8500     };
8501     return f;
8502 };
8503
8504 Roo.data.Record.AUTO_ID = 1000;
8505 Roo.data.Record.EDIT = 'edit';
8506 Roo.data.Record.REJECT = 'reject';
8507 Roo.data.Record.COMMIT = 'commit';
8508
8509 Roo.data.Record.prototype = {
8510     /**
8511      * Readonly flag - true if this record has been modified.
8512      * @type Boolean
8513      */
8514     dirty : false,
8515     editing : false,
8516     error: null,
8517     modified: null,
8518
8519     // private
8520     join : function(store){
8521         this.store = store;
8522     },
8523
8524     /**
8525      * Set the named field to the specified value.
8526      * @param {String} name The name of the field to set.
8527      * @param {Object} value The value to set the field to.
8528      */
8529     set : function(name, value){
8530         if(this.data[name] == value){
8531             return;
8532         }
8533         this.dirty = true;
8534         if(!this.modified){
8535             this.modified = {};
8536         }
8537         if(typeof this.modified[name] == 'undefined'){
8538             this.modified[name] = this.data[name];
8539         }
8540         this.data[name] = value;
8541         if(!this.editing && this.store){
8542             this.store.afterEdit(this);
8543         }       
8544     },
8545
8546     /**
8547      * Get the value of the named field.
8548      * @param {String} name The name of the field to get the value of.
8549      * @return {Object} The value of the field.
8550      */
8551     get : function(name){
8552         return this.data[name]; 
8553     },
8554
8555     // private
8556     beginEdit : function(){
8557         this.editing = true;
8558         this.modified = {}; 
8559     },
8560
8561     // private
8562     cancelEdit : function(){
8563         this.editing = false;
8564         delete this.modified;
8565     },
8566
8567     // private
8568     endEdit : function(){
8569         this.editing = false;
8570         if(this.dirty && this.store){
8571             this.store.afterEdit(this);
8572         }
8573     },
8574
8575     /**
8576      * Usually called by the {@link Roo.data.Store} which owns the Record.
8577      * Rejects all changes made to the Record since either creation, or the last commit operation.
8578      * Modified fields are reverted to their original values.
8579      * <p>
8580      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8581      * of reject operations.
8582      */
8583     reject : function(){
8584         var m = this.modified;
8585         for(var n in m){
8586             if(typeof m[n] != "function"){
8587                 this.data[n] = m[n];
8588             }
8589         }
8590         this.dirty = false;
8591         delete this.modified;
8592         this.editing = false;
8593         if(this.store){
8594             this.store.afterReject(this);
8595         }
8596     },
8597
8598     /**
8599      * Usually called by the {@link Roo.data.Store} which owns the Record.
8600      * Commits all changes made to the Record since either creation, or the last commit operation.
8601      * <p>
8602      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8603      * of commit operations.
8604      */
8605     commit : function(){
8606         this.dirty = false;
8607         delete this.modified;
8608         this.editing = false;
8609         if(this.store){
8610             this.store.afterCommit(this);
8611         }
8612     },
8613
8614     // private
8615     hasError : function(){
8616         return this.error != null;
8617     },
8618
8619     // private
8620     clearError : function(){
8621         this.error = null;
8622     },
8623
8624     /**
8625      * Creates a copy of this record.
8626      * @param {String} id (optional) A new record id if you don't want to use this record's id
8627      * @return {Record}
8628      */
8629     copy : function(newId) {
8630         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8631     }
8632 };/*
8633  * Based on:
8634  * Ext JS Library 1.1.1
8635  * Copyright(c) 2006-2007, Ext JS, LLC.
8636  *
8637  * Originally Released Under LGPL - original licence link has changed is not relivant.
8638  *
8639  * Fork - LGPL
8640  * <script type="text/javascript">
8641  */
8642
8643
8644
8645 /**
8646  * @class Roo.data.Store
8647  * @extends Roo.util.Observable
8648  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8649  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8650  * <p>
8651  * 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
8652  * has no knowledge of the format of the data returned by the Proxy.<br>
8653  * <p>
8654  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8655  * instances from the data object. These records are cached and made available through accessor functions.
8656  * @constructor
8657  * Creates a new Store.
8658  * @param {Object} config A config object containing the objects needed for the Store to access data,
8659  * and read the data into Records.
8660  */
8661 Roo.data.Store = function(config){
8662     this.data = new Roo.util.MixedCollection(false);
8663     this.data.getKey = function(o){
8664         return o.id;
8665     };
8666     this.baseParams = {};
8667     // private
8668     this.paramNames = {
8669         "start" : "start",
8670         "limit" : "limit",
8671         "sort" : "sort",
8672         "dir" : "dir",
8673         "multisort" : "_multisort"
8674     };
8675
8676     if(config && config.data){
8677         this.inlineData = config.data;
8678         delete config.data;
8679     }
8680
8681     Roo.apply(this, config);
8682     
8683     if(this.reader){ // reader passed
8684         this.reader = Roo.factory(this.reader, Roo.data);
8685         this.reader.xmodule = this.xmodule || false;
8686         if(!this.recordType){
8687             this.recordType = this.reader.recordType;
8688         }
8689         if(this.reader.onMetaChange){
8690             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8691         }
8692     }
8693
8694     if(this.recordType){
8695         this.fields = this.recordType.prototype.fields;
8696     }
8697     this.modified = [];
8698
8699     this.addEvents({
8700         /**
8701          * @event datachanged
8702          * Fires when the data cache has changed, and a widget which is using this Store
8703          * as a Record cache should refresh its view.
8704          * @param {Store} this
8705          */
8706         datachanged : true,
8707         /**
8708          * @event metachange
8709          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8710          * @param {Store} this
8711          * @param {Object} meta The JSON metadata
8712          */
8713         metachange : true,
8714         /**
8715          * @event add
8716          * Fires when Records have been added to the Store
8717          * @param {Store} this
8718          * @param {Roo.data.Record[]} records The array of Records added
8719          * @param {Number} index The index at which the record(s) were added
8720          */
8721         add : true,
8722         /**
8723          * @event remove
8724          * Fires when a Record has been removed from the Store
8725          * @param {Store} this
8726          * @param {Roo.data.Record} record The Record that was removed
8727          * @param {Number} index The index at which the record was removed
8728          */
8729         remove : true,
8730         /**
8731          * @event update
8732          * Fires when a Record has been updated
8733          * @param {Store} this
8734          * @param {Roo.data.Record} record The Record that was updated
8735          * @param {String} operation The update operation being performed.  Value may be one of:
8736          * <pre><code>
8737  Roo.data.Record.EDIT
8738  Roo.data.Record.REJECT
8739  Roo.data.Record.COMMIT
8740          * </code></pre>
8741          */
8742         update : true,
8743         /**
8744          * @event clear
8745          * Fires when the data cache has been cleared.
8746          * @param {Store} this
8747          */
8748         clear : true,
8749         /**
8750          * @event beforeload
8751          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8752          * the load action will be canceled.
8753          * @param {Store} this
8754          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8755          */
8756         beforeload : true,
8757         /**
8758          * @event beforeloadadd
8759          * Fires after a new set of Records has been loaded.
8760          * @param {Store} this
8761          * @param {Roo.data.Record[]} records The Records that were loaded
8762          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8763          */
8764         beforeloadadd : true,
8765         /**
8766          * @event load
8767          * Fires after a new set of Records has been loaded, before they are added to the store.
8768          * @param {Store} this
8769          * @param {Roo.data.Record[]} records The Records that were loaded
8770          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8771          * @params {Object} return from reader
8772          */
8773         load : true,
8774         /**
8775          * @event loadexception
8776          * Fires if an exception occurs in the Proxy during loading.
8777          * Called with the signature of the Proxy's "loadexception" event.
8778          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8779          * 
8780          * @param {Proxy} 
8781          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8782          * @param {Object} load options 
8783          * @param {Object} jsonData from your request (normally this contains the Exception)
8784          */
8785         loadexception : true
8786     });
8787     
8788     if(this.proxy){
8789         this.proxy = Roo.factory(this.proxy, Roo.data);
8790         this.proxy.xmodule = this.xmodule || false;
8791         this.relayEvents(this.proxy,  ["loadexception"]);
8792     }
8793     this.sortToggle = {};
8794     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8795
8796     Roo.data.Store.superclass.constructor.call(this);
8797
8798     if(this.inlineData){
8799         this.loadData(this.inlineData);
8800         delete this.inlineData;
8801     }
8802 };
8803
8804 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8805      /**
8806     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8807     * without a remote query - used by combo/forms at present.
8808     */
8809     
8810     /**
8811     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8812     */
8813     /**
8814     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8815     */
8816     /**
8817     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8818     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8819     */
8820     /**
8821     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8822     * on any HTTP request
8823     */
8824     /**
8825     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8826     */
8827     /**
8828     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8829     */
8830     multiSort: false,
8831     /**
8832     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8833     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8834     */
8835     remoteSort : false,
8836
8837     /**
8838     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8839      * loaded or when a record is removed. (defaults to false).
8840     */
8841     pruneModifiedRecords : false,
8842
8843     // private
8844     lastOptions : null,
8845
8846     /**
8847      * Add Records to the Store and fires the add event.
8848      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8849      */
8850     add : function(records){
8851         records = [].concat(records);
8852         for(var i = 0, len = records.length; i < len; i++){
8853             records[i].join(this);
8854         }
8855         var index = this.data.length;
8856         this.data.addAll(records);
8857         this.fireEvent("add", this, records, index);
8858     },
8859
8860     /**
8861      * Remove a Record from the Store and fires the remove event.
8862      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8863      */
8864     remove : function(record){
8865         var index = this.data.indexOf(record);
8866         this.data.removeAt(index);
8867         if(this.pruneModifiedRecords){
8868             this.modified.remove(record);
8869         }
8870         this.fireEvent("remove", this, record, index);
8871     },
8872
8873     /**
8874      * Remove all Records from the Store and fires the clear event.
8875      */
8876     removeAll : function(){
8877         this.data.clear();
8878         if(this.pruneModifiedRecords){
8879             this.modified = [];
8880         }
8881         this.fireEvent("clear", this);
8882     },
8883
8884     /**
8885      * Inserts Records to the Store at the given index and fires the add event.
8886      * @param {Number} index The start index at which to insert the passed Records.
8887      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8888      */
8889     insert : function(index, records){
8890         records = [].concat(records);
8891         for(var i = 0, len = records.length; i < len; i++){
8892             this.data.insert(index, records[i]);
8893             records[i].join(this);
8894         }
8895         this.fireEvent("add", this, records, index);
8896     },
8897
8898     /**
8899      * Get the index within the cache of the passed Record.
8900      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8901      * @return {Number} The index of the passed Record. Returns -1 if not found.
8902      */
8903     indexOf : function(record){
8904         return this.data.indexOf(record);
8905     },
8906
8907     /**
8908      * Get the index within the cache of the Record with the passed id.
8909      * @param {String} id The id of the Record to find.
8910      * @return {Number} The index of the Record. Returns -1 if not found.
8911      */
8912     indexOfId : function(id){
8913         return this.data.indexOfKey(id);
8914     },
8915
8916     /**
8917      * Get the Record with the specified id.
8918      * @param {String} id The id of the Record to find.
8919      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8920      */
8921     getById : function(id){
8922         return this.data.key(id);
8923     },
8924
8925     /**
8926      * Get the Record at the specified index.
8927      * @param {Number} index The index of the Record to find.
8928      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8929      */
8930     getAt : function(index){
8931         return this.data.itemAt(index);
8932     },
8933
8934     /**
8935      * Returns a range of Records between specified indices.
8936      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8937      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8938      * @return {Roo.data.Record[]} An array of Records
8939      */
8940     getRange : function(start, end){
8941         return this.data.getRange(start, end);
8942     },
8943
8944     // private
8945     storeOptions : function(o){
8946         o = Roo.apply({}, o);
8947         delete o.callback;
8948         delete o.scope;
8949         this.lastOptions = o;
8950     },
8951
8952     /**
8953      * Loads the Record cache from the configured Proxy using the configured Reader.
8954      * <p>
8955      * If using remote paging, then the first load call must specify the <em>start</em>
8956      * and <em>limit</em> properties in the options.params property to establish the initial
8957      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8958      * <p>
8959      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8960      * and this call will return before the new data has been loaded. Perform any post-processing
8961      * in a callback function, or in a "load" event handler.</strong>
8962      * <p>
8963      * @param {Object} options An object containing properties which control loading options:<ul>
8964      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8965      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8966      * passed the following arguments:<ul>
8967      * <li>r : Roo.data.Record[]</li>
8968      * <li>options: Options object from the load call</li>
8969      * <li>success: Boolean success indicator</li></ul></li>
8970      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8971      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8972      * </ul>
8973      */
8974     load : function(options){
8975         options = options || {};
8976         if(this.fireEvent("beforeload", this, options) !== false){
8977             this.storeOptions(options);
8978             var p = Roo.apply(options.params || {}, this.baseParams);
8979             // if meta was not loaded from remote source.. try requesting it.
8980             if (!this.reader.metaFromRemote) {
8981                 p._requestMeta = 1;
8982             }
8983             if(this.sortInfo && this.remoteSort){
8984                 var pn = this.paramNames;
8985                 p[pn["sort"]] = this.sortInfo.field;
8986                 p[pn["dir"]] = this.sortInfo.direction;
8987             }
8988             if (this.multiSort) {
8989                 var pn = this.paramNames;
8990                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8991             }
8992             
8993             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8994         }
8995     },
8996
8997     /**
8998      * Reloads the Record cache from the configured Proxy using the configured Reader and
8999      * the options from the last load operation performed.
9000      * @param {Object} options (optional) An object containing properties which may override the options
9001      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9002      * the most recently used options are reused).
9003      */
9004     reload : function(options){
9005         this.load(Roo.applyIf(options||{}, this.lastOptions));
9006     },
9007
9008     // private
9009     // Called as a callback by the Reader during a load operation.
9010     loadRecords : function(o, options, success){
9011         if(!o || success === false){
9012             if(success !== false){
9013                 this.fireEvent("load", this, [], options, o);
9014             }
9015             if(options.callback){
9016                 options.callback.call(options.scope || this, [], options, false);
9017             }
9018             return;
9019         }
9020         // if data returned failure - throw an exception.
9021         if (o.success === false) {
9022             // show a message if no listener is registered.
9023             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9024                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9025             }
9026             // loadmask wil be hooked into this..
9027             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9028             return;
9029         }
9030         var r = o.records, t = o.totalRecords || r.length;
9031         
9032         this.fireEvent("beforeloadadd", this, r, options, o);
9033         
9034         if(!options || options.add !== true){
9035             if(this.pruneModifiedRecords){
9036                 this.modified = [];
9037             }
9038             for(var i = 0, len = r.length; i < len; i++){
9039                 r[i].join(this);
9040             }
9041             if(this.snapshot){
9042                 this.data = this.snapshot;
9043                 delete this.snapshot;
9044             }
9045             this.data.clear();
9046             this.data.addAll(r);
9047             this.totalLength = t;
9048             this.applySort();
9049             this.fireEvent("datachanged", this);
9050         }else{
9051             this.totalLength = Math.max(t, this.data.length+r.length);
9052             this.add(r);
9053         }
9054         this.fireEvent("load", this, r, options, o);
9055         if(options.callback){
9056             options.callback.call(options.scope || this, r, options, true);
9057         }
9058     },
9059
9060
9061     /**
9062      * Loads data from a passed data block. A Reader which understands the format of the data
9063      * must have been configured in the constructor.
9064      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9065      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9066      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9067      */
9068     loadData : function(o, append){
9069         var r = this.reader.readRecords(o);
9070         this.loadRecords(r, {add: append}, true);
9071     },
9072
9073     /**
9074      * Gets the number of cached records.
9075      * <p>
9076      * <em>If using paging, this may not be the total size of the dataset. If the data object
9077      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9078      * the data set size</em>
9079      */
9080     getCount : function(){
9081         return this.data.length || 0;
9082     },
9083
9084     /**
9085      * Gets the total number of records in the dataset as returned by the server.
9086      * <p>
9087      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9088      * the dataset size</em>
9089      */
9090     getTotalCount : function(){
9091         return this.totalLength || 0;
9092     },
9093
9094     /**
9095      * Returns the sort state of the Store as an object with two properties:
9096      * <pre><code>
9097  field {String} The name of the field by which the Records are sorted
9098  direction {String} The sort order, "ASC" or "DESC"
9099      * </code></pre>
9100      */
9101     getSortState : function(){
9102         return this.sortInfo;
9103     },
9104
9105     // private
9106     applySort : function(){
9107         if(this.sortInfo && !this.remoteSort){
9108             var s = this.sortInfo, f = s.field;
9109             var st = this.fields.get(f).sortType;
9110             var fn = function(r1, r2){
9111                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9112                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9113             };
9114             this.data.sort(s.direction, fn);
9115             if(this.snapshot && this.snapshot != this.data){
9116                 this.snapshot.sort(s.direction, fn);
9117             }
9118         }
9119     },
9120
9121     /**
9122      * Sets the default sort column and order to be used by the next load operation.
9123      * @param {String} fieldName The name of the field to sort by.
9124      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9125      */
9126     setDefaultSort : function(field, dir){
9127         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9128     },
9129
9130     /**
9131      * Sort the Records.
9132      * If remote sorting is used, the sort is performed on the server, and the cache is
9133      * reloaded. If local sorting is used, the cache is sorted internally.
9134      * @param {String} fieldName The name of the field to sort by.
9135      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9136      */
9137     sort : function(fieldName, dir){
9138         var f = this.fields.get(fieldName);
9139         if(!dir){
9140             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9141             
9142             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9143                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9144             }else{
9145                 dir = f.sortDir;
9146             }
9147         }
9148         this.sortToggle[f.name] = dir;
9149         this.sortInfo = {field: f.name, direction: dir};
9150         if(!this.remoteSort){
9151             this.applySort();
9152             this.fireEvent("datachanged", this);
9153         }else{
9154             this.load(this.lastOptions);
9155         }
9156     },
9157
9158     /**
9159      * Calls the specified function for each of the Records in the cache.
9160      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9161      * Returning <em>false</em> aborts and exits the iteration.
9162      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9163      */
9164     each : function(fn, scope){
9165         this.data.each(fn, scope);
9166     },
9167
9168     /**
9169      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9170      * (e.g., during paging).
9171      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9172      */
9173     getModifiedRecords : function(){
9174         return this.modified;
9175     },
9176
9177     // private
9178     createFilterFn : function(property, value, anyMatch){
9179         if(!value.exec){ // not a regex
9180             value = String(value);
9181             if(value.length == 0){
9182                 return false;
9183             }
9184             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9185         }
9186         return function(r){
9187             return value.test(r.data[property]);
9188         };
9189     },
9190
9191     /**
9192      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9193      * @param {String} property A field on your records
9194      * @param {Number} start The record index to start at (defaults to 0)
9195      * @param {Number} end The last record index to include (defaults to length - 1)
9196      * @return {Number} The sum
9197      */
9198     sum : function(property, start, end){
9199         var rs = this.data.items, v = 0;
9200         start = start || 0;
9201         end = (end || end === 0) ? end : rs.length-1;
9202
9203         for(var i = start; i <= end; i++){
9204             v += (rs[i].data[property] || 0);
9205         }
9206         return v;
9207     },
9208
9209     /**
9210      * Filter the records by a specified property.
9211      * @param {String} field A field on your records
9212      * @param {String/RegExp} value Either a string that the field
9213      * should start with or a RegExp to test against the field
9214      * @param {Boolean} anyMatch True to match any part not just the beginning
9215      */
9216     filter : function(property, value, anyMatch){
9217         var fn = this.createFilterFn(property, value, anyMatch);
9218         return fn ? this.filterBy(fn) : this.clearFilter();
9219     },
9220
9221     /**
9222      * Filter by a function. The specified function will be called with each
9223      * record in this data source. If the function returns true the record is included,
9224      * otherwise it is filtered.
9225      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9226      * @param {Object} scope (optional) The scope of the function (defaults to this)
9227      */
9228     filterBy : function(fn, scope){
9229         this.snapshot = this.snapshot || this.data;
9230         this.data = this.queryBy(fn, scope||this);
9231         this.fireEvent("datachanged", this);
9232     },
9233
9234     /**
9235      * Query the records by a specified property.
9236      * @param {String} field A field on your records
9237      * @param {String/RegExp} value Either a string that the field
9238      * should start with or a RegExp to test against the field
9239      * @param {Boolean} anyMatch True to match any part not just the beginning
9240      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9241      */
9242     query : function(property, value, anyMatch){
9243         var fn = this.createFilterFn(property, value, anyMatch);
9244         return fn ? this.queryBy(fn) : this.data.clone();
9245     },
9246
9247     /**
9248      * Query by a function. The specified function will be called with each
9249      * record in this data source. If the function returns true the record is included
9250      * in the results.
9251      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9252      * @param {Object} scope (optional) The scope of the function (defaults to this)
9253       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9254      **/
9255     queryBy : function(fn, scope){
9256         var data = this.snapshot || this.data;
9257         return data.filterBy(fn, scope||this);
9258     },
9259
9260     /**
9261      * Collects unique values for a particular dataIndex from this store.
9262      * @param {String} dataIndex The property to collect
9263      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9264      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9265      * @return {Array} An array of the unique values
9266      **/
9267     collect : function(dataIndex, allowNull, bypassFilter){
9268         var d = (bypassFilter === true && this.snapshot) ?
9269                 this.snapshot.items : this.data.items;
9270         var v, sv, r = [], l = {};
9271         for(var i = 0, len = d.length; i < len; i++){
9272             v = d[i].data[dataIndex];
9273             sv = String(v);
9274             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9275                 l[sv] = true;
9276                 r[r.length] = v;
9277             }
9278         }
9279         return r;
9280     },
9281
9282     /**
9283      * Revert to a view of the Record cache with no filtering applied.
9284      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9285      */
9286     clearFilter : function(suppressEvent){
9287         if(this.snapshot && this.snapshot != this.data){
9288             this.data = this.snapshot;
9289             delete this.snapshot;
9290             if(suppressEvent !== true){
9291                 this.fireEvent("datachanged", this);
9292             }
9293         }
9294     },
9295
9296     // private
9297     afterEdit : function(record){
9298         if(this.modified.indexOf(record) == -1){
9299             this.modified.push(record);
9300         }
9301         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9302     },
9303     
9304     // private
9305     afterReject : function(record){
9306         this.modified.remove(record);
9307         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9308     },
9309
9310     // private
9311     afterCommit : function(record){
9312         this.modified.remove(record);
9313         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9314     },
9315
9316     /**
9317      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9318      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9319      */
9320     commitChanges : function(){
9321         var m = this.modified.slice(0);
9322         this.modified = [];
9323         for(var i = 0, len = m.length; i < len; i++){
9324             m[i].commit();
9325         }
9326     },
9327
9328     /**
9329      * Cancel outstanding changes on all changed records.
9330      */
9331     rejectChanges : function(){
9332         var m = this.modified.slice(0);
9333         this.modified = [];
9334         for(var i = 0, len = m.length; i < len; i++){
9335             m[i].reject();
9336         }
9337     },
9338
9339     onMetaChange : function(meta, rtype, o){
9340         this.recordType = rtype;
9341         this.fields = rtype.prototype.fields;
9342         delete this.snapshot;
9343         this.sortInfo = meta.sortInfo || this.sortInfo;
9344         this.modified = [];
9345         this.fireEvent('metachange', this, this.reader.meta);
9346     },
9347     
9348     moveIndex : function(data, type)
9349     {
9350         var index = this.indexOf(data);
9351         
9352         var newIndex = index + type;
9353         
9354         this.remove(data);
9355         
9356         this.insert(newIndex, data);
9357         
9358     }
9359 });/*
9360  * Based on:
9361  * Ext JS Library 1.1.1
9362  * Copyright(c) 2006-2007, Ext JS, LLC.
9363  *
9364  * Originally Released Under LGPL - original licence link has changed is not relivant.
9365  *
9366  * Fork - LGPL
9367  * <script type="text/javascript">
9368  */
9369
9370 /**
9371  * @class Roo.data.SimpleStore
9372  * @extends Roo.data.Store
9373  * Small helper class to make creating Stores from Array data easier.
9374  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9375  * @cfg {Array} fields An array of field definition objects, or field name strings.
9376  * @cfg {Array} data The multi-dimensional array of data
9377  * @constructor
9378  * @param {Object} config
9379  */
9380 Roo.data.SimpleStore = function(config){
9381     Roo.data.SimpleStore.superclass.constructor.call(this, {
9382         isLocal : true,
9383         reader: new Roo.data.ArrayReader({
9384                 id: config.id
9385             },
9386             Roo.data.Record.create(config.fields)
9387         ),
9388         proxy : new Roo.data.MemoryProxy(config.data)
9389     });
9390     this.load();
9391 };
9392 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9393  * Based on:
9394  * Ext JS Library 1.1.1
9395  * Copyright(c) 2006-2007, Ext JS, LLC.
9396  *
9397  * Originally Released Under LGPL - original licence link has changed is not relivant.
9398  *
9399  * Fork - LGPL
9400  * <script type="text/javascript">
9401  */
9402
9403 /**
9404 /**
9405  * @extends Roo.data.Store
9406  * @class Roo.data.JsonStore
9407  * Small helper class to make creating Stores for JSON data easier. <br/>
9408 <pre><code>
9409 var store = new Roo.data.JsonStore({
9410     url: 'get-images.php',
9411     root: 'images',
9412     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9413 });
9414 </code></pre>
9415  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9416  * JsonReader and HttpProxy (unless inline data is provided).</b>
9417  * @cfg {Array} fields An array of field definition objects, or field name strings.
9418  * @constructor
9419  * @param {Object} config
9420  */
9421 Roo.data.JsonStore = function(c){
9422     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9423         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9424         reader: new Roo.data.JsonReader(c, c.fields)
9425     }));
9426 };
9427 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9428  * Based on:
9429  * Ext JS Library 1.1.1
9430  * Copyright(c) 2006-2007, Ext JS, LLC.
9431  *
9432  * Originally Released Under LGPL - original licence link has changed is not relivant.
9433  *
9434  * Fork - LGPL
9435  * <script type="text/javascript">
9436  */
9437
9438  
9439 Roo.data.Field = function(config){
9440     if(typeof config == "string"){
9441         config = {name: config};
9442     }
9443     Roo.apply(this, config);
9444     
9445     if(!this.type){
9446         this.type = "auto";
9447     }
9448     
9449     var st = Roo.data.SortTypes;
9450     // named sortTypes are supported, here we look them up
9451     if(typeof this.sortType == "string"){
9452         this.sortType = st[this.sortType];
9453     }
9454     
9455     // set default sortType for strings and dates
9456     if(!this.sortType){
9457         switch(this.type){
9458             case "string":
9459                 this.sortType = st.asUCString;
9460                 break;
9461             case "date":
9462                 this.sortType = st.asDate;
9463                 break;
9464             default:
9465                 this.sortType = st.none;
9466         }
9467     }
9468
9469     // define once
9470     var stripRe = /[\$,%]/g;
9471
9472     // prebuilt conversion function for this field, instead of
9473     // switching every time we're reading a value
9474     if(!this.convert){
9475         var cv, dateFormat = this.dateFormat;
9476         switch(this.type){
9477             case "":
9478             case "auto":
9479             case undefined:
9480                 cv = function(v){ return v; };
9481                 break;
9482             case "string":
9483                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9484                 break;
9485             case "int":
9486                 cv = function(v){
9487                     return v !== undefined && v !== null && v !== '' ?
9488                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9489                     };
9490                 break;
9491             case "float":
9492                 cv = function(v){
9493                     return v !== undefined && v !== null && v !== '' ?
9494                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9495                     };
9496                 break;
9497             case "bool":
9498             case "boolean":
9499                 cv = function(v){ return v === true || v === "true" || v == 1; };
9500                 break;
9501             case "date":
9502                 cv = function(v){
9503                     if(!v){
9504                         return '';
9505                     }
9506                     if(v instanceof Date){
9507                         return v;
9508                     }
9509                     if(dateFormat){
9510                         if(dateFormat == "timestamp"){
9511                             return new Date(v*1000);
9512                         }
9513                         return Date.parseDate(v, dateFormat);
9514                     }
9515                     var parsed = Date.parse(v);
9516                     return parsed ? new Date(parsed) : null;
9517                 };
9518              break;
9519             
9520         }
9521         this.convert = cv;
9522     }
9523 };
9524
9525 Roo.data.Field.prototype = {
9526     dateFormat: null,
9527     defaultValue: "",
9528     mapping: null,
9529     sortType : null,
9530     sortDir : "ASC"
9531 };/*
9532  * Based on:
9533  * Ext JS Library 1.1.1
9534  * Copyright(c) 2006-2007, Ext JS, LLC.
9535  *
9536  * Originally Released Under LGPL - original licence link has changed is not relivant.
9537  *
9538  * Fork - LGPL
9539  * <script type="text/javascript">
9540  */
9541  
9542 // Base class for reading structured data from a data source.  This class is intended to be
9543 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9544
9545 /**
9546  * @class Roo.data.DataReader
9547  * Base class for reading structured data from a data source.  This class is intended to be
9548  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9549  */
9550
9551 Roo.data.DataReader = function(meta, recordType){
9552     
9553     this.meta = meta;
9554     
9555     this.recordType = recordType instanceof Array ? 
9556         Roo.data.Record.create(recordType) : recordType;
9557 };
9558
9559 Roo.data.DataReader.prototype = {
9560      /**
9561      * Create an empty record
9562      * @param {Object} data (optional) - overlay some values
9563      * @return {Roo.data.Record} record created.
9564      */
9565     newRow :  function(d) {
9566         var da =  {};
9567         this.recordType.prototype.fields.each(function(c) {
9568             switch( c.type) {
9569                 case 'int' : da[c.name] = 0; break;
9570                 case 'date' : da[c.name] = new Date(); break;
9571                 case 'float' : da[c.name] = 0.0; break;
9572                 case 'boolean' : da[c.name] = false; break;
9573                 default : da[c.name] = ""; break;
9574             }
9575             
9576         });
9577         return new this.recordType(Roo.apply(da, d));
9578     }
9579     
9580 };/*
9581  * Based on:
9582  * Ext JS Library 1.1.1
9583  * Copyright(c) 2006-2007, Ext JS, LLC.
9584  *
9585  * Originally Released Under LGPL - original licence link has changed is not relivant.
9586  *
9587  * Fork - LGPL
9588  * <script type="text/javascript">
9589  */
9590
9591 /**
9592  * @class Roo.data.DataProxy
9593  * @extends Roo.data.Observable
9594  * This class is an abstract base class for implementations which provide retrieval of
9595  * unformatted data objects.<br>
9596  * <p>
9597  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9598  * (of the appropriate type which knows how to parse the data object) to provide a block of
9599  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9600  * <p>
9601  * Custom implementations must implement the load method as described in
9602  * {@link Roo.data.HttpProxy#load}.
9603  */
9604 Roo.data.DataProxy = function(){
9605     this.addEvents({
9606         /**
9607          * @event beforeload
9608          * Fires before a network request is made to retrieve a data object.
9609          * @param {Object} This DataProxy object.
9610          * @param {Object} params The params parameter to the load function.
9611          */
9612         beforeload : true,
9613         /**
9614          * @event load
9615          * Fires before the load method's callback is called.
9616          * @param {Object} This DataProxy object.
9617          * @param {Object} o The data object.
9618          * @param {Object} arg The callback argument object passed to the load function.
9619          */
9620         load : true,
9621         /**
9622          * @event loadexception
9623          * Fires if an Exception occurs during data retrieval.
9624          * @param {Object} This DataProxy object.
9625          * @param {Object} o The data object.
9626          * @param {Object} arg The callback argument object passed to the load function.
9627          * @param {Object} e The Exception.
9628          */
9629         loadexception : true
9630     });
9631     Roo.data.DataProxy.superclass.constructor.call(this);
9632 };
9633
9634 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9635
9636     /**
9637      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9638      */
9639 /*
9640  * Based on:
9641  * Ext JS Library 1.1.1
9642  * Copyright(c) 2006-2007, Ext JS, LLC.
9643  *
9644  * Originally Released Under LGPL - original licence link has changed is not relivant.
9645  *
9646  * Fork - LGPL
9647  * <script type="text/javascript">
9648  */
9649 /**
9650  * @class Roo.data.MemoryProxy
9651  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9652  * to the Reader when its load method is called.
9653  * @constructor
9654  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9655  */
9656 Roo.data.MemoryProxy = function(data){
9657     if (data.data) {
9658         data = data.data;
9659     }
9660     Roo.data.MemoryProxy.superclass.constructor.call(this);
9661     this.data = data;
9662 };
9663
9664 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9665     /**
9666      * Load data from the requested source (in this case an in-memory
9667      * data object passed to the constructor), read the data object into
9668      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9669      * process that block using the passed callback.
9670      * @param {Object} params This parameter is not used by the MemoryProxy class.
9671      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9672      * object into a block of Roo.data.Records.
9673      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9674      * The function must be passed <ul>
9675      * <li>The Record block object</li>
9676      * <li>The "arg" argument from the load function</li>
9677      * <li>A boolean success indicator</li>
9678      * </ul>
9679      * @param {Object} scope The scope in which to call the callback
9680      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9681      */
9682     load : function(params, reader, callback, scope, arg){
9683         params = params || {};
9684         var result;
9685         try {
9686             result = reader.readRecords(this.data);
9687         }catch(e){
9688             this.fireEvent("loadexception", this, arg, null, e);
9689             callback.call(scope, null, arg, false);
9690             return;
9691         }
9692         callback.call(scope, result, arg, true);
9693     },
9694     
9695     // private
9696     update : function(params, records){
9697         
9698     }
9699 });/*
9700  * Based on:
9701  * Ext JS Library 1.1.1
9702  * Copyright(c) 2006-2007, Ext JS, LLC.
9703  *
9704  * Originally Released Under LGPL - original licence link has changed is not relivant.
9705  *
9706  * Fork - LGPL
9707  * <script type="text/javascript">
9708  */
9709 /**
9710  * @class Roo.data.HttpProxy
9711  * @extends Roo.data.DataProxy
9712  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9713  * configured to reference a certain URL.<br><br>
9714  * <p>
9715  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9716  * from which the running page was served.<br><br>
9717  * <p>
9718  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9719  * <p>
9720  * Be aware that to enable the browser to parse an XML document, the server must set
9721  * the Content-Type header in the HTTP response to "text/xml".
9722  * @constructor
9723  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9724  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9725  * will be used to make the request.
9726  */
9727 Roo.data.HttpProxy = function(conn){
9728     Roo.data.HttpProxy.superclass.constructor.call(this);
9729     // is conn a conn config or a real conn?
9730     this.conn = conn;
9731     this.useAjax = !conn || !conn.events;
9732   
9733 };
9734
9735 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9736     // thse are take from connection...
9737     
9738     /**
9739      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9740      */
9741     /**
9742      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9743      * extra parameters to each request made by this object. (defaults to undefined)
9744      */
9745     /**
9746      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9747      *  to each request made by this object. (defaults to undefined)
9748      */
9749     /**
9750      * @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)
9751      */
9752     /**
9753      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9754      */
9755      /**
9756      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9757      * @type Boolean
9758      */
9759   
9760
9761     /**
9762      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9763      * @type Boolean
9764      */
9765     /**
9766      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9767      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9768      * a finer-grained basis than the DataProxy events.
9769      */
9770     getConnection : function(){
9771         return this.useAjax ? Roo.Ajax : this.conn;
9772     },
9773
9774     /**
9775      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9776      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9777      * process that block using the passed callback.
9778      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9779      * for the request to the remote server.
9780      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9781      * object into a block of Roo.data.Records.
9782      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9783      * The function must be passed <ul>
9784      * <li>The Record block object</li>
9785      * <li>The "arg" argument from the load function</li>
9786      * <li>A boolean success indicator</li>
9787      * </ul>
9788      * @param {Object} scope The scope in which to call the callback
9789      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9790      */
9791     load : function(params, reader, callback, scope, arg){
9792         if(this.fireEvent("beforeload", this, params) !== false){
9793             var  o = {
9794                 params : params || {},
9795                 request: {
9796                     callback : callback,
9797                     scope : scope,
9798                     arg : arg
9799                 },
9800                 reader: reader,
9801                 callback : this.loadResponse,
9802                 scope: this
9803             };
9804             if(this.useAjax){
9805                 Roo.applyIf(o, this.conn);
9806                 if(this.activeRequest){
9807                     Roo.Ajax.abort(this.activeRequest);
9808                 }
9809                 this.activeRequest = Roo.Ajax.request(o);
9810             }else{
9811                 this.conn.request(o);
9812             }
9813         }else{
9814             callback.call(scope||this, null, arg, false);
9815         }
9816     },
9817
9818     // private
9819     loadResponse : function(o, success, response){
9820         delete this.activeRequest;
9821         if(!success){
9822             this.fireEvent("loadexception", this, o, response);
9823             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9824             return;
9825         }
9826         var result;
9827         try {
9828             result = o.reader.read(response);
9829         }catch(e){
9830             this.fireEvent("loadexception", this, o, response, e);
9831             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9832             return;
9833         }
9834         
9835         this.fireEvent("load", this, o, o.request.arg);
9836         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9837     },
9838
9839     // private
9840     update : function(dataSet){
9841
9842     },
9843
9844     // private
9845     updateResponse : function(dataSet){
9846
9847     }
9848 });/*
9849  * Based on:
9850  * Ext JS Library 1.1.1
9851  * Copyright(c) 2006-2007, Ext JS, LLC.
9852  *
9853  * Originally Released Under LGPL - original licence link has changed is not relivant.
9854  *
9855  * Fork - LGPL
9856  * <script type="text/javascript">
9857  */
9858
9859 /**
9860  * @class Roo.data.ScriptTagProxy
9861  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9862  * other than the originating domain of the running page.<br><br>
9863  * <p>
9864  * <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
9865  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9866  * <p>
9867  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9868  * source code that is used as the source inside a &lt;script> tag.<br><br>
9869  * <p>
9870  * In order for the browser to process the returned data, the server must wrap the data object
9871  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9872  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9873  * depending on whether the callback name was passed:
9874  * <p>
9875  * <pre><code>
9876 boolean scriptTag = false;
9877 String cb = request.getParameter("callback");
9878 if (cb != null) {
9879     scriptTag = true;
9880     response.setContentType("text/javascript");
9881 } else {
9882     response.setContentType("application/x-json");
9883 }
9884 Writer out = response.getWriter();
9885 if (scriptTag) {
9886     out.write(cb + "(");
9887 }
9888 out.print(dataBlock.toJsonString());
9889 if (scriptTag) {
9890     out.write(");");
9891 }
9892 </pre></code>
9893  *
9894  * @constructor
9895  * @param {Object} config A configuration object.
9896  */
9897 Roo.data.ScriptTagProxy = function(config){
9898     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9899     Roo.apply(this, config);
9900     this.head = document.getElementsByTagName("head")[0];
9901 };
9902
9903 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9904
9905 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9906     /**
9907      * @cfg {String} url The URL from which to request the data object.
9908      */
9909     /**
9910      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9911      */
9912     timeout : 30000,
9913     /**
9914      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9915      * the server the name of the callback function set up by the load call to process the returned data object.
9916      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9917      * javascript output which calls this named function passing the data object as its only parameter.
9918      */
9919     callbackParam : "callback",
9920     /**
9921      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9922      * name to the request.
9923      */
9924     nocache : true,
9925
9926     /**
9927      * Load data from the configured URL, read the data object into
9928      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9929      * process that block using the passed callback.
9930      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9931      * for the request to the remote server.
9932      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9933      * object into a block of Roo.data.Records.
9934      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9935      * The function must be passed <ul>
9936      * <li>The Record block object</li>
9937      * <li>The "arg" argument from the load function</li>
9938      * <li>A boolean success indicator</li>
9939      * </ul>
9940      * @param {Object} scope The scope in which to call the callback
9941      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9942      */
9943     load : function(params, reader, callback, scope, arg){
9944         if(this.fireEvent("beforeload", this, params) !== false){
9945
9946             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9947
9948             var url = this.url;
9949             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9950             if(this.nocache){
9951                 url += "&_dc=" + (new Date().getTime());
9952             }
9953             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9954             var trans = {
9955                 id : transId,
9956                 cb : "stcCallback"+transId,
9957                 scriptId : "stcScript"+transId,
9958                 params : params,
9959                 arg : arg,
9960                 url : url,
9961                 callback : callback,
9962                 scope : scope,
9963                 reader : reader
9964             };
9965             var conn = this;
9966
9967             window[trans.cb] = function(o){
9968                 conn.handleResponse(o, trans);
9969             };
9970
9971             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9972
9973             if(this.autoAbort !== false){
9974                 this.abort();
9975             }
9976
9977             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9978
9979             var script = document.createElement("script");
9980             script.setAttribute("src", url);
9981             script.setAttribute("type", "text/javascript");
9982             script.setAttribute("id", trans.scriptId);
9983             this.head.appendChild(script);
9984
9985             this.trans = trans;
9986         }else{
9987             callback.call(scope||this, null, arg, false);
9988         }
9989     },
9990
9991     // private
9992     isLoading : function(){
9993         return this.trans ? true : false;
9994     },
9995
9996     /**
9997      * Abort the current server request.
9998      */
9999     abort : function(){
10000         if(this.isLoading()){
10001             this.destroyTrans(this.trans);
10002         }
10003     },
10004
10005     // private
10006     destroyTrans : function(trans, isLoaded){
10007         this.head.removeChild(document.getElementById(trans.scriptId));
10008         clearTimeout(trans.timeoutId);
10009         if(isLoaded){
10010             window[trans.cb] = undefined;
10011             try{
10012                 delete window[trans.cb];
10013             }catch(e){}
10014         }else{
10015             // if hasn't been loaded, wait for load to remove it to prevent script error
10016             window[trans.cb] = function(){
10017                 window[trans.cb] = undefined;
10018                 try{
10019                     delete window[trans.cb];
10020                 }catch(e){}
10021             };
10022         }
10023     },
10024
10025     // private
10026     handleResponse : function(o, trans){
10027         this.trans = false;
10028         this.destroyTrans(trans, true);
10029         var result;
10030         try {
10031             result = trans.reader.readRecords(o);
10032         }catch(e){
10033             this.fireEvent("loadexception", this, o, trans.arg, e);
10034             trans.callback.call(trans.scope||window, null, trans.arg, false);
10035             return;
10036         }
10037         this.fireEvent("load", this, o, trans.arg);
10038         trans.callback.call(trans.scope||window, result, trans.arg, true);
10039     },
10040
10041     // private
10042     handleFailure : function(trans){
10043         this.trans = false;
10044         this.destroyTrans(trans, false);
10045         this.fireEvent("loadexception", this, null, trans.arg);
10046         trans.callback.call(trans.scope||window, null, trans.arg, false);
10047     }
10048 });/*
10049  * Based on:
10050  * Ext JS Library 1.1.1
10051  * Copyright(c) 2006-2007, Ext JS, LLC.
10052  *
10053  * Originally Released Under LGPL - original licence link has changed is not relivant.
10054  *
10055  * Fork - LGPL
10056  * <script type="text/javascript">
10057  */
10058
10059 /**
10060  * @class Roo.data.JsonReader
10061  * @extends Roo.data.DataReader
10062  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10063  * based on mappings in a provided Roo.data.Record constructor.
10064  * 
10065  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10066  * in the reply previously. 
10067  * 
10068  * <p>
10069  * Example code:
10070  * <pre><code>
10071 var RecordDef = Roo.data.Record.create([
10072     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10073     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10074 ]);
10075 var myReader = new Roo.data.JsonReader({
10076     totalProperty: "results",    // The property which contains the total dataset size (optional)
10077     root: "rows",                // The property which contains an Array of row objects
10078     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10079 }, RecordDef);
10080 </code></pre>
10081  * <p>
10082  * This would consume a JSON file like this:
10083  * <pre><code>
10084 { 'results': 2, 'rows': [
10085     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10086     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10087 }
10088 </code></pre>
10089  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10090  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10091  * paged from the remote server.
10092  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10093  * @cfg {String} root name of the property which contains the Array of row objects.
10094  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10095  * @constructor
10096  * Create a new JsonReader
10097  * @param {Object} meta Metadata configuration options
10098  * @param {Object} recordType Either an Array of field definition objects,
10099  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10100  */
10101 Roo.data.JsonReader = function(meta, recordType){
10102     
10103     meta = meta || {};
10104     // set some defaults:
10105     Roo.applyIf(meta, {
10106         totalProperty: 'total',
10107         successProperty : 'success',
10108         root : 'data',
10109         id : 'id'
10110     });
10111     
10112     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10113 };
10114 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10115     
10116     /**
10117      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10118      * Used by Store query builder to append _requestMeta to params.
10119      * 
10120      */
10121     metaFromRemote : false,
10122     /**
10123      * This method is only used by a DataProxy which has retrieved data from a remote server.
10124      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10125      * @return {Object} data A data block which is used by an Roo.data.Store object as
10126      * a cache of Roo.data.Records.
10127      */
10128     read : function(response){
10129         var json = response.responseText;
10130        
10131         var o = /* eval:var:o */ eval("("+json+")");
10132         if(!o) {
10133             throw {message: "JsonReader.read: Json object not found"};
10134         }
10135         
10136         if(o.metaData){
10137             
10138             delete this.ef;
10139             this.metaFromRemote = true;
10140             this.meta = o.metaData;
10141             this.recordType = Roo.data.Record.create(o.metaData.fields);
10142             this.onMetaChange(this.meta, this.recordType, o);
10143         }
10144         return this.readRecords(o);
10145     },
10146
10147     // private function a store will implement
10148     onMetaChange : function(meta, recordType, o){
10149
10150     },
10151
10152     /**
10153          * @ignore
10154          */
10155     simpleAccess: function(obj, subsc) {
10156         return obj[subsc];
10157     },
10158
10159         /**
10160          * @ignore
10161          */
10162     getJsonAccessor: function(){
10163         var re = /[\[\.]/;
10164         return function(expr) {
10165             try {
10166                 return(re.test(expr))
10167                     ? new Function("obj", "return obj." + expr)
10168                     : function(obj){
10169                         return obj[expr];
10170                     };
10171             } catch(e){}
10172             return Roo.emptyFn;
10173         };
10174     }(),
10175
10176     /**
10177      * Create a data block containing Roo.data.Records from an XML document.
10178      * @param {Object} o An object which contains an Array of row objects in the property specified
10179      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10180      * which contains the total size of the dataset.
10181      * @return {Object} data A data block which is used by an Roo.data.Store object as
10182      * a cache of Roo.data.Records.
10183      */
10184     readRecords : function(o){
10185         /**
10186          * After any data loads, the raw JSON data is available for further custom processing.
10187          * @type Object
10188          */
10189         this.o = o;
10190         var s = this.meta, Record = this.recordType,
10191             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10192
10193 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10194         if (!this.ef) {
10195             if(s.totalProperty) {
10196                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10197                 }
10198                 if(s.successProperty) {
10199                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10200                 }
10201                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10202                 if (s.id) {
10203                         var g = this.getJsonAccessor(s.id);
10204                         this.getId = function(rec) {
10205                                 var r = g(rec);  
10206                                 return (r === undefined || r === "") ? null : r;
10207                         };
10208                 } else {
10209                         this.getId = function(){return null;};
10210                 }
10211             this.ef = [];
10212             for(var jj = 0; jj < fl; jj++){
10213                 f = fi[jj];
10214                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10215                 this.ef[jj] = this.getJsonAccessor(map);
10216             }
10217         }
10218
10219         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10220         if(s.totalProperty){
10221             var vt = parseInt(this.getTotal(o), 10);
10222             if(!isNaN(vt)){
10223                 totalRecords = vt;
10224             }
10225         }
10226         if(s.successProperty){
10227             var vs = this.getSuccess(o);
10228             if(vs === false || vs === 'false'){
10229                 success = false;
10230             }
10231         }
10232         var records = [];
10233         for(var i = 0; i < c; i++){
10234                 var n = root[i];
10235             var values = {};
10236             var id = this.getId(n);
10237             for(var j = 0; j < fl; j++){
10238                 f = fi[j];
10239             var v = this.ef[j](n);
10240             if (!f.convert) {
10241                 Roo.log('missing convert for ' + f.name);
10242                 Roo.log(f);
10243                 continue;
10244             }
10245             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10246             }
10247             var record = new Record(values, id);
10248             record.json = n;
10249             records[i] = record;
10250         }
10251         return {
10252             raw : o,
10253             success : success,
10254             records : records,
10255             totalRecords : totalRecords
10256         };
10257     }
10258 });/*
10259  * Based on:
10260  * Ext JS Library 1.1.1
10261  * Copyright(c) 2006-2007, Ext JS, LLC.
10262  *
10263  * Originally Released Under LGPL - original licence link has changed is not relivant.
10264  *
10265  * Fork - LGPL
10266  * <script type="text/javascript">
10267  */
10268
10269 /**
10270  * @class Roo.data.ArrayReader
10271  * @extends Roo.data.DataReader
10272  * Data reader class to create an Array of Roo.data.Record objects from an Array.
10273  * Each element of that Array represents a row of data fields. The
10274  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10275  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10276  * <p>
10277  * Example code:.
10278  * <pre><code>
10279 var RecordDef = Roo.data.Record.create([
10280     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
10281     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
10282 ]);
10283 var myReader = new Roo.data.ArrayReader({
10284     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
10285 }, RecordDef);
10286 </code></pre>
10287  * <p>
10288  * This would consume an Array like this:
10289  * <pre><code>
10290 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10291   </code></pre>
10292  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10293  * @constructor
10294  * Create a new JsonReader
10295  * @param {Object} meta Metadata configuration options.
10296  * @param {Object} recordType Either an Array of field definition objects
10297  * as specified to {@link Roo.data.Record#create},
10298  * or an {@link Roo.data.Record} object
10299  * created using {@link Roo.data.Record#create}.
10300  */
10301 Roo.data.ArrayReader = function(meta, recordType){
10302     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10303 };
10304
10305 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10306     /**
10307      * Create a data block containing Roo.data.Records from an XML document.
10308      * @param {Object} o An Array of row objects which represents the dataset.
10309      * @return {Object} data A data block which is used by an Roo.data.Store object as
10310      * a cache of Roo.data.Records.
10311      */
10312     readRecords : function(o){
10313         var sid = this.meta ? this.meta.id : null;
10314         var recordType = this.recordType, fields = recordType.prototype.fields;
10315         var records = [];
10316         var root = o;
10317             for(var i = 0; i < root.length; i++){
10318                     var n = root[i];
10319                 var values = {};
10320                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10321                 for(var j = 0, jlen = fields.length; j < jlen; j++){
10322                 var f = fields.items[j];
10323                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10324                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10325                 v = f.convert(v);
10326                 values[f.name] = v;
10327             }
10328                 var record = new recordType(values, id);
10329                 record.json = n;
10330                 records[records.length] = record;
10331             }
10332             return {
10333                 records : records,
10334                 totalRecords : records.length
10335             };
10336     }
10337 });/*
10338  * - LGPL
10339  * * 
10340  */
10341
10342 /**
10343  * @class Roo.bootstrap.ComboBox
10344  * @extends Roo.bootstrap.TriggerField
10345  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10346  * @cfg {Boolean} append (true|false) default false
10347  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10348  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10349  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10350  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10351  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10352  * @constructor
10353  * Create a new ComboBox.
10354  * @param {Object} config Configuration options
10355  */
10356 Roo.bootstrap.ComboBox = function(config){
10357     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10358     this.addEvents({
10359         /**
10360          * @event expand
10361          * Fires when the dropdown list is expanded
10362              * @param {Roo.bootstrap.ComboBox} combo This combo box
10363              */
10364         'expand' : true,
10365         /**
10366          * @event collapse
10367          * Fires when the dropdown list is collapsed
10368              * @param {Roo.bootstrap.ComboBox} combo This combo box
10369              */
10370         'collapse' : true,
10371         /**
10372          * @event beforeselect
10373          * Fires before a list item is selected. Return false to cancel the selection.
10374              * @param {Roo.bootstrap.ComboBox} combo This combo box
10375              * @param {Roo.data.Record} record The data record returned from the underlying store
10376              * @param {Number} index The index of the selected item in the dropdown list
10377              */
10378         'beforeselect' : true,
10379         /**
10380          * @event select
10381          * Fires when a list item is selected
10382              * @param {Roo.bootstrap.ComboBox} combo This combo box
10383              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10384              * @param {Number} index The index of the selected item in the dropdown list
10385              */
10386         'select' : true,
10387         /**
10388          * @event beforequery
10389          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10390          * The event object passed has these properties:
10391              * @param {Roo.bootstrap.ComboBox} combo This combo box
10392              * @param {String} query The query
10393              * @param {Boolean} forceAll true to force "all" query
10394              * @param {Boolean} cancel true to cancel the query
10395              * @param {Object} e The query event object
10396              */
10397         'beforequery': true,
10398          /**
10399          * @event add
10400          * Fires when the 'add' icon is pressed (add a listener to enable add button)
10401              * @param {Roo.bootstrap.ComboBox} combo This combo box
10402              */
10403         'add' : true,
10404         /**
10405          * @event edit
10406          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10407              * @param {Roo.bootstrap.ComboBox} combo This combo box
10408              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10409              */
10410         'edit' : true,
10411         /**
10412          * @event remove
10413          * Fires when the remove value from the combobox array
10414              * @param {Roo.bootstrap.ComboBox} combo This combo box
10415              */
10416         'remove' : true
10417         
10418     });
10419     
10420     this.item = [];
10421     this.tickItems = [];
10422     
10423     this.selectedIndex = -1;
10424     if(this.mode == 'local'){
10425         if(config.queryDelay === undefined){
10426             this.queryDelay = 10;
10427         }
10428         if(config.minChars === undefined){
10429             this.minChars = 0;
10430         }
10431     }
10432 };
10433
10434 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10435      
10436     /**
10437      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10438      * rendering into an Roo.Editor, defaults to false)
10439      */
10440     /**
10441      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10442      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10443      */
10444     /**
10445      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10446      */
10447     /**
10448      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10449      * the dropdown list (defaults to undefined, with no header element)
10450      */
10451
10452      /**
10453      * @cfg {String/Roo.Template} tpl The template to use to render the output
10454      */
10455      
10456      /**
10457      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10458      */
10459     listWidth: undefined,
10460     /**
10461      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10462      * mode = 'remote' or 'text' if mode = 'local')
10463      */
10464     displayField: undefined,
10465     /**
10466      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10467      * mode = 'remote' or 'value' if mode = 'local'). 
10468      * Note: use of a valueField requires the user make a selection
10469      * in order for a value to be mapped.
10470      */
10471     valueField: undefined,
10472     
10473     
10474     /**
10475      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10476      * field's data value (defaults to the underlying DOM element's name)
10477      */
10478     hiddenName: undefined,
10479     /**
10480      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10481      */
10482     listClass: '',
10483     /**
10484      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10485      */
10486     selectedClass: 'active',
10487     
10488     /**
10489      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10490      */
10491     shadow:'sides',
10492     /**
10493      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10494      * anchor positions (defaults to 'tl-bl')
10495      */
10496     listAlign: 'tl-bl?',
10497     /**
10498      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10499      */
10500     maxHeight: 300,
10501     /**
10502      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10503      * query specified by the allQuery config option (defaults to 'query')
10504      */
10505     triggerAction: 'query',
10506     /**
10507      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10508      * (defaults to 4, does not apply if editable = false)
10509      */
10510     minChars : 4,
10511     /**
10512      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10513      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10514      */
10515     typeAhead: false,
10516     /**
10517      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10518      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10519      */
10520     queryDelay: 500,
10521     /**
10522      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10523      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10524      */
10525     pageSize: 0,
10526     /**
10527      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10528      * when editable = true (defaults to false)
10529      */
10530     selectOnFocus:false,
10531     /**
10532      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10533      */
10534     queryParam: 'query',
10535     /**
10536      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10537      * when mode = 'remote' (defaults to 'Loading...')
10538      */
10539     loadingText: 'Loading...',
10540     /**
10541      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10542      */
10543     resizable: false,
10544     /**
10545      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10546      */
10547     handleHeight : 8,
10548     /**
10549      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10550      * traditional select (defaults to true)
10551      */
10552     editable: true,
10553     /**
10554      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10555      */
10556     allQuery: '',
10557     /**
10558      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10559      */
10560     mode: 'remote',
10561     /**
10562      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10563      * listWidth has a higher value)
10564      */
10565     minListWidth : 70,
10566     /**
10567      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10568      * allow the user to set arbitrary text into the field (defaults to false)
10569      */
10570     forceSelection:false,
10571     /**
10572      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10573      * if typeAhead = true (defaults to 250)
10574      */
10575     typeAheadDelay : 250,
10576     /**
10577      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10578      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10579      */
10580     valueNotFoundText : undefined,
10581     /**
10582      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10583      */
10584     blockFocus : false,
10585     
10586     /**
10587      * @cfg {Boolean} disableClear Disable showing of clear button.
10588      */
10589     disableClear : false,
10590     /**
10591      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10592      */
10593     alwaysQuery : false,
10594     
10595     /**
10596      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10597      */
10598     multiple : false,
10599     
10600     //private
10601     addicon : false,
10602     editicon: false,
10603     
10604     page: 0,
10605     hasQuery: false,
10606     append: false,
10607     loadNext: false,
10608     autoFocus : true,
10609     tickable : false,
10610     btnPosition : 'right',
10611     triggerList : true,
10612     showToggleBtn : true,
10613     // element that contains real text value.. (when hidden is used..)
10614     
10615     getAutoCreate : function()
10616     {
10617         var cfg = false;
10618         
10619         /*
10620          *  Normal ComboBox
10621          */
10622         if(!this.tickable){
10623             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10624             return cfg;
10625         }
10626         
10627         /*
10628          *  ComboBox with tickable selections
10629          */
10630              
10631         var align = this.labelAlign || this.parentLabelAlign();
10632         
10633         cfg = {
10634             cls : 'form-group roo-combobox-tickable' //input-group
10635         };
10636         
10637         
10638         var buttons = {
10639             tag : 'div',
10640             cls : 'tickable-buttons',
10641             cn : [
10642                 {
10643                     tag : 'button',
10644                     type : 'button',
10645                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10646                     html : 'Edit'
10647                 },
10648                 {
10649                     tag : 'button',
10650                     type : 'button',
10651                     name : 'ok',
10652                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10653                     html : 'Done'
10654                 },
10655                 {
10656                     tag : 'button',
10657                     type : 'button',
10658                     name : 'cancel',
10659                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10660                     html : 'Cancel'
10661                 }
10662             ]
10663         };
10664         
10665         var _this = this;
10666         Roo.each(buttons.cn, function(c){
10667             if (_this.size) {
10668                 c.cls += ' btn-' + _this.size;
10669             }
10670
10671             if (_this.disabled) {
10672                 c.disabled = true;
10673             }
10674         });
10675         
10676         var box = {
10677             tag: 'div',
10678             cn: [
10679                 {
10680                     tag: 'input',
10681                     type : 'hidden',
10682                     cls: 'form-hidden-field'
10683                 },
10684                 {
10685                     tag: 'ul',
10686                     cls: 'select2-choices',
10687                     cn:[
10688                         {
10689                             tag: 'li',
10690                             cls: 'select2-search-field',
10691                             cn: [
10692
10693                                 buttons
10694                             ]
10695                         }
10696                     ]
10697                 }
10698             ]
10699         }
10700         
10701         var combobox = {
10702             cls: 'select2-container input-group select2-container-multi',
10703             cn: [
10704                 box
10705 //                {
10706 //                    tag: 'ul',
10707 //                    cls: 'typeahead typeahead-long dropdown-menu',
10708 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
10709 //                }
10710             ]
10711         };
10712         
10713         if (align ==='left' && this.fieldLabel.length) {
10714             
10715                 Roo.log("left and has label");
10716                 cfg.cn = [
10717                     
10718                     {
10719                         tag: 'label',
10720                         'for' :  id,
10721                         cls : 'control-label col-sm-' + this.labelWidth,
10722                         html : this.fieldLabel
10723                         
10724                     },
10725                     {
10726                         cls : "col-sm-" + (12 - this.labelWidth), 
10727                         cn: [
10728                             combobox
10729                         ]
10730                     }
10731                     
10732                 ];
10733         } else if ( this.fieldLabel.length) {
10734                 Roo.log(" label");
10735                  cfg.cn = [
10736                    
10737                     {
10738                         tag: 'label',
10739                         //cls : 'input-group-addon',
10740                         html : this.fieldLabel
10741                         
10742                     },
10743                     
10744                     combobox
10745                     
10746                 ];
10747
10748         } else {
10749             
10750                 Roo.log(" no label && no align");
10751                 cfg = combobox
10752                      
10753                 
10754         }
10755          
10756         var settings=this;
10757         ['xs','sm','md','lg'].map(function(size){
10758             if (settings[size]) {
10759                 cfg.cls += ' col-' + size + '-' + settings[size];
10760             }
10761         });
10762         
10763         return cfg;
10764         
10765     },
10766     
10767     // private
10768     initEvents: function()
10769     {
10770         
10771         if (!this.store) {
10772             throw "can not find store for combo";
10773         }
10774         this.store = Roo.factory(this.store, Roo.data);
10775         
10776         if(this.tickable){
10777             this.initTickableEvents();
10778             return;
10779         }
10780         
10781         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10782         
10783         if(this.hiddenName){
10784             
10785             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10786             
10787             this.hiddenField.dom.value =
10788                 this.hiddenValue !== undefined ? this.hiddenValue :
10789                 this.value !== undefined ? this.value : '';
10790
10791             // prevent input submission
10792             this.el.dom.removeAttribute('name');
10793             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10794              
10795              
10796         }
10797         //if(Roo.isGecko){
10798         //    this.el.dom.setAttribute('autocomplete', 'off');
10799         //}
10800         
10801         var cls = 'x-combo-list';
10802         
10803         //this.list = new Roo.Layer({
10804         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10805         //});
10806         
10807         var _this = this;
10808         
10809         (function(){
10810             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10811             _this.list.setWidth(lw);
10812         }).defer(100);
10813         
10814         this.list.on('mouseover', this.onViewOver, this);
10815         this.list.on('mousemove', this.onViewMove, this);
10816         
10817         this.list.on('scroll', this.onViewScroll, this);
10818         
10819         /*
10820         this.list.swallowEvent('mousewheel');
10821         this.assetHeight = 0;
10822
10823         if(this.title){
10824             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10825             this.assetHeight += this.header.getHeight();
10826         }
10827
10828         this.innerList = this.list.createChild({cls:cls+'-inner'});
10829         this.innerList.on('mouseover', this.onViewOver, this);
10830         this.innerList.on('mousemove', this.onViewMove, this);
10831         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10832         
10833         if(this.allowBlank && !this.pageSize && !this.disableClear){
10834             this.footer = this.list.createChild({cls:cls+'-ft'});
10835             this.pageTb = new Roo.Toolbar(this.footer);
10836            
10837         }
10838         if(this.pageSize){
10839             this.footer = this.list.createChild({cls:cls+'-ft'});
10840             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10841                     {pageSize: this.pageSize});
10842             
10843         }
10844         
10845         if (this.pageTb && this.allowBlank && !this.disableClear) {
10846             var _this = this;
10847             this.pageTb.add(new Roo.Toolbar.Fill(), {
10848                 cls: 'x-btn-icon x-btn-clear',
10849                 text: '&#160;',
10850                 handler: function()
10851                 {
10852                     _this.collapse();
10853                     _this.clearValue();
10854                     _this.onSelect(false, -1);
10855                 }
10856             });
10857         }
10858         if (this.footer) {
10859             this.assetHeight += this.footer.getHeight();
10860         }
10861         */
10862             
10863         if(!this.tpl){
10864             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10865         }
10866
10867         this.view = new Roo.View(this.list, this.tpl, {
10868             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10869         });
10870         //this.view.wrapEl.setDisplayed(false);
10871         this.view.on('click', this.onViewClick, this);
10872         
10873         
10874         
10875         this.store.on('beforeload', this.onBeforeLoad, this);
10876         this.store.on('load', this.onLoad, this);
10877         this.store.on('loadexception', this.onLoadException, this);
10878         /*
10879         if(this.resizable){
10880             this.resizer = new Roo.Resizable(this.list,  {
10881                pinned:true, handles:'se'
10882             });
10883             this.resizer.on('resize', function(r, w, h){
10884                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10885                 this.listWidth = w;
10886                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10887                 this.restrictHeight();
10888             }, this);
10889             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10890         }
10891         */
10892         if(!this.editable){
10893             this.editable = true;
10894             this.setEditable(false);
10895         }
10896         
10897         /*
10898         
10899         if (typeof(this.events.add.listeners) != 'undefined') {
10900             
10901             this.addicon = this.wrap.createChild(
10902                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10903        
10904             this.addicon.on('click', function(e) {
10905                 this.fireEvent('add', this);
10906             }, this);
10907         }
10908         if (typeof(this.events.edit.listeners) != 'undefined') {
10909             
10910             this.editicon = this.wrap.createChild(
10911                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10912             if (this.addicon) {
10913                 this.editicon.setStyle('margin-left', '40px');
10914             }
10915             this.editicon.on('click', function(e) {
10916                 
10917                 // we fire even  if inothing is selected..
10918                 this.fireEvent('edit', this, this.lastData );
10919                 
10920             }, this);
10921         }
10922         */
10923         
10924         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10925             "up" : function(e){
10926                 this.inKeyMode = true;
10927                 this.selectPrev();
10928             },
10929
10930             "down" : function(e){
10931                 if(!this.isExpanded()){
10932                     this.onTriggerClick();
10933                 }else{
10934                     this.inKeyMode = true;
10935                     this.selectNext();
10936                 }
10937             },
10938
10939             "enter" : function(e){
10940 //                this.onViewClick();
10941                 //return true;
10942                 this.collapse();
10943                 
10944                 if(this.fireEvent("specialkey", this, e)){
10945                     this.onViewClick(false);
10946                 }
10947                 
10948                 return true;
10949             },
10950
10951             "esc" : function(e){
10952                 this.collapse();
10953             },
10954
10955             "tab" : function(e){
10956                 this.collapse();
10957                 
10958                 if(this.fireEvent("specialkey", this, e)){
10959                     this.onViewClick(false);
10960                 }
10961                 
10962                 return true;
10963             },
10964
10965             scope : this,
10966
10967             doRelay : function(foo, bar, hname){
10968                 if(hname == 'down' || this.scope.isExpanded()){
10969                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10970                 }
10971                 return true;
10972             },
10973
10974             forceKeyDown: true
10975         });
10976         
10977         
10978         this.queryDelay = Math.max(this.queryDelay || 10,
10979                 this.mode == 'local' ? 10 : 250);
10980         
10981         
10982         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10983         
10984         if(this.typeAhead){
10985             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10986         }
10987         if(this.editable !== false){
10988             this.inputEl().on("keyup", this.onKeyUp, this);
10989         }
10990         if(this.forceSelection){
10991             this.inputEl().on('blur', this.doForce, this);
10992         }
10993         
10994         if(this.multiple){
10995             this.choices = this.el.select('ul.select2-choices', true).first();
10996             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10997         }
10998     },
10999     
11000     initTickableEvents: function()
11001     {   
11002         this.createList();
11003         
11004         if(this.hiddenName){
11005             
11006             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11007             
11008             this.hiddenField.dom.value =
11009                 this.hiddenValue !== undefined ? this.hiddenValue :
11010                 this.value !== undefined ? this.value : '';
11011
11012             // prevent input submission
11013             this.el.dom.removeAttribute('name');
11014             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11015              
11016              
11017         }
11018         
11019 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11020         
11021         this.choices = this.el.select('ul.select2-choices', true).first();
11022         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11023         if(this.triggerList){
11024             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11025         }
11026          
11027         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11028         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11029         
11030         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11031         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11032         
11033         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11034         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11035         
11036         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11037         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11038         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11039         
11040         this.okBtn.hide();
11041         this.cancelBtn.hide();
11042         
11043         var _this = this;
11044         
11045         (function(){
11046             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11047             _this.list.setWidth(lw);
11048         }).defer(100);
11049         
11050         this.list.on('mouseover', this.onViewOver, this);
11051         this.list.on('mousemove', this.onViewMove, this);
11052         
11053         this.list.on('scroll', this.onViewScroll, this);
11054         
11055         if(!this.tpl){
11056             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11057         }
11058
11059         this.view = new Roo.View(this.list, this.tpl, {
11060             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11061         });
11062         
11063         //this.view.wrapEl.setDisplayed(false);
11064         this.view.on('click', this.onViewClick, this);
11065         
11066         
11067         
11068         this.store.on('beforeload', this.onBeforeLoad, this);
11069         this.store.on('load', this.onLoad, this);
11070         this.store.on('loadexception', this.onLoadException, this);
11071         
11072 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
11073 //            "up" : function(e){
11074 //                this.inKeyMode = true;
11075 //                this.selectPrev();
11076 //            },
11077 //
11078 //            "down" : function(e){
11079 //                if(!this.isExpanded()){
11080 //                    this.onTriggerClick();
11081 //                }else{
11082 //                    this.inKeyMode = true;
11083 //                    this.selectNext();
11084 //                }
11085 //            },
11086 //
11087 //            "enter" : function(e){
11088 ////                this.onViewClick();
11089 //                //return true;
11090 //                this.collapse();
11091 //                
11092 //                if(this.fireEvent("specialkey", this, e)){
11093 //                    this.onViewClick(false);
11094 //                }
11095 //                
11096 //                return true;
11097 //            },
11098 //
11099 //            "esc" : function(e){
11100 //                this.collapse();
11101 //            },
11102 //
11103 //            "tab" : function(e){
11104 //                this.collapse();
11105 //                
11106 //                if(this.fireEvent("specialkey", this, e)){
11107 //                    this.onViewClick(false);
11108 //                }
11109 //                
11110 //                return true;
11111 //            },
11112 //
11113 //            scope : this,
11114 //
11115 //            doRelay : function(foo, bar, hname){
11116 //                if(hname == 'down' || this.scope.isExpanded()){
11117 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11118 //                }
11119 //                return true;
11120 //            },
11121 //
11122 //            forceKeyDown: true
11123 //        });
11124         
11125         
11126         this.queryDelay = Math.max(this.queryDelay || 10,
11127                 this.mode == 'local' ? 10 : 250);
11128         
11129         
11130         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11131         
11132         if(this.typeAhead){
11133             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11134         }
11135     },
11136
11137     onDestroy : function(){
11138         if(this.view){
11139             this.view.setStore(null);
11140             this.view.el.removeAllListeners();
11141             this.view.el.remove();
11142             this.view.purgeListeners();
11143         }
11144         if(this.list){
11145             this.list.dom.innerHTML  = '';
11146         }
11147         
11148         if(this.store){
11149             this.store.un('beforeload', this.onBeforeLoad, this);
11150             this.store.un('load', this.onLoad, this);
11151             this.store.un('loadexception', this.onLoadException, this);
11152         }
11153         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11154     },
11155
11156     // private
11157     fireKey : function(e){
11158         if(e.isNavKeyPress() && !this.list.isVisible()){
11159             this.fireEvent("specialkey", this, e);
11160         }
11161     },
11162
11163     // private
11164     onResize: function(w, h){
11165 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11166 //        
11167 //        if(typeof w != 'number'){
11168 //            // we do not handle it!?!?
11169 //            return;
11170 //        }
11171 //        var tw = this.trigger.getWidth();
11172 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
11173 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
11174 //        var x = w - tw;
11175 //        this.inputEl().setWidth( this.adjustWidth('input', x));
11176 //            
11177 //        //this.trigger.setStyle('left', x+'px');
11178 //        
11179 //        if(this.list && this.listWidth === undefined){
11180 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11181 //            this.list.setWidth(lw);
11182 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11183 //        }
11184         
11185     
11186         
11187     },
11188
11189     /**
11190      * Allow or prevent the user from directly editing the field text.  If false is passed,
11191      * the user will only be able to select from the items defined in the dropdown list.  This method
11192      * is the runtime equivalent of setting the 'editable' config option at config time.
11193      * @param {Boolean} value True to allow the user to directly edit the field text
11194      */
11195     setEditable : function(value){
11196         if(value == this.editable){
11197             return;
11198         }
11199         this.editable = value;
11200         if(!value){
11201             this.inputEl().dom.setAttribute('readOnly', true);
11202             this.inputEl().on('mousedown', this.onTriggerClick,  this);
11203             this.inputEl().addClass('x-combo-noedit');
11204         }else{
11205             this.inputEl().dom.setAttribute('readOnly', false);
11206             this.inputEl().un('mousedown', this.onTriggerClick,  this);
11207             this.inputEl().removeClass('x-combo-noedit');
11208         }
11209     },
11210
11211     // private
11212     
11213     onBeforeLoad : function(combo,opts){
11214         if(!this.hasFocus){
11215             return;
11216         }
11217          if (!opts.add) {
11218             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11219          }
11220         this.restrictHeight();
11221         this.selectedIndex = -1;
11222     },
11223
11224     // private
11225     onLoad : function(){
11226         
11227         this.hasQuery = false;
11228         
11229         if(!this.hasFocus){
11230             return;
11231         }
11232         
11233         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11234             this.loading.hide();
11235         }
11236         
11237         if(this.store.getCount() > 0){
11238             this.expand();
11239 //            this.restrictHeight();
11240             if(this.lastQuery == this.allQuery){
11241                 if(this.editable && !this.tickable){
11242                     this.inputEl().dom.select();
11243                 }
11244                 
11245                 if(
11246                     !this.selectByValue(this.value, true) &&
11247                     this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' || 
11248                     this.store.lastOptions.add != true)
11249                 ){
11250                     this.select(0, true);
11251                 }
11252             }else{
11253                 if(this.autoFocus){
11254                     this.selectNext();
11255                 }
11256                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11257                     this.taTask.delay(this.typeAheadDelay);
11258                 }
11259             }
11260         }else{
11261             this.onEmptyResults();
11262         }
11263         
11264         //this.el.focus();
11265     },
11266     // private
11267     onLoadException : function()
11268     {
11269         this.hasQuery = false;
11270         
11271         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11272             this.loading.hide();
11273         }
11274         
11275         this.collapse();
11276         Roo.log(this.store.reader.jsonData);
11277         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11278             // fixme
11279             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11280         }
11281         
11282         
11283     },
11284     // private
11285     onTypeAhead : function(){
11286         if(this.store.getCount() > 0){
11287             var r = this.store.getAt(0);
11288             var newValue = r.data[this.displayField];
11289             var len = newValue.length;
11290             var selStart = this.getRawValue().length;
11291             
11292             if(selStart != len){
11293                 this.setRawValue(newValue);
11294                 this.selectText(selStart, newValue.length);
11295             }
11296         }
11297     },
11298
11299     // private
11300     onSelect : function(record, index){
11301         
11302         if(this.fireEvent('beforeselect', this, record, index) !== false){
11303         
11304             this.setFromData(index > -1 ? record.data : false);
11305             
11306             this.collapse();
11307             this.fireEvent('select', this, record, index);
11308         }
11309     },
11310
11311     /**
11312      * Returns the currently selected field value or empty string if no value is set.
11313      * @return {String} value The selected value
11314      */
11315     getValue : function(){
11316         
11317         if(this.multiple){
11318             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11319         }
11320         
11321         if(this.valueField){
11322             return typeof this.value != 'undefined' ? this.value : '';
11323         }else{
11324             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11325         }
11326     },
11327
11328     /**
11329      * Clears any text/value currently set in the field
11330      */
11331     clearValue : function(){
11332         if(this.hiddenField){
11333             this.hiddenField.dom.value = '';
11334         }
11335         this.value = '';
11336         this.setRawValue('');
11337         this.lastSelectionText = '';
11338         
11339     },
11340
11341     /**
11342      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
11343      * will be displayed in the field.  If the value does not match the data value of an existing item,
11344      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11345      * Otherwise the field will be blank (although the value will still be set).
11346      * @param {String} value The value to match
11347      */
11348     setValue : function(v){
11349         if(this.multiple){
11350             this.syncValue();
11351             return;
11352         }
11353         
11354         var text = v;
11355         if(this.valueField){
11356             var r = this.findRecord(this.valueField, v);
11357             if(r){
11358                 text = r.data[this.displayField];
11359             }else if(this.valueNotFoundText !== undefined){
11360                 text = this.valueNotFoundText;
11361             }
11362         }
11363         this.lastSelectionText = text;
11364         if(this.hiddenField){
11365             this.hiddenField.dom.value = v;
11366         }
11367         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11368         this.value = v;
11369     },
11370     /**
11371      * @property {Object} the last set data for the element
11372      */
11373     
11374     lastData : false,
11375     /**
11376      * Sets the value of the field based on a object which is related to the record format for the store.
11377      * @param {Object} value the value to set as. or false on reset?
11378      */
11379     setFromData : function(o){
11380         
11381         if(this.multiple){
11382             this.addItem(o);
11383             return;
11384         }
11385             
11386         var dv = ''; // display value
11387         var vv = ''; // value value..
11388         this.lastData = o;
11389         if (this.displayField) {
11390             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11391         } else {
11392             // this is an error condition!!!
11393             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11394         }
11395         
11396         if(this.valueField){
11397             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11398         }
11399         
11400         if(this.hiddenField){
11401             this.hiddenField.dom.value = vv;
11402             
11403             this.lastSelectionText = dv;
11404             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11405             this.value = vv;
11406             return;
11407         }
11408         // no hidden field.. - we store the value in 'value', but still display
11409         // display field!!!!
11410         this.lastSelectionText = dv;
11411         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11412         this.value = vv;
11413         
11414         
11415     },
11416     // private
11417     reset : function(){
11418         // overridden so that last data is reset..
11419         this.setValue(this.originalValue);
11420         this.clearInvalid();
11421         this.lastData = false;
11422         if (this.view) {
11423             this.view.clearSelections();
11424         }
11425     },
11426     // private
11427     findRecord : function(prop, value){
11428         var record;
11429         if(this.store.getCount() > 0){
11430             this.store.each(function(r){
11431                 if(r.data[prop] == value){
11432                     record = r;
11433                     return false;
11434                 }
11435                 return true;
11436             });
11437         }
11438         return record;
11439     },
11440     
11441     getName: function()
11442     {
11443         // returns hidden if it's set..
11444         if (!this.rendered) {return ''};
11445         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11446         
11447     },
11448     // private
11449     onViewMove : function(e, t){
11450         this.inKeyMode = false;
11451     },
11452
11453     // private
11454     onViewOver : function(e, t){
11455         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11456             return;
11457         }
11458         var item = this.view.findItemFromChild(t);
11459         
11460         if(item){
11461             var index = this.view.indexOf(item);
11462             this.select(index, false);
11463         }
11464     },
11465
11466     // private
11467     onViewClick : function(view, doFocus, el, e)
11468     {
11469         var index = this.view.getSelectedIndexes()[0];
11470         
11471         var r = this.store.getAt(index);
11472         
11473         if(this.tickable){
11474             
11475             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11476                 return;
11477             }
11478             
11479             var rm = false;
11480             var _this = this;
11481             
11482             Roo.each(this.tickItems, function(v,k){
11483                 
11484                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11485                     _this.tickItems.splice(k, 1);
11486                     rm = true;
11487                     return;
11488                 }
11489             })
11490             
11491             if(rm){
11492                 return;
11493             }
11494             
11495             this.tickItems.push(r.data);
11496             return;
11497         }
11498         
11499         if(r){
11500             this.onSelect(r, index);
11501         }
11502         if(doFocus !== false && !this.blockFocus){
11503             this.inputEl().focus();
11504         }
11505     },
11506
11507     // private
11508     restrictHeight : function(){
11509         //this.innerList.dom.style.height = '';
11510         //var inner = this.innerList.dom;
11511         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11512         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11513         //this.list.beginUpdate();
11514         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11515         this.list.alignTo(this.inputEl(), this.listAlign);
11516         this.list.alignTo(this.inputEl(), this.listAlign);
11517         //this.list.endUpdate();
11518     },
11519
11520     // private
11521     onEmptyResults : function(){
11522         this.collapse();
11523     },
11524
11525     /**
11526      * Returns true if the dropdown list is expanded, else false.
11527      */
11528     isExpanded : function(){
11529         return this.list.isVisible();
11530     },
11531
11532     /**
11533      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11534      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11535      * @param {String} value The data value of the item to select
11536      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11537      * selected item if it is not currently in view (defaults to true)
11538      * @return {Boolean} True if the value matched an item in the list, else false
11539      */
11540     selectByValue : function(v, scrollIntoView){
11541         if(v !== undefined && v !== null){
11542             var r = this.findRecord(this.valueField || this.displayField, v);
11543             if(r){
11544                 this.select(this.store.indexOf(r), scrollIntoView);
11545                 return true;
11546             }
11547         }
11548         return false;
11549     },
11550
11551     /**
11552      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11553      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11554      * @param {Number} index The zero-based index of the list item to select
11555      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11556      * selected item if it is not currently in view (defaults to true)
11557      */
11558     select : function(index, scrollIntoView){
11559         this.selectedIndex = index;
11560         this.view.select(index);
11561         if(scrollIntoView !== false){
11562             var el = this.view.getNode(index);
11563             if(el && !this.multiple && !this.tickable){
11564                 this.list.scrollChildIntoView(el, false);
11565             }
11566         }
11567     },
11568
11569     // private
11570     selectNext : function(){
11571         var ct = this.store.getCount();
11572         if(ct > 0){
11573             if(this.selectedIndex == -1){
11574                 this.select(0);
11575             }else if(this.selectedIndex < ct-1){
11576                 this.select(this.selectedIndex+1);
11577             }
11578         }
11579     },
11580
11581     // private
11582     selectPrev : function(){
11583         var ct = this.store.getCount();
11584         if(ct > 0){
11585             if(this.selectedIndex == -1){
11586                 this.select(0);
11587             }else if(this.selectedIndex != 0){
11588                 this.select(this.selectedIndex-1);
11589             }
11590         }
11591     },
11592
11593     // private
11594     onKeyUp : function(e){
11595         if(this.editable !== false && !e.isSpecialKey()){
11596             this.lastKey = e.getKey();
11597             this.dqTask.delay(this.queryDelay);
11598         }
11599     },
11600
11601     // private
11602     validateBlur : function(){
11603         return !this.list || !this.list.isVisible();   
11604     },
11605
11606     // private
11607     initQuery : function(){
11608         this.doQuery(this.getRawValue());
11609     },
11610
11611     // private
11612     doForce : function(){
11613         if(this.inputEl().dom.value.length > 0){
11614             this.inputEl().dom.value =
11615                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11616              
11617         }
11618     },
11619
11620     /**
11621      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11622      * query allowing the query action to be canceled if needed.
11623      * @param {String} query The SQL query to execute
11624      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11625      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11626      * saved in the current store (defaults to false)
11627      */
11628     doQuery : function(q, forceAll){
11629         
11630         if(q === undefined || q === null){
11631             q = '';
11632         }
11633         var qe = {
11634             query: q,
11635             forceAll: forceAll,
11636             combo: this,
11637             cancel:false
11638         };
11639         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11640             return false;
11641         }
11642         q = qe.query;
11643         
11644         forceAll = qe.forceAll;
11645         if(forceAll === true || (q.length >= this.minChars)){
11646             
11647             this.hasQuery = true;
11648             
11649             if(this.lastQuery != q || this.alwaysQuery){
11650                 this.lastQuery = q;
11651                 if(this.mode == 'local'){
11652                     this.selectedIndex = -1;
11653                     if(forceAll){
11654                         this.store.clearFilter();
11655                     }else{
11656                         this.store.filter(this.displayField, q);
11657                     }
11658                     this.onLoad();
11659                 }else{
11660                     this.store.baseParams[this.queryParam] = q;
11661                     
11662                     var options = {params : this.getParams(q)};
11663                     
11664                     if(this.loadNext){
11665                         options.add = true;
11666                         options.params.start = this.page * this.pageSize;
11667                     }
11668                     
11669                     this.store.load(options);
11670                     /*
11671                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11672                      *  we should expand the list on onLoad
11673                      *  so command out it
11674                      */
11675 //                    this.expand();
11676                 }
11677             }else{
11678                 this.selectedIndex = -1;
11679                 this.onLoad();   
11680             }
11681         }
11682         
11683         this.loadNext = false;
11684     },
11685
11686     // private
11687     getParams : function(q){
11688         var p = {};
11689         //p[this.queryParam] = q;
11690         
11691         if(this.pageSize){
11692             p.start = 0;
11693             p.limit = this.pageSize;
11694         }
11695         return p;
11696     },
11697
11698     /**
11699      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11700      */
11701     collapse : function(){
11702         if(!this.isExpanded()){
11703             return;
11704         }
11705         
11706         this.list.hide();
11707         
11708         if(this.tickable){
11709             this.okBtn.hide();
11710             this.cancelBtn.hide();
11711             this.trigger.show();
11712         }
11713         
11714         Roo.get(document).un('mousedown', this.collapseIf, this);
11715         Roo.get(document).un('mousewheel', this.collapseIf, this);
11716         if (!this.editable) {
11717             Roo.get(document).un('keydown', this.listKeyPress, this);
11718         }
11719         this.fireEvent('collapse', this);
11720     },
11721
11722     // private
11723     collapseIf : function(e){
11724         var in_combo  = e.within(this.el);
11725         var in_list =  e.within(this.list);
11726         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11727         
11728         if (in_combo || in_list || is_list) {
11729             //e.stopPropagation();
11730             return;
11731         }
11732         
11733         if(this.tickable){
11734             this.onTickableFooterButtonClick(e, false, false);
11735         }
11736
11737         this.collapse();
11738         
11739     },
11740
11741     /**
11742      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11743      */
11744     expand : function(){
11745        
11746         if(this.isExpanded() || !this.hasFocus){
11747             return;
11748         }
11749         
11750         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11751         this.list.setWidth(lw);
11752         
11753         
11754          Roo.log('expand');
11755         
11756         this.list.show();
11757         
11758         this.restrictHeight();
11759         
11760         if(this.tickable){
11761             
11762             this.tickItems = Roo.apply([], this.item);
11763             
11764             this.okBtn.show();
11765             this.cancelBtn.show();
11766             this.trigger.hide();
11767             
11768         }
11769         
11770         Roo.get(document).on('mousedown', this.collapseIf, this);
11771         Roo.get(document).on('mousewheel', this.collapseIf, this);
11772         if (!this.editable) {
11773             Roo.get(document).on('keydown', this.listKeyPress, this);
11774         }
11775         
11776         this.fireEvent('expand', this);
11777     },
11778
11779     // private
11780     // Implements the default empty TriggerField.onTriggerClick function
11781     onTriggerClick : function(e)
11782     {
11783         Roo.log('trigger click');
11784         
11785         if(this.disabled || !this.triggerList){
11786             return;
11787         }
11788         
11789         this.page = 0;
11790         this.loadNext = false;
11791         
11792         if(this.isExpanded()){
11793             this.collapse();
11794             if (!this.blockFocus) {
11795                 this.inputEl().focus();
11796             }
11797             
11798         }else {
11799             this.hasFocus = true;
11800             if(this.triggerAction == 'all') {
11801                 this.doQuery(this.allQuery, true);
11802             } else {
11803                 this.doQuery(this.getRawValue());
11804             }
11805             if (!this.blockFocus) {
11806                 this.inputEl().focus();
11807             }
11808         }
11809     },
11810     
11811     onTickableTriggerClick : function(e)
11812     {
11813         if(this.disabled){
11814             return;
11815         }
11816         
11817         this.page = 0;
11818         this.loadNext = false;
11819         this.hasFocus = true;
11820         
11821         if(this.triggerAction == 'all') {
11822             this.doQuery(this.allQuery, true);
11823         } else {
11824             this.doQuery(this.getRawValue());
11825         }
11826     },
11827     
11828     onSearchFieldClick : function(e)
11829     {
11830         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11831             return;
11832         }
11833         
11834         this.page = 0;
11835         this.loadNext = false;
11836         this.hasFocus = true;
11837         
11838         if(this.triggerAction == 'all') {
11839             this.doQuery(this.allQuery, true);
11840         } else {
11841             this.doQuery(this.getRawValue());
11842         }
11843     },
11844     
11845     listKeyPress : function(e)
11846     {
11847         //Roo.log('listkeypress');
11848         // scroll to first matching element based on key pres..
11849         if (e.isSpecialKey()) {
11850             return false;
11851         }
11852         var k = String.fromCharCode(e.getKey()).toUpperCase();
11853         //Roo.log(k);
11854         var match  = false;
11855         var csel = this.view.getSelectedNodes();
11856         var cselitem = false;
11857         if (csel.length) {
11858             var ix = this.view.indexOf(csel[0]);
11859             cselitem  = this.store.getAt(ix);
11860             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11861                 cselitem = false;
11862             }
11863             
11864         }
11865         
11866         this.store.each(function(v) { 
11867             if (cselitem) {
11868                 // start at existing selection.
11869                 if (cselitem.id == v.id) {
11870                     cselitem = false;
11871                 }
11872                 return true;
11873             }
11874                 
11875             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11876                 match = this.store.indexOf(v);
11877                 return false;
11878             }
11879             return true;
11880         }, this);
11881         
11882         if (match === false) {
11883             return true; // no more action?
11884         }
11885         // scroll to?
11886         this.view.select(match);
11887         var sn = Roo.get(this.view.getSelectedNodes()[0])
11888         sn.scrollIntoView(sn.dom.parentNode, false);
11889     },
11890     
11891     onViewScroll : function(e, t){
11892         
11893         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11894             return;
11895         }
11896         
11897         this.hasQuery = true;
11898         
11899         this.loading = this.list.select('.loading', true).first();
11900         
11901         if(this.loading === null){
11902             this.list.createChild({
11903                 tag: 'div',
11904                 cls: 'loading select2-more-results select2-active',
11905                 html: 'Loading more results...'
11906             })
11907             
11908             this.loading = this.list.select('.loading', true).first();
11909             
11910             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11911             
11912             this.loading.hide();
11913         }
11914         
11915         this.loading.show();
11916         
11917         var _combo = this;
11918         
11919         this.page++;
11920         this.loadNext = true;
11921         
11922         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11923         
11924         return;
11925     },
11926     
11927     addItem : function(o)
11928     {   
11929         var dv = ''; // display value
11930         
11931         if (this.displayField) {
11932             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11933         } else {
11934             // this is an error condition!!!
11935             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11936         }
11937         
11938         if(!dv.length){
11939             return;
11940         }
11941         
11942         var choice = this.choices.createChild({
11943             tag: 'li',
11944             cls: 'select2-search-choice',
11945             cn: [
11946                 {
11947                     tag: 'div',
11948                     html: dv
11949                 },
11950                 {
11951                     tag: 'a',
11952                     href: '#',
11953                     cls: 'select2-search-choice-close',
11954                     tabindex: '-1'
11955                 }
11956             ]
11957             
11958         }, this.searchField);
11959         
11960         var close = choice.select('a.select2-search-choice-close', true).first()
11961         
11962         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11963         
11964         this.item.push(o);
11965         
11966         this.lastData = o;
11967         
11968         this.syncValue();
11969         
11970         this.inputEl().dom.value = '';
11971         
11972     },
11973     
11974     onRemoveItem : function(e, _self, o)
11975     {
11976         e.preventDefault();
11977         var index = this.item.indexOf(o.data) * 1;
11978         
11979         if( index < 0){
11980             Roo.log('not this item?!');
11981             return;
11982         }
11983         
11984         this.item.splice(index, 1);
11985         o.item.remove();
11986         
11987         this.syncValue();
11988         
11989         this.fireEvent('remove', this, e);
11990         
11991     },
11992     
11993     syncValue : function()
11994     {
11995         if(!this.item.length){
11996             this.clearValue();
11997             return;
11998         }
11999             
12000         var value = [];
12001         var _this = this;
12002         Roo.each(this.item, function(i){
12003             if(_this.valueField){
12004                 value.push(i[_this.valueField]);
12005                 return;
12006             }
12007
12008             value.push(i);
12009         });
12010
12011         this.value = value.join(',');
12012
12013         if(this.hiddenField){
12014             this.hiddenField.dom.value = this.value;
12015         }
12016     },
12017     
12018     clearItem : function()
12019     {
12020         if(!this.multiple){
12021             return;
12022         }
12023         
12024         this.item = [];
12025         
12026         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12027            c.remove();
12028         });
12029         
12030         this.syncValue();
12031     },
12032     
12033     inputEl: function ()
12034     {
12035         if(this.tickable){
12036             return this.searchField;
12037         }
12038         return this.el.select('input.form-control',true).first();
12039     },
12040     
12041     
12042     onTickableFooterButtonClick : function(e, btn, el)
12043     {
12044         e.preventDefault();
12045         
12046         if(btn && btn.name == 'cancel'){
12047             this.tickItems = Roo.apply([], this.item);
12048             this.collapse();
12049             return;
12050         }
12051         
12052         this.clearItem();
12053         
12054         var _this = this;
12055         
12056         Roo.each(this.tickItems, function(o){
12057             _this.addItem(o);
12058         });
12059         
12060         this.collapse();
12061         
12062     },
12063     
12064     validate : function()
12065     {
12066         var v = this.getRawValue();
12067         
12068         if(this.multiple){
12069             v = this.getValue();
12070         }
12071         
12072         if(this.disabled || this.validateValue(v)){
12073             this.clearInvalid();
12074             return true;
12075         }
12076         return false;
12077     }
12078     
12079     
12080
12081     /** 
12082     * @cfg {Boolean} grow 
12083     * @hide 
12084     */
12085     /** 
12086     * @cfg {Number} growMin 
12087     * @hide 
12088     */
12089     /** 
12090     * @cfg {Number} growMax 
12091     * @hide 
12092     */
12093     /**
12094      * @hide
12095      * @method autoSize
12096      */
12097 });
12098 /*
12099  * Based on:
12100  * Ext JS Library 1.1.1
12101  * Copyright(c) 2006-2007, Ext JS, LLC.
12102  *
12103  * Originally Released Under LGPL - original licence link has changed is not relivant.
12104  *
12105  * Fork - LGPL
12106  * <script type="text/javascript">
12107  */
12108
12109 /**
12110  * @class Roo.View
12111  * @extends Roo.util.Observable
12112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
12113  * This class also supports single and multi selection modes. <br>
12114  * Create a data model bound view:
12115  <pre><code>
12116  var store = new Roo.data.Store(...);
12117
12118  var view = new Roo.View({
12119     el : "my-element",
12120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
12121  
12122     singleSelect: true,
12123     selectedClass: "ydataview-selected",
12124     store: store
12125  });
12126
12127  // listen for node click?
12128  view.on("click", function(vw, index, node, e){
12129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12130  });
12131
12132  // load XML data
12133  dataModel.load("foobar.xml");
12134  </code></pre>
12135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12136  * <br><br>
12137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12139  * 
12140  * Note: old style constructor is still suported (container, template, config)
12141  * 
12142  * @constructor
12143  * Create a new View
12144  * @param {Object} config The config object
12145  * 
12146  */
12147 Roo.View = function(config, depreciated_tpl, depreciated_config){
12148     
12149     this.parent = false;
12150     
12151     if (typeof(depreciated_tpl) == 'undefined') {
12152         // new way.. - universal constructor.
12153         Roo.apply(this, config);
12154         this.el  = Roo.get(this.el);
12155     } else {
12156         // old format..
12157         this.el  = Roo.get(config);
12158         this.tpl = depreciated_tpl;
12159         Roo.apply(this, depreciated_config);
12160     }
12161     this.wrapEl  = this.el.wrap().wrap();
12162     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12163     
12164     
12165     if(typeof(this.tpl) == "string"){
12166         this.tpl = new Roo.Template(this.tpl);
12167     } else {
12168         // support xtype ctors..
12169         this.tpl = new Roo.factory(this.tpl, Roo);
12170     }
12171     
12172     
12173     this.tpl.compile();
12174     
12175     /** @private */
12176     this.addEvents({
12177         /**
12178          * @event beforeclick
12179          * Fires before a click is processed. Returns false to cancel the default action.
12180          * @param {Roo.View} this
12181          * @param {Number} index The index of the target node
12182          * @param {HTMLElement} node The target node
12183          * @param {Roo.EventObject} e The raw event object
12184          */
12185             "beforeclick" : true,
12186         /**
12187          * @event click
12188          * Fires when a template node is clicked.
12189          * @param {Roo.View} this
12190          * @param {Number} index The index of the target node
12191          * @param {HTMLElement} node The target node
12192          * @param {Roo.EventObject} e The raw event object
12193          */
12194             "click" : true,
12195         /**
12196          * @event dblclick
12197          * Fires when a template node is double clicked.
12198          * @param {Roo.View} this
12199          * @param {Number} index The index of the target node
12200          * @param {HTMLElement} node The target node
12201          * @param {Roo.EventObject} e The raw event object
12202          */
12203             "dblclick" : true,
12204         /**
12205          * @event contextmenu
12206          * Fires when a template node is right clicked.
12207          * @param {Roo.View} this
12208          * @param {Number} index The index of the target node
12209          * @param {HTMLElement} node The target node
12210          * @param {Roo.EventObject} e The raw event object
12211          */
12212             "contextmenu" : true,
12213         /**
12214          * @event selectionchange
12215          * Fires when the selected nodes change.
12216          * @param {Roo.View} this
12217          * @param {Array} selections Array of the selected nodes
12218          */
12219             "selectionchange" : true,
12220     
12221         /**
12222          * @event beforeselect
12223          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12224          * @param {Roo.View} this
12225          * @param {HTMLElement} node The node to be selected
12226          * @param {Array} selections Array of currently selected nodes
12227          */
12228             "beforeselect" : true,
12229         /**
12230          * @event preparedata
12231          * Fires on every row to render, to allow you to change the data.
12232          * @param {Roo.View} this
12233          * @param {Object} data to be rendered (change this)
12234          */
12235           "preparedata" : true
12236           
12237           
12238         });
12239
12240
12241
12242     this.el.on({
12243         "click": this.onClick,
12244         "dblclick": this.onDblClick,
12245         "contextmenu": this.onContextMenu,
12246         scope:this
12247     });
12248
12249     this.selections = [];
12250     this.nodes = [];
12251     this.cmp = new Roo.CompositeElementLite([]);
12252     if(this.store){
12253         this.store = Roo.factory(this.store, Roo.data);
12254         this.setStore(this.store, true);
12255     }
12256     
12257     if ( this.footer && this.footer.xtype) {
12258            
12259          var fctr = this.wrapEl.appendChild(document.createElement("div"));
12260         
12261         this.footer.dataSource = this.store
12262         this.footer.container = fctr;
12263         this.footer = Roo.factory(this.footer, Roo);
12264         fctr.insertFirst(this.el);
12265         
12266         // this is a bit insane - as the paging toolbar seems to detach the el..
12267 //        dom.parentNode.parentNode.parentNode
12268          // they get detached?
12269     }
12270     
12271     
12272     Roo.View.superclass.constructor.call(this);
12273     
12274     
12275 };
12276
12277 Roo.extend(Roo.View, Roo.util.Observable, {
12278     
12279      /**
12280      * @cfg {Roo.data.Store} store Data store to load data from.
12281      */
12282     store : false,
12283     
12284     /**
12285      * @cfg {String|Roo.Element} el The container element.
12286      */
12287     el : '',
12288     
12289     /**
12290      * @cfg {String|Roo.Template} tpl The template used by this View 
12291      */
12292     tpl : false,
12293     /**
12294      * @cfg {String} dataName the named area of the template to use as the data area
12295      *                          Works with domtemplates roo-name="name"
12296      */
12297     dataName: false,
12298     /**
12299      * @cfg {String} selectedClass The css class to add to selected nodes
12300      */
12301     selectedClass : "x-view-selected",
12302      /**
12303      * @cfg {String} emptyText The empty text to show when nothing is loaded.
12304      */
12305     emptyText : "",
12306     
12307     /**
12308      * @cfg {String} text to display on mask (default Loading)
12309      */
12310     mask : false,
12311     /**
12312      * @cfg {Boolean} multiSelect Allow multiple selection
12313      */
12314     multiSelect : false,
12315     /**
12316      * @cfg {Boolean} singleSelect Allow single selection
12317      */
12318     singleSelect:  false,
12319     
12320     /**
12321      * @cfg {Boolean} toggleSelect - selecting 
12322      */
12323     toggleSelect : false,
12324     
12325     /**
12326      * @cfg {Boolean} tickable - selecting 
12327      */
12328     tickable : false,
12329     
12330     /**
12331      * Returns the element this view is bound to.
12332      * @return {Roo.Element}
12333      */
12334     getEl : function(){
12335         return this.wrapEl;
12336     },
12337     
12338     
12339
12340     /**
12341      * Refreshes the view. - called by datachanged on the store. - do not call directly.
12342      */
12343     refresh : function(){
12344         Roo.log('refresh');
12345         var t = this.tpl;
12346         
12347         // if we are using something like 'domtemplate', then
12348         // the what gets used is:
12349         // t.applySubtemplate(NAME, data, wrapping data..)
12350         // the outer template then get' applied with
12351         //     the store 'extra data'
12352         // and the body get's added to the
12353         //      roo-name="data" node?
12354         //      <span class='roo-tpl-{name}'></span> ?????
12355         
12356         
12357         
12358         this.clearSelections();
12359         this.el.update("");
12360         var html = [];
12361         var records = this.store.getRange();
12362         if(records.length < 1) {
12363             
12364             // is this valid??  = should it render a template??
12365             
12366             this.el.update(this.emptyText);
12367             return;
12368         }
12369         var el = this.el;
12370         if (this.dataName) {
12371             this.el.update(t.apply(this.store.meta)); //????
12372             el = this.el.child('.roo-tpl-' + this.dataName);
12373         }
12374         
12375         for(var i = 0, len = records.length; i < len; i++){
12376             var data = this.prepareData(records[i].data, i, records[i]);
12377             this.fireEvent("preparedata", this, data, i, records[i]);
12378             
12379             var d = Roo.apply({}, data);
12380             
12381             if(this.tickable){
12382                 Roo.apply(d, {'roo-id' : Roo.id()});
12383                 
12384                 var _this = this;
12385             
12386                 Roo.each(this.parent.item, function(item){
12387                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12388                         return;
12389                     }
12390                     Roo.apply(d, {'roo-data-checked' : 'checked'});
12391                 });
12392             }
12393             
12394             html[html.length] = Roo.util.Format.trim(
12395                 this.dataName ?
12396                     t.applySubtemplate(this.dataName, d, this.store.meta) :
12397                     t.apply(d)
12398             );
12399         }
12400         
12401         
12402         
12403         el.update(html.join(""));
12404         this.nodes = el.dom.childNodes;
12405         this.updateIndexes(0);
12406     },
12407     
12408
12409     /**
12410      * Function to override to reformat the data that is sent to
12411      * the template for each node.
12412      * DEPRICATED - use the preparedata event handler.
12413      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12414      * a JSON object for an UpdateManager bound view).
12415      */
12416     prepareData : function(data, index, record)
12417     {
12418         this.fireEvent("preparedata", this, data, index, record);
12419         return data;
12420     },
12421
12422     onUpdate : function(ds, record){
12423          Roo.log('on update');   
12424         this.clearSelections();
12425         var index = this.store.indexOf(record);
12426         var n = this.nodes[index];
12427         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12428         n.parentNode.removeChild(n);
12429         this.updateIndexes(index, index);
12430     },
12431
12432     
12433     
12434 // --------- FIXME     
12435     onAdd : function(ds, records, index)
12436     {
12437         Roo.log(['on Add', ds, records, index] );        
12438         this.clearSelections();
12439         if(this.nodes.length == 0){
12440             this.refresh();
12441             return;
12442         }
12443         var n = this.nodes[index];
12444         for(var i = 0, len = records.length; i < len; i++){
12445             var d = this.prepareData(records[i].data, i, records[i]);
12446             if(n){
12447                 this.tpl.insertBefore(n, d);
12448             }else{
12449                 
12450                 this.tpl.append(this.el, d);
12451             }
12452         }
12453         this.updateIndexes(index);
12454     },
12455
12456     onRemove : function(ds, record, index){
12457         Roo.log('onRemove');
12458         this.clearSelections();
12459         var el = this.dataName  ?
12460             this.el.child('.roo-tpl-' + this.dataName) :
12461             this.el; 
12462         
12463         el.dom.removeChild(this.nodes[index]);
12464         this.updateIndexes(index);
12465     },
12466
12467     /**
12468      * Refresh an individual node.
12469      * @param {Number} index
12470      */
12471     refreshNode : function(index){
12472         this.onUpdate(this.store, this.store.getAt(index));
12473     },
12474
12475     updateIndexes : function(startIndex, endIndex){
12476         var ns = this.nodes;
12477         startIndex = startIndex || 0;
12478         endIndex = endIndex || ns.length - 1;
12479         for(var i = startIndex; i <= endIndex; i++){
12480             ns[i].nodeIndex = i;
12481         }
12482     },
12483
12484     /**
12485      * Changes the data store this view uses and refresh the view.
12486      * @param {Store} store
12487      */
12488     setStore : function(store, initial){
12489         if(!initial && this.store){
12490             this.store.un("datachanged", this.refresh);
12491             this.store.un("add", this.onAdd);
12492             this.store.un("remove", this.onRemove);
12493             this.store.un("update", this.onUpdate);
12494             this.store.un("clear", this.refresh);
12495             this.store.un("beforeload", this.onBeforeLoad);
12496             this.store.un("load", this.onLoad);
12497             this.store.un("loadexception", this.onLoad);
12498         }
12499         if(store){
12500           
12501             store.on("datachanged", this.refresh, this);
12502             store.on("add", this.onAdd, this);
12503             store.on("remove", this.onRemove, this);
12504             store.on("update", this.onUpdate, this);
12505             store.on("clear", this.refresh, this);
12506             store.on("beforeload", this.onBeforeLoad, this);
12507             store.on("load", this.onLoad, this);
12508             store.on("loadexception", this.onLoad, this);
12509         }
12510         
12511         if(store){
12512             this.refresh();
12513         }
12514     },
12515     /**
12516      * onbeforeLoad - masks the loading area.
12517      *
12518      */
12519     onBeforeLoad : function(store,opts)
12520     {
12521          Roo.log('onBeforeLoad');   
12522         if (!opts.add) {
12523             this.el.update("");
12524         }
12525         this.el.mask(this.mask ? this.mask : "Loading" ); 
12526     },
12527     onLoad : function ()
12528     {
12529         this.el.unmask();
12530     },
12531     
12532
12533     /**
12534      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12535      * @param {HTMLElement} node
12536      * @return {HTMLElement} The template node
12537      */
12538     findItemFromChild : function(node){
12539         var el = this.dataName  ?
12540             this.el.child('.roo-tpl-' + this.dataName,true) :
12541             this.el.dom; 
12542         
12543         if(!node || node.parentNode == el){
12544                     return node;
12545             }
12546             var p = node.parentNode;
12547             while(p && p != el){
12548             if(p.parentNode == el){
12549                 return p;
12550             }
12551             p = p.parentNode;
12552         }
12553             return null;
12554     },
12555
12556     /** @ignore */
12557     onClick : function(e){
12558         var item = this.findItemFromChild(e.getTarget());
12559         if(item){
12560             var index = this.indexOf(item);
12561             if(this.onItemClick(item, index, e) !== false){
12562                 this.fireEvent("click", this, index, item, e);
12563             }
12564         }else{
12565             this.clearSelections();
12566         }
12567     },
12568
12569     /** @ignore */
12570     onContextMenu : function(e){
12571         var item = this.findItemFromChild(e.getTarget());
12572         if(item){
12573             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12574         }
12575     },
12576
12577     /** @ignore */
12578     onDblClick : function(e){
12579         var item = this.findItemFromChild(e.getTarget());
12580         if(item){
12581             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12582         }
12583     },
12584
12585     onItemClick : function(item, index, e)
12586     {
12587         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12588             return false;
12589         }
12590         if (this.toggleSelect) {
12591             var m = this.isSelected(item) ? 'unselect' : 'select';
12592             Roo.log(m);
12593             var _t = this;
12594             _t[m](item, true, false);
12595             return true;
12596         }
12597         if(this.multiSelect || this.singleSelect){
12598             if(this.multiSelect && e.shiftKey && this.lastSelection){
12599                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12600             }else{
12601                 this.select(item, this.multiSelect && e.ctrlKey);
12602                 this.lastSelection = item;
12603             }
12604             
12605             if(!this.tickable){
12606                 e.preventDefault();
12607             }
12608             
12609         }
12610         return true;
12611     },
12612
12613     /**
12614      * Get the number of selected nodes.
12615      * @return {Number}
12616      */
12617     getSelectionCount : function(){
12618         return this.selections.length;
12619     },
12620
12621     /**
12622      * Get the currently selected nodes.
12623      * @return {Array} An array of HTMLElements
12624      */
12625     getSelectedNodes : function(){
12626         return this.selections;
12627     },
12628
12629     /**
12630      * Get the indexes of the selected nodes.
12631      * @return {Array}
12632      */
12633     getSelectedIndexes : function(){
12634         var indexes = [], s = this.selections;
12635         for(var i = 0, len = s.length; i < len; i++){
12636             indexes.push(s[i].nodeIndex);
12637         }
12638         return indexes;
12639     },
12640
12641     /**
12642      * Clear all selections
12643      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12644      */
12645     clearSelections : function(suppressEvent){
12646         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12647             this.cmp.elements = this.selections;
12648             this.cmp.removeClass(this.selectedClass);
12649             this.selections = [];
12650             if(!suppressEvent){
12651                 this.fireEvent("selectionchange", this, this.selections);
12652             }
12653         }
12654     },
12655
12656     /**
12657      * Returns true if the passed node is selected
12658      * @param {HTMLElement/Number} node The node or node index
12659      * @return {Boolean}
12660      */
12661     isSelected : function(node){
12662         var s = this.selections;
12663         if(s.length < 1){
12664             return false;
12665         }
12666         node = this.getNode(node);
12667         return s.indexOf(node) !== -1;
12668     },
12669
12670     /**
12671      * Selects nodes.
12672      * @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
12673      * @param {Boolean} keepExisting (optional) true to keep existing selections
12674      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12675      */
12676     select : function(nodeInfo, keepExisting, suppressEvent){
12677         if(nodeInfo instanceof Array){
12678             if(!keepExisting){
12679                 this.clearSelections(true);
12680             }
12681             for(var i = 0, len = nodeInfo.length; i < len; i++){
12682                 this.select(nodeInfo[i], true, true);
12683             }
12684             return;
12685         } 
12686         var node = this.getNode(nodeInfo);
12687         if(!node || this.isSelected(node)){
12688             return; // already selected.
12689         }
12690         if(!keepExisting){
12691             this.clearSelections(true);
12692         }
12693         
12694         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12695             Roo.fly(node).addClass(this.selectedClass);
12696             this.selections.push(node);
12697             if(!suppressEvent){
12698                 this.fireEvent("selectionchange", this, this.selections);
12699             }
12700         }
12701         
12702         
12703     },
12704       /**
12705      * Unselects nodes.
12706      * @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
12707      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12708      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12709      */
12710     unselect : function(nodeInfo, keepExisting, suppressEvent)
12711     {
12712         if(nodeInfo instanceof Array){
12713             Roo.each(this.selections, function(s) {
12714                 this.unselect(s, nodeInfo);
12715             }, this);
12716             return;
12717         }
12718         var node = this.getNode(nodeInfo);
12719         if(!node || !this.isSelected(node)){
12720             Roo.log("not selected");
12721             return; // not selected.
12722         }
12723         // fireevent???
12724         var ns = [];
12725         Roo.each(this.selections, function(s) {
12726             if (s == node ) {
12727                 Roo.fly(node).removeClass(this.selectedClass);
12728
12729                 return;
12730             }
12731             ns.push(s);
12732         },this);
12733         
12734         this.selections= ns;
12735         this.fireEvent("selectionchange", this, this.selections);
12736     },
12737
12738     /**
12739      * Gets a template node.
12740      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12741      * @return {HTMLElement} The node or null if it wasn't found
12742      */
12743     getNode : function(nodeInfo){
12744         if(typeof nodeInfo == "string"){
12745             return document.getElementById(nodeInfo);
12746         }else if(typeof nodeInfo == "number"){
12747             return this.nodes[nodeInfo];
12748         }
12749         return nodeInfo;
12750     },
12751
12752     /**
12753      * Gets a range template nodes.
12754      * @param {Number} startIndex
12755      * @param {Number} endIndex
12756      * @return {Array} An array of nodes
12757      */
12758     getNodes : function(start, end){
12759         var ns = this.nodes;
12760         start = start || 0;
12761         end = typeof end == "undefined" ? ns.length - 1 : end;
12762         var nodes = [];
12763         if(start <= end){
12764             for(var i = start; i <= end; i++){
12765                 nodes.push(ns[i]);
12766             }
12767         } else{
12768             for(var i = start; i >= end; i--){
12769                 nodes.push(ns[i]);
12770             }
12771         }
12772         return nodes;
12773     },
12774
12775     /**
12776      * Finds the index of the passed node
12777      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12778      * @return {Number} The index of the node or -1
12779      */
12780     indexOf : function(node){
12781         node = this.getNode(node);
12782         if(typeof node.nodeIndex == "number"){
12783             return node.nodeIndex;
12784         }
12785         var ns = this.nodes;
12786         for(var i = 0, len = ns.length; i < len; i++){
12787             if(ns[i] == node){
12788                 return i;
12789             }
12790         }
12791         return -1;
12792     }
12793 });
12794 /*
12795  * - LGPL
12796  *
12797  * based on jquery fullcalendar
12798  * 
12799  */
12800
12801 Roo.bootstrap = Roo.bootstrap || {};
12802 /**
12803  * @class Roo.bootstrap.Calendar
12804  * @extends Roo.bootstrap.Component
12805  * Bootstrap Calendar class
12806  * @cfg {Boolean} loadMask (true|false) default false
12807  * @cfg {Object} header generate the user specific header of the calendar, default false
12808
12809  * @constructor
12810  * Create a new Container
12811  * @param {Object} config The config object
12812  */
12813
12814
12815
12816 Roo.bootstrap.Calendar = function(config){
12817     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12818      this.addEvents({
12819         /**
12820              * @event select
12821              * Fires when a date is selected
12822              * @param {DatePicker} this
12823              * @param {Date} date The selected date
12824              */
12825         'select': true,
12826         /**
12827              * @event monthchange
12828              * Fires when the displayed month changes 
12829              * @param {DatePicker} this
12830              * @param {Date} date The selected month
12831              */
12832         'monthchange': true,
12833         /**
12834              * @event evententer
12835              * Fires when mouse over an event
12836              * @param {Calendar} this
12837              * @param {event} Event
12838              */
12839         'evententer': true,
12840         /**
12841              * @event eventleave
12842              * Fires when the mouse leaves an
12843              * @param {Calendar} this
12844              * @param {event}
12845              */
12846         'eventleave': true,
12847         /**
12848              * @event eventclick
12849              * Fires when the mouse click an
12850              * @param {Calendar} this
12851              * @param {event}
12852              */
12853         'eventclick': true
12854         
12855     });
12856
12857 };
12858
12859 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12860     
12861      /**
12862      * @cfg {Number} startDay
12863      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12864      */
12865     startDay : 0,
12866     
12867     loadMask : false,
12868     
12869     header : false,
12870       
12871     getAutoCreate : function(){
12872         
12873         
12874         var fc_button = function(name, corner, style, content ) {
12875             return Roo.apply({},{
12876                 tag : 'span',
12877                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12878                          (corner.length ?
12879                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12880                             ''
12881                         ),
12882                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12883                 unselectable: 'on'
12884             });
12885         };
12886         
12887         var header = {};
12888         
12889         if(!this.header){
12890             header = {
12891                 tag : 'table',
12892                 cls : 'fc-header',
12893                 style : 'width:100%',
12894                 cn : [
12895                     {
12896                         tag: 'tr',
12897                         cn : [
12898                             {
12899                                 tag : 'td',
12900                                 cls : 'fc-header-left',
12901                                 cn : [
12902                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12903                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12904                                     { tag: 'span', cls: 'fc-header-space' },
12905                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12906
12907
12908                                 ]
12909                             },
12910
12911                             {
12912                                 tag : 'td',
12913                                 cls : 'fc-header-center',
12914                                 cn : [
12915                                     {
12916                                         tag: 'span',
12917                                         cls: 'fc-header-title',
12918                                         cn : {
12919                                             tag: 'H2',
12920                                             html : 'month / year'
12921                                         }
12922                                     }
12923
12924                                 ]
12925                             },
12926                             {
12927                                 tag : 'td',
12928                                 cls : 'fc-header-right',
12929                                 cn : [
12930                               /*      fc_button('month', 'left', '', 'month' ),
12931                                     fc_button('week', '', '', 'week' ),
12932                                     fc_button('day', 'right', '', 'day' )
12933                                 */    
12934
12935                                 ]
12936                             }
12937
12938                         ]
12939                     }
12940                 ]
12941             };
12942         }
12943         
12944         header = this.header;
12945         
12946        
12947         var cal_heads = function() {
12948             var ret = [];
12949             // fixme - handle this.
12950             
12951             for (var i =0; i < Date.dayNames.length; i++) {
12952                 var d = Date.dayNames[i];
12953                 ret.push({
12954                     tag: 'th',
12955                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12956                     html : d.substring(0,3)
12957                 });
12958                 
12959             }
12960             ret[0].cls += ' fc-first';
12961             ret[6].cls += ' fc-last';
12962             return ret;
12963         };
12964         var cal_cell = function(n) {
12965             return  {
12966                 tag: 'td',
12967                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12968                 cn : [
12969                     {
12970                         cn : [
12971                             {
12972                                 cls: 'fc-day-number',
12973                                 html: 'D'
12974                             },
12975                             {
12976                                 cls: 'fc-day-content',
12977                              
12978                                 cn : [
12979                                      {
12980                                         style: 'position: relative;' // height: 17px;
12981                                     }
12982                                 ]
12983                             }
12984                             
12985                             
12986                         ]
12987                     }
12988                 ]
12989                 
12990             }
12991         };
12992         var cal_rows = function() {
12993             
12994             var ret = []
12995             for (var r = 0; r < 6; r++) {
12996                 var row= {
12997                     tag : 'tr',
12998                     cls : 'fc-week',
12999                     cn : []
13000                 };
13001                 
13002                 for (var i =0; i < Date.dayNames.length; i++) {
13003                     var d = Date.dayNames[i];
13004                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
13005
13006                 }
13007                 row.cn[0].cls+=' fc-first';
13008                 row.cn[0].cn[0].style = 'min-height:90px';
13009                 row.cn[6].cls+=' fc-last';
13010                 ret.push(row);
13011                 
13012             }
13013             ret[0].cls += ' fc-first';
13014             ret[4].cls += ' fc-prev-last';
13015             ret[5].cls += ' fc-last';
13016             return ret;
13017             
13018         };
13019         
13020         var cal_table = {
13021             tag: 'table',
13022             cls: 'fc-border-separate',
13023             style : 'width:100%',
13024             cellspacing  : 0,
13025             cn : [
13026                 { 
13027                     tag: 'thead',
13028                     cn : [
13029                         { 
13030                             tag: 'tr',
13031                             cls : 'fc-first fc-last',
13032                             cn : cal_heads()
13033                         }
13034                     ]
13035                 },
13036                 { 
13037                     tag: 'tbody',
13038                     cn : cal_rows()
13039                 }
13040                   
13041             ]
13042         };
13043          
13044          var cfg = {
13045             cls : 'fc fc-ltr',
13046             cn : [
13047                 header,
13048                 {
13049                     cls : 'fc-content',
13050                     style : "position: relative;",
13051                     cn : [
13052                         {
13053                             cls : 'fc-view fc-view-month fc-grid',
13054                             style : 'position: relative',
13055                             unselectable : 'on',
13056                             cn : [
13057                                 {
13058                                     cls : 'fc-event-container',
13059                                     style : 'position:absolute;z-index:8;top:0;left:0;'
13060                                 },
13061                                 cal_table
13062                             ]
13063                         }
13064                     ]
13065     
13066                 }
13067            ] 
13068             
13069         };
13070         
13071          
13072         
13073         return cfg;
13074     },
13075     
13076     
13077     initEvents : function()
13078     {
13079         if(!this.store){
13080             throw "can not find store for calendar";
13081         }
13082         
13083         var mark = {
13084             tag: "div",
13085             cls:"x-dlg-mask",
13086             style: "text-align:center",
13087             cn: [
13088                 {
13089                     tag: "div",
13090                     style: "background-color:white;width:50%;margin:250 auto",
13091                     cn: [
13092                         {
13093                             tag: "img",
13094                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
13095                         },
13096                         {
13097                             tag: "span",
13098                             html: "Loading"
13099                         }
13100                         
13101                     ]
13102                 }
13103             ]
13104         }
13105         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13106         
13107         var size = this.el.select('.fc-content', true).first().getSize();
13108         this.maskEl.setSize(size.width, size.height);
13109         this.maskEl.enableDisplayMode("block");
13110         if(!this.loadMask){
13111             this.maskEl.hide();
13112         }
13113         
13114         this.store = Roo.factory(this.store, Roo.data);
13115         this.store.on('load', this.onLoad, this);
13116         this.store.on('beforeload', this.onBeforeLoad, this);
13117         
13118         this.resize();
13119         
13120         this.cells = this.el.select('.fc-day',true);
13121         //Roo.log(this.cells);
13122         this.textNodes = this.el.query('.fc-day-number');
13123         this.cells.addClassOnOver('fc-state-hover');
13124         
13125         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13126         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13127         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13128         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13129         
13130         this.on('monthchange', this.onMonthChange, this);
13131         
13132         this.update(new Date().clearTime());
13133     },
13134     
13135     resize : function() {
13136         var sz  = this.el.getSize();
13137         
13138         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13139         this.el.select('.fc-day-content div',true).setHeight(34);
13140     },
13141     
13142     
13143     // private
13144     showPrevMonth : function(e){
13145         this.update(this.activeDate.add("mo", -1));
13146     },
13147     showToday : function(e){
13148         this.update(new Date().clearTime());
13149     },
13150     // private
13151     showNextMonth : function(e){
13152         this.update(this.activeDate.add("mo", 1));
13153     },
13154
13155     // private
13156     showPrevYear : function(){
13157         this.update(this.activeDate.add("y", -1));
13158     },
13159
13160     // private
13161     showNextYear : function(){
13162         this.update(this.activeDate.add("y", 1));
13163     },
13164
13165     
13166    // private
13167     update : function(date)
13168     {
13169         var vd = this.activeDate;
13170         this.activeDate = date;
13171 //        if(vd && this.el){
13172 //            var t = date.getTime();
13173 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13174 //                Roo.log('using add remove');
13175 //                
13176 //                this.fireEvent('monthchange', this, date);
13177 //                
13178 //                this.cells.removeClass("fc-state-highlight");
13179 //                this.cells.each(function(c){
13180 //                   if(c.dateValue == t){
13181 //                       c.addClass("fc-state-highlight");
13182 //                       setTimeout(function(){
13183 //                            try{c.dom.firstChild.focus();}catch(e){}
13184 //                       }, 50);
13185 //                       return false;
13186 //                   }
13187 //                   return true;
13188 //                });
13189 //                return;
13190 //            }
13191 //        }
13192         
13193         var days = date.getDaysInMonth();
13194         
13195         var firstOfMonth = date.getFirstDateOfMonth();
13196         var startingPos = firstOfMonth.getDay()-this.startDay;
13197         
13198         if(startingPos < this.startDay){
13199             startingPos += 7;
13200         }
13201         
13202         var pm = date.add(Date.MONTH, -1);
13203         var prevStart = pm.getDaysInMonth()-startingPos;
13204 //        
13205         this.cells = this.el.select('.fc-day',true);
13206         this.textNodes = this.el.query('.fc-day-number');
13207         this.cells.addClassOnOver('fc-state-hover');
13208         
13209         var cells = this.cells.elements;
13210         var textEls = this.textNodes;
13211         
13212         Roo.each(cells, function(cell){
13213             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13214         });
13215         
13216         days += startingPos;
13217
13218         // convert everything to numbers so it's fast
13219         var day = 86400000;
13220         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13221         //Roo.log(d);
13222         //Roo.log(pm);
13223         //Roo.log(prevStart);
13224         
13225         var today = new Date().clearTime().getTime();
13226         var sel = date.clearTime().getTime();
13227         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13228         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13229         var ddMatch = this.disabledDatesRE;
13230         var ddText = this.disabledDatesText;
13231         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13232         var ddaysText = this.disabledDaysText;
13233         var format = this.format;
13234         
13235         var setCellClass = function(cal, cell){
13236             cell.row = 0;
13237             cell.events = [];
13238             cell.more = [];
13239             //Roo.log('set Cell Class');
13240             cell.title = "";
13241             var t = d.getTime();
13242             
13243             //Roo.log(d);
13244             
13245             cell.dateValue = t;
13246             if(t == today){
13247                 cell.className += " fc-today";
13248                 cell.className += " fc-state-highlight";
13249                 cell.title = cal.todayText;
13250             }
13251             if(t == sel){
13252                 // disable highlight in other month..
13253                 //cell.className += " fc-state-highlight";
13254                 
13255             }
13256             // disabling
13257             if(t < min) {
13258                 cell.className = " fc-state-disabled";
13259                 cell.title = cal.minText;
13260                 return;
13261             }
13262             if(t > max) {
13263                 cell.className = " fc-state-disabled";
13264                 cell.title = cal.maxText;
13265                 return;
13266             }
13267             if(ddays){
13268                 if(ddays.indexOf(d.getDay()) != -1){
13269                     cell.title = ddaysText;
13270                     cell.className = " fc-state-disabled";
13271                 }
13272             }
13273             if(ddMatch && format){
13274                 var fvalue = d.dateFormat(format);
13275                 if(ddMatch.test(fvalue)){
13276                     cell.title = ddText.replace("%0", fvalue);
13277                     cell.className = " fc-state-disabled";
13278                 }
13279             }
13280             
13281             if (!cell.initialClassName) {
13282                 cell.initialClassName = cell.dom.className;
13283             }
13284             
13285             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
13286         };
13287
13288         var i = 0;
13289         
13290         for(; i < startingPos; i++) {
13291             textEls[i].innerHTML = (++prevStart);
13292             d.setDate(d.getDate()+1);
13293             
13294             cells[i].className = "fc-past fc-other-month";
13295             setCellClass(this, cells[i]);
13296         }
13297         
13298         var intDay = 0;
13299         
13300         for(; i < days; i++){
13301             intDay = i - startingPos + 1;
13302             textEls[i].innerHTML = (intDay);
13303             d.setDate(d.getDate()+1);
13304             
13305             cells[i].className = ''; // "x-date-active";
13306             setCellClass(this, cells[i]);
13307         }
13308         var extraDays = 0;
13309         
13310         for(; i < 42; i++) {
13311             textEls[i].innerHTML = (++extraDays);
13312             d.setDate(d.getDate()+1);
13313             
13314             cells[i].className = "fc-future fc-other-month";
13315             setCellClass(this, cells[i]);
13316         }
13317         
13318         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13319         
13320         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13321         
13322         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13323         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13324         
13325         if(totalRows != 6){
13326             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13327             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13328         }
13329         
13330         this.fireEvent('monthchange', this, date);
13331         
13332         
13333         /*
13334         if(!this.internalRender){
13335             var main = this.el.dom.firstChild;
13336             var w = main.offsetWidth;
13337             this.el.setWidth(w + this.el.getBorderWidth("lr"));
13338             Roo.fly(main).setWidth(w);
13339             this.internalRender = true;
13340             // opera does not respect the auto grow header center column
13341             // then, after it gets a width opera refuses to recalculate
13342             // without a second pass
13343             if(Roo.isOpera && !this.secondPass){
13344                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13345                 this.secondPass = true;
13346                 this.update.defer(10, this, [date]);
13347             }
13348         }
13349         */
13350         
13351     },
13352     
13353     findCell : function(dt) {
13354         dt = dt.clearTime().getTime();
13355         var ret = false;
13356         this.cells.each(function(c){
13357             //Roo.log("check " +c.dateValue + '?=' + dt);
13358             if(c.dateValue == dt){
13359                 ret = c;
13360                 return false;
13361             }
13362             return true;
13363         });
13364         
13365         return ret;
13366     },
13367     
13368     findCells : function(ev) {
13369         var s = ev.start.clone().clearTime().getTime();
13370        // Roo.log(s);
13371         var e= ev.end.clone().clearTime().getTime();
13372        // Roo.log(e);
13373         var ret = [];
13374         this.cells.each(function(c){
13375              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13376             
13377             if(c.dateValue > e){
13378                 return ;
13379             }
13380             if(c.dateValue < s){
13381                 return ;
13382             }
13383             ret.push(c);
13384         });
13385         
13386         return ret;    
13387     },
13388     
13389 //    findBestRow: function(cells)
13390 //    {
13391 //        var ret = 0;
13392 //        
13393 //        for (var i =0 ; i < cells.length;i++) {
13394 //            ret  = Math.max(cells[i].rows || 0,ret);
13395 //        }
13396 //        return ret;
13397 //        
13398 //    },
13399     
13400     
13401     addItem : function(ev)
13402     {
13403         // look for vertical location slot in
13404         var cells = this.findCells(ev);
13405         
13406 //        ev.row = this.findBestRow(cells);
13407         
13408         // work out the location.
13409         
13410         var crow = false;
13411         var rows = [];
13412         for(var i =0; i < cells.length; i++) {
13413             
13414             cells[i].row = cells[0].row;
13415             
13416             if(i == 0){
13417                 cells[i].row = cells[i].row + 1;
13418             }
13419             
13420             if (!crow) {
13421                 crow = {
13422                     start : cells[i],
13423                     end :  cells[i]
13424                 };
13425                 continue;
13426             }
13427             if (crow.start.getY() == cells[i].getY()) {
13428                 // on same row.
13429                 crow.end = cells[i];
13430                 continue;
13431             }
13432             // different row.
13433             rows.push(crow);
13434             crow = {
13435                 start: cells[i],
13436                 end : cells[i]
13437             };
13438             
13439         }
13440         
13441         rows.push(crow);
13442         ev.els = [];
13443         ev.rows = rows;
13444         ev.cells = cells;
13445         
13446         cells[0].events.push(ev);
13447         
13448         this.calevents.push(ev);
13449     },
13450     
13451     clearEvents: function() {
13452         
13453         if(!this.calevents){
13454             return;
13455         }
13456         
13457         Roo.each(this.cells.elements, function(c){
13458             c.row = 0;
13459             c.events = [];
13460             c.more = [];
13461         });
13462         
13463         Roo.each(this.calevents, function(e) {
13464             Roo.each(e.els, function(el) {
13465                 el.un('mouseenter' ,this.onEventEnter, this);
13466                 el.un('mouseleave' ,this.onEventLeave, this);
13467                 el.remove();
13468             },this);
13469         },this);
13470         
13471         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13472             e.remove();
13473         });
13474         
13475     },
13476     
13477     renderEvents: function()
13478     {   
13479         var _this = this;
13480         
13481         this.cells.each(function(c) {
13482             
13483             if(c.row < 5){
13484                 return;
13485             }
13486             
13487             var ev = c.events;
13488             
13489             var r = 4;
13490             if(c.row != c.events.length){
13491                 r = 4 - (4 - (c.row - c.events.length));
13492             }
13493             
13494             c.events = ev.slice(0, r);
13495             c.more = ev.slice(r);
13496             
13497             if(c.more.length && c.more.length == 1){
13498                 c.events.push(c.more.pop());
13499             }
13500             
13501             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13502             
13503         });
13504             
13505         this.cells.each(function(c) {
13506             
13507             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13508             
13509             
13510             for (var e = 0; e < c.events.length; e++){
13511                 var ev = c.events[e];
13512                 var rows = ev.rows;
13513                 
13514                 for(var i = 0; i < rows.length; i++) {
13515                 
13516                     // how many rows should it span..
13517
13518                     var  cfg = {
13519                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13520                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13521
13522                         unselectable : "on",
13523                         cn : [
13524                             {
13525                                 cls: 'fc-event-inner',
13526                                 cn : [
13527     //                                {
13528     //                                  tag:'span',
13529     //                                  cls: 'fc-event-time',
13530     //                                  html : cells.length > 1 ? '' : ev.time
13531     //                                },
13532                                     {
13533                                       tag:'span',
13534                                       cls: 'fc-event-title',
13535                                       html : String.format('{0}', ev.title)
13536                                     }
13537
13538
13539                                 ]
13540                             },
13541                             {
13542                                 cls: 'ui-resizable-handle ui-resizable-e',
13543                                 html : '&nbsp;&nbsp;&nbsp'
13544                             }
13545
13546                         ]
13547                     };
13548
13549                     if (i == 0) {
13550                         cfg.cls += ' fc-event-start';
13551                     }
13552                     if ((i+1) == rows.length) {
13553                         cfg.cls += ' fc-event-end';
13554                     }
13555
13556                     var ctr = _this.el.select('.fc-event-container',true).first();
13557                     var cg = ctr.createChild(cfg);
13558
13559                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13560                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13561
13562                     var r = (c.more.length) ? 1 : 0;
13563                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13564                     cg.setWidth(ebox.right - sbox.x -2);
13565
13566                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13567                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13568                     cg.on('click', _this.onEventClick, _this, ev);
13569
13570                     ev.els.push(cg);
13571                     
13572                 }
13573                 
13574             }
13575             
13576             
13577             if(c.more.length){
13578                 var  cfg = {
13579                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13580                     style : 'position: absolute',
13581                     unselectable : "on",
13582                     cn : [
13583                         {
13584                             cls: 'fc-event-inner',
13585                             cn : [
13586                                 {
13587                                   tag:'span',
13588                                   cls: 'fc-event-title',
13589                                   html : 'More'
13590                                 }
13591
13592
13593                             ]
13594                         },
13595                         {
13596                             cls: 'ui-resizable-handle ui-resizable-e',
13597                             html : '&nbsp;&nbsp;&nbsp'
13598                         }
13599
13600                     ]
13601                 };
13602
13603                 var ctr = _this.el.select('.fc-event-container',true).first();
13604                 var cg = ctr.createChild(cfg);
13605
13606                 var sbox = c.select('.fc-day-content',true).first().getBox();
13607                 var ebox = c.select('.fc-day-content',true).first().getBox();
13608                 //Roo.log(cg);
13609                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13610                 cg.setWidth(ebox.right - sbox.x -2);
13611
13612                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13613                 
13614             }
13615             
13616         });
13617         
13618         
13619         
13620     },
13621     
13622     onEventEnter: function (e, el,event,d) {
13623         this.fireEvent('evententer', this, el, event);
13624     },
13625     
13626     onEventLeave: function (e, el,event,d) {
13627         this.fireEvent('eventleave', this, el, event);
13628     },
13629     
13630     onEventClick: function (e, el,event,d) {
13631         this.fireEvent('eventclick', this, el, event);
13632     },
13633     
13634     onMonthChange: function () {
13635         this.store.load();
13636     },
13637     
13638     onMoreEventClick: function(e, el, more)
13639     {
13640         var _this = this;
13641         
13642         this.calpopover.placement = 'right';
13643         this.calpopover.setTitle('More');
13644         
13645         this.calpopover.setContent('');
13646         
13647         var ctr = this.calpopover.el.select('.popover-content', true).first();
13648         
13649         Roo.each(more, function(m){
13650             var cfg = {
13651                 cls : 'fc-event-hori fc-event-draggable',
13652                 html : m.title
13653             }
13654             var cg = ctr.createChild(cfg);
13655             
13656             cg.on('click', _this.onEventClick, _this, m);
13657         });
13658         
13659         this.calpopover.show(el);
13660         
13661         
13662     },
13663     
13664     onLoad: function () 
13665     {   
13666         this.calevents = [];
13667         var cal = this;
13668         
13669         if(this.store.getCount() > 0){
13670             this.store.data.each(function(d){
13671                cal.addItem({
13672                     id : d.data.id,
13673                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13674                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13675                     time : d.data.start_time,
13676                     title : d.data.title,
13677                     description : d.data.description,
13678                     venue : d.data.venue
13679                 });
13680             });
13681         }
13682         
13683         this.renderEvents();
13684         
13685         if(this.calevents.length && this.loadMask){
13686             this.maskEl.hide();
13687         }
13688     },
13689     
13690     onBeforeLoad: function()
13691     {
13692         this.clearEvents();
13693         if(this.loadMask){
13694             this.maskEl.show();
13695         }
13696     }
13697 });
13698
13699  
13700  /*
13701  * - LGPL
13702  *
13703  * element
13704  * 
13705  */
13706
13707 /**
13708  * @class Roo.bootstrap.Popover
13709  * @extends Roo.bootstrap.Component
13710  * Bootstrap Popover class
13711  * @cfg {String} html contents of the popover   (or false to use children..)
13712  * @cfg {String} title of popover (or false to hide)
13713  * @cfg {String} placement how it is placed
13714  * @cfg {String} trigger click || hover (or false to trigger manually)
13715  * @cfg {String} over what (parent or false to trigger manually.)
13716  * @cfg {Number} delay - delay before showing
13717  
13718  * @constructor
13719  * Create a new Popover
13720  * @param {Object} config The config object
13721  */
13722
13723 Roo.bootstrap.Popover = function(config){
13724     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13725 };
13726
13727 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13728     
13729     title: 'Fill in a title',
13730     html: false,
13731     
13732     placement : 'right',
13733     trigger : 'hover', // hover
13734     
13735     delay : 0,
13736     
13737     over: 'parent',
13738     
13739     can_build_overlaid : false,
13740     
13741     getChildContainer : function()
13742     {
13743         return this.el.select('.popover-content',true).first();
13744     },
13745     
13746     getAutoCreate : function(){
13747          Roo.log('make popover?');
13748         var cfg = {
13749            cls : 'popover roo-dynamic',
13750            style: 'display:block',
13751            cn : [
13752                 {
13753                     cls : 'arrow'
13754                 },
13755                 {
13756                     cls : 'popover-inner',
13757                     cn : [
13758                         {
13759                             tag: 'h3',
13760                             cls: 'popover-title',
13761                             html : this.title
13762                         },
13763                         {
13764                             cls : 'popover-content',
13765                             html : this.html
13766                         }
13767                     ]
13768                     
13769                 }
13770            ]
13771         };
13772         
13773         return cfg;
13774     },
13775     setTitle: function(str)
13776     {
13777         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13778     },
13779     setContent: function(str)
13780     {
13781         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13782     },
13783     // as it get's added to the bottom of the page.
13784     onRender : function(ct, position)
13785     {
13786         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13787         if(!this.el){
13788             var cfg = Roo.apply({},  this.getAutoCreate());
13789             cfg.id = Roo.id();
13790             
13791             if (this.cls) {
13792                 cfg.cls += ' ' + this.cls;
13793             }
13794             if (this.style) {
13795                 cfg.style = this.style;
13796             }
13797             Roo.log("adding to ")
13798             this.el = Roo.get(document.body).createChild(cfg, position);
13799             Roo.log(this.el);
13800         }
13801         this.initEvents();
13802     },
13803     
13804     initEvents : function()
13805     {
13806         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13807         this.el.enableDisplayMode('block');
13808         this.el.hide();
13809         if (this.over === false) {
13810             return; 
13811         }
13812         if (this.triggers === false) {
13813             return;
13814         }
13815         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13816         var triggers = this.trigger ? this.trigger.split(' ') : [];
13817         Roo.each(triggers, function(trigger) {
13818         
13819             if (trigger == 'click') {
13820                 on_el.on('click', this.toggle, this);
13821             } else if (trigger != 'manual') {
13822                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13823                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13824       
13825                 on_el.on(eventIn  ,this.enter, this);
13826                 on_el.on(eventOut, this.leave, this);
13827             }
13828         }, this);
13829         
13830     },
13831     
13832     
13833     // private
13834     timeout : null,
13835     hoverState : null,
13836     
13837     toggle : function () {
13838         this.hoverState == 'in' ? this.leave() : this.enter();
13839     },
13840     
13841     enter : function () {
13842        
13843     
13844         clearTimeout(this.timeout);
13845     
13846         this.hoverState = 'in'
13847     
13848         if (!this.delay || !this.delay.show) {
13849             this.show();
13850             return 
13851         }
13852         var _t = this;
13853         this.timeout = setTimeout(function () {
13854             if (_t.hoverState == 'in') {
13855                 _t.show();
13856             }
13857         }, this.delay.show)
13858     },
13859     leave : function() {
13860         clearTimeout(this.timeout);
13861     
13862         this.hoverState = 'out'
13863     
13864         if (!this.delay || !this.delay.hide) {
13865             this.hide();
13866             return 
13867         }
13868         var _t = this;
13869         this.timeout = setTimeout(function () {
13870             if (_t.hoverState == 'out') {
13871                 _t.hide();
13872             }
13873         }, this.delay.hide)
13874     },
13875     
13876     show : function (on_el)
13877     {
13878         if (!on_el) {
13879             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13880         }
13881         // set content.
13882         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13883         if (this.html !== false) {
13884             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13885         }
13886         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13887         if (!this.title.length) {
13888             this.el.select('.popover-title',true).hide();
13889         }
13890         
13891         var placement = typeof this.placement == 'function' ?
13892             this.placement.call(this, this.el, on_el) :
13893             this.placement;
13894             
13895         var autoToken = /\s?auto?\s?/i;
13896         var autoPlace = autoToken.test(placement);
13897         if (autoPlace) {
13898             placement = placement.replace(autoToken, '') || 'top';
13899         }
13900         
13901         //this.el.detach()
13902         //this.el.setXY([0,0]);
13903         this.el.show();
13904         this.el.dom.style.display='block';
13905         this.el.addClass(placement);
13906         
13907         //this.el.appendTo(on_el);
13908         
13909         var p = this.getPosition();
13910         var box = this.el.getBox();
13911         
13912         if (autoPlace) {
13913             // fixme..
13914         }
13915         var align = Roo.bootstrap.Popover.alignment[placement]
13916         this.el.alignTo(on_el, align[0],align[1]);
13917         //var arrow = this.el.select('.arrow',true).first();
13918         //arrow.set(align[2], 
13919         
13920         this.el.addClass('in');
13921         this.hoverState = null;
13922         
13923         if (this.el.hasClass('fade')) {
13924             // fade it?
13925         }
13926         
13927     },
13928     hide : function()
13929     {
13930         this.el.setXY([0,0]);
13931         this.el.removeClass('in');
13932         this.el.hide();
13933         
13934     }
13935     
13936 });
13937
13938 Roo.bootstrap.Popover.alignment = {
13939     'left' : ['r-l', [-10,0], 'right'],
13940     'right' : ['l-r', [10,0], 'left'],
13941     'bottom' : ['t-b', [0,10], 'top'],
13942     'top' : [ 'b-t', [0,-10], 'bottom']
13943 };
13944
13945  /*
13946  * - LGPL
13947  *
13948  * Progress
13949  * 
13950  */
13951
13952 /**
13953  * @class Roo.bootstrap.Progress
13954  * @extends Roo.bootstrap.Component
13955  * Bootstrap Progress class
13956  * @cfg {Boolean} striped striped of the progress bar
13957  * @cfg {Boolean} active animated of the progress bar
13958  * 
13959  * 
13960  * @constructor
13961  * Create a new Progress
13962  * @param {Object} config The config object
13963  */
13964
13965 Roo.bootstrap.Progress = function(config){
13966     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13967 };
13968
13969 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13970     
13971     striped : false,
13972     active: false,
13973     
13974     getAutoCreate : function(){
13975         var cfg = {
13976             tag: 'div',
13977             cls: 'progress'
13978         };
13979         
13980         
13981         if(this.striped){
13982             cfg.cls += ' progress-striped';
13983         }
13984       
13985         if(this.active){
13986             cfg.cls += ' active';
13987         }
13988         
13989         
13990         return cfg;
13991     }
13992    
13993 });
13994
13995  
13996
13997  /*
13998  * - LGPL
13999  *
14000  * ProgressBar
14001  * 
14002  */
14003
14004 /**
14005  * @class Roo.bootstrap.ProgressBar
14006  * @extends Roo.bootstrap.Component
14007  * Bootstrap ProgressBar class
14008  * @cfg {Number} aria_valuenow aria-value now
14009  * @cfg {Number} aria_valuemin aria-value min
14010  * @cfg {Number} aria_valuemax aria-value max
14011  * @cfg {String} label label for the progress bar
14012  * @cfg {String} panel (success | info | warning | danger )
14013  * @cfg {String} role role of the progress bar
14014  * @cfg {String} sr_only text
14015  * 
14016  * 
14017  * @constructor
14018  * Create a new ProgressBar
14019  * @param {Object} config The config object
14020  */
14021
14022 Roo.bootstrap.ProgressBar = function(config){
14023     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
14024 };
14025
14026 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
14027     
14028     aria_valuenow : 0,
14029     aria_valuemin : 0,
14030     aria_valuemax : 100,
14031     label : false,
14032     panel : false,
14033     role : false,
14034     sr_only: false,
14035     
14036     getAutoCreate : function()
14037     {
14038         
14039         var cfg = {
14040             tag: 'div',
14041             cls: 'progress-bar',
14042             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14043         };
14044         
14045         if(this.sr_only){
14046             cfg.cn = {
14047                 tag: 'span',
14048                 cls: 'sr-only',
14049                 html: this.sr_only
14050             }
14051         }
14052         
14053         if(this.role){
14054             cfg.role = this.role;
14055         }
14056         
14057         if(this.aria_valuenow){
14058             cfg['aria-valuenow'] = this.aria_valuenow;
14059         }
14060         
14061         if(this.aria_valuemin){
14062             cfg['aria-valuemin'] = this.aria_valuemin;
14063         }
14064         
14065         if(this.aria_valuemax){
14066             cfg['aria-valuemax'] = this.aria_valuemax;
14067         }
14068         
14069         if(this.label && !this.sr_only){
14070             cfg.html = this.label;
14071         }
14072         
14073         if(this.panel){
14074             cfg.cls += ' progress-bar-' + this.panel;
14075         }
14076         
14077         return cfg;
14078     },
14079     
14080     update : function(aria_valuenow)
14081     {
14082         this.aria_valuenow = aria_valuenow;
14083         
14084         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14085     }
14086    
14087 });
14088
14089  
14090
14091  /*
14092  * - LGPL
14093  *
14094  * column
14095  * 
14096  */
14097
14098 /**
14099  * @class Roo.bootstrap.TabGroup
14100  * @extends Roo.bootstrap.Column
14101  * Bootstrap Column class
14102  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14103  * @cfg {Boolean} carousel true to make the group behave like a carousel
14104  * 
14105  * @constructor
14106  * Create a new TabGroup
14107  * @param {Object} config The config object
14108  */
14109
14110 Roo.bootstrap.TabGroup = function(config){
14111     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14112     if (!this.navId) {
14113         this.navId = Roo.id();
14114     }
14115     this.tabs = [];
14116     Roo.bootstrap.TabGroup.register(this);
14117     
14118 };
14119
14120 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
14121     
14122     carousel : false,
14123     transition : false,
14124      
14125     getAutoCreate : function()
14126     {
14127         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14128         
14129         cfg.cls += ' tab-content';
14130         
14131         if (this.carousel) {
14132             cfg.cls += ' carousel slide';
14133             cfg.cn = [{
14134                cls : 'carousel-inner'
14135             }]
14136         }
14137         
14138         
14139         return cfg;
14140     },
14141     getChildContainer : function()
14142     {
14143         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14144     },
14145     
14146     /**
14147     * register a Navigation item
14148     * @param {Roo.bootstrap.NavItem} the navitem to add
14149     */
14150     register : function(item)
14151     {
14152         this.tabs.push( item);
14153         item.navId = this.navId; // not really needed..
14154     
14155     },
14156     
14157     getActivePanel : function()
14158     {
14159         var r = false;
14160         Roo.each(this.tabs, function(t) {
14161             if (t.active) {
14162                 r = t;
14163                 return false;
14164             }
14165             return null;
14166         });
14167         return r;
14168         
14169     },
14170     getPanelByName : function(n)
14171     {
14172         var r = false;
14173         Roo.each(this.tabs, function(t) {
14174             if (t.tabId == n) {
14175                 r = t;
14176                 return false;
14177             }
14178             return null;
14179         });
14180         return r;
14181     },
14182     indexOfPanel : function(p)
14183     {
14184         var r = false;
14185         Roo.each(this.tabs, function(t,i) {
14186             if (t.tabId == p.tabId) {
14187                 r = i;
14188                 return false;
14189             }
14190             return null;
14191         });
14192         return r;
14193     },
14194     /**
14195      * show a specific panel
14196      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14197      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14198      */
14199     showPanel : function (pan)
14200     {
14201         
14202         if (typeof(pan) == 'number') {
14203             pan = this.tabs[pan];
14204         }
14205         if (typeof(pan) == 'string') {
14206             pan = this.getPanelByName(pan);
14207         }
14208         if (pan.tabId == this.getActivePanel().tabId) {
14209             return true;
14210         }
14211         var cur = this.getActivePanel();
14212         
14213         if (false === cur.fireEvent('beforedeactivate')) {
14214             return false;
14215         }
14216         
14217         if (this.carousel) {
14218             this.transition = true;
14219             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
14220             var lr = dir == 'next' ? 'left' : 'right';
14221             pan.el.addClass(dir); // or prev
14222             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14223             cur.el.addClass(lr); // or right
14224             pan.el.addClass(lr);
14225             
14226             var _this = this;
14227             cur.el.on('transitionend', function() {
14228                 Roo.log("trans end?");
14229                 
14230                 pan.el.removeClass([lr,dir]);
14231                 pan.setActive(true);
14232                 
14233                 cur.el.removeClass([lr]);
14234                 cur.setActive(false);
14235                 
14236                 _this.transition = false;
14237                 
14238             }, this, { single:  true } );
14239             return true;
14240         }
14241         
14242         cur.setActive(false);
14243         pan.setActive(true);
14244         return true;
14245         
14246     },
14247     showPanelNext : function()
14248     {
14249         var i = this.indexOfPanel(this.getActivePanel());
14250         if (i > this.tabs.length) {
14251             return;
14252         }
14253         this.showPanel(this.tabs[i+1]);
14254     },
14255     showPanelPrev : function()
14256     {
14257         var i = this.indexOfPanel(this.getActivePanel());
14258         if (i  < 1) {
14259             return;
14260         }
14261         this.showPanel(this.tabs[i-1]);
14262     }
14263     
14264     
14265   
14266 });
14267
14268  
14269
14270  
14271  
14272 Roo.apply(Roo.bootstrap.TabGroup, {
14273     
14274     groups: {},
14275      /**
14276     * register a Navigation Group
14277     * @param {Roo.bootstrap.NavGroup} the navgroup to add
14278     */
14279     register : function(navgrp)
14280     {
14281         this.groups[navgrp.navId] = navgrp;
14282         
14283     },
14284     /**
14285     * fetch a Navigation Group based on the navigation ID
14286     * if one does not exist , it will get created.
14287     * @param {string} the navgroup to add
14288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
14289     */
14290     get: function(navId) {
14291         if (typeof(this.groups[navId]) == 'undefined') {
14292             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14293         }
14294         return this.groups[navId] ;
14295     }
14296     
14297     
14298     
14299 });
14300
14301  /*
14302  * - LGPL
14303  *
14304  * TabPanel
14305  * 
14306  */
14307
14308 /**
14309  * @class Roo.bootstrap.TabPanel
14310  * @extends Roo.bootstrap.Component
14311  * Bootstrap TabPanel class
14312  * @cfg {Boolean} active panel active
14313  * @cfg {String} html panel content
14314  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14315  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14316  * 
14317  * 
14318  * @constructor
14319  * Create a new TabPanel
14320  * @param {Object} config The config object
14321  */
14322
14323 Roo.bootstrap.TabPanel = function(config){
14324     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14325     this.addEvents({
14326         /**
14327              * @event changed
14328              * Fires when the active status changes
14329              * @param {Roo.bootstrap.TabPanel} this
14330              * @param {Boolean} state the new state
14331             
14332          */
14333         'changed': true,
14334         /**
14335              * @event beforedeactivate
14336              * Fires before a tab is de-activated - can be used to do validation on a form.
14337              * @param {Roo.bootstrap.TabPanel} this
14338              * @return {Boolean} false if there is an error
14339             
14340          */
14341         'beforedeactivate': true
14342      });
14343     
14344     this.tabId = this.tabId || Roo.id();
14345   
14346 };
14347
14348 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
14349     
14350     active: false,
14351     html: false,
14352     tabId: false,
14353     navId : false,
14354     
14355     getAutoCreate : function(){
14356         var cfg = {
14357             tag: 'div',
14358             // item is needed for carousel - not sure if it has any effect otherwise
14359             cls: 'tab-pane item',
14360             html: this.html || ''
14361         };
14362         
14363         if(this.active){
14364             cfg.cls += ' active';
14365         }
14366         
14367         if(this.tabId){
14368             cfg.tabId = this.tabId;
14369         }
14370         
14371         
14372         return cfg;
14373     },
14374     
14375     initEvents:  function()
14376     {
14377         Roo.log('-------- init events on tab panel ---------');
14378         
14379         var p = this.parent();
14380         this.navId = this.navId || p.navId;
14381         
14382         if (typeof(this.navId) != 'undefined') {
14383             // not really needed.. but just in case.. parent should be a NavGroup.
14384             var tg = Roo.bootstrap.TabGroup.get(this.navId);
14385             Roo.log(['register', tg, this]);
14386             tg.register(this);
14387         }
14388     },
14389     
14390     
14391     onRender : function(ct, position)
14392     {
14393        // Roo.log("Call onRender: " + this.xtype);
14394         
14395         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14396         
14397         
14398         
14399         
14400         
14401     },
14402     
14403     setActive: function(state)
14404     {
14405         Roo.log("panel - set active " + this.tabId + "=" + state);
14406         
14407         this.active = state;
14408         if (!state) {
14409             this.el.removeClass('active');
14410             
14411         } else  if (!this.el.hasClass('active')) {
14412             this.el.addClass('active');
14413         }
14414         this.fireEvent('changed', this, state);
14415     }
14416     
14417     
14418 });
14419  
14420
14421  
14422
14423  /*
14424  * - LGPL
14425  *
14426  * DateField
14427  * 
14428  */
14429
14430 /**
14431  * @class Roo.bootstrap.DateField
14432  * @extends Roo.bootstrap.Input
14433  * Bootstrap DateField class
14434  * @cfg {Number} weekStart default 0
14435  * @cfg {String} viewMode default empty, (months|years)
14436  * @cfg {String} minViewMode default empty, (months|years)
14437  * @cfg {Number} startDate default -Infinity
14438  * @cfg {Number} endDate default Infinity
14439  * @cfg {Boolean} todayHighlight default false
14440  * @cfg {Boolean} todayBtn default false
14441  * @cfg {Boolean} calendarWeeks default false
14442  * @cfg {Object} daysOfWeekDisabled default empty
14443  * @cfg {Boolean} singleMode default false (true | false)
14444  * 
14445  * @cfg {Boolean} keyboardNavigation default true
14446  * @cfg {String} language default en
14447  * 
14448  * @constructor
14449  * Create a new DateField
14450  * @param {Object} config The config object
14451  */
14452
14453 Roo.bootstrap.DateField = function(config){
14454     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14455      this.addEvents({
14456             /**
14457              * @event show
14458              * Fires when this field show.
14459              * @param {Roo.bootstrap.DateField} this
14460              * @param {Mixed} date The date value
14461              */
14462             show : true,
14463             /**
14464              * @event show
14465              * Fires when this field hide.
14466              * @param {Roo.bootstrap.DateField} this
14467              * @param {Mixed} date The date value
14468              */
14469             hide : true,
14470             /**
14471              * @event select
14472              * Fires when select a date.
14473              * @param {Roo.bootstrap.DateField} this
14474              * @param {Mixed} date The date value
14475              */
14476             select : true
14477         });
14478 };
14479
14480 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
14481     
14482     /**
14483      * @cfg {String} format
14484      * The default date format string which can be overriden for localization support.  The format must be
14485      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14486      */
14487     format : "m/d/y",
14488     /**
14489      * @cfg {String} altFormats
14490      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14491      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14492      */
14493     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14494     
14495     weekStart : 0,
14496     
14497     viewMode : '',
14498     
14499     minViewMode : '',
14500     
14501     todayHighlight : false,
14502     
14503     todayBtn: false,
14504     
14505     language: 'en',
14506     
14507     keyboardNavigation: true,
14508     
14509     calendarWeeks: false,
14510     
14511     startDate: -Infinity,
14512     
14513     endDate: Infinity,
14514     
14515     daysOfWeekDisabled: [],
14516     
14517     _events: [],
14518     
14519     singleMode : false,
14520     
14521     UTCDate: function()
14522     {
14523         return new Date(Date.UTC.apply(Date, arguments));
14524     },
14525     
14526     UTCToday: function()
14527     {
14528         var today = new Date();
14529         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14530     },
14531     
14532     getDate: function() {
14533             var d = this.getUTCDate();
14534             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14535     },
14536     
14537     getUTCDate: function() {
14538             return this.date;
14539     },
14540     
14541     setDate: function(d) {
14542             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14543     },
14544     
14545     setUTCDate: function(d) {
14546             this.date = d;
14547             this.setValue(this.formatDate(this.date));
14548     },
14549         
14550     onRender: function(ct, position)
14551     {
14552         
14553         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14554         
14555         this.language = this.language || 'en';
14556         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14557         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14558         
14559         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14560         this.format = this.format || 'm/d/y';
14561         this.isInline = false;
14562         this.isInput = true;
14563         this.component = this.el.select('.add-on', true).first() || false;
14564         this.component = (this.component && this.component.length === 0) ? false : this.component;
14565         this.hasInput = this.component && this.inputEL().length;
14566         
14567         if (typeof(this.minViewMode === 'string')) {
14568             switch (this.minViewMode) {
14569                 case 'months':
14570                     this.minViewMode = 1;
14571                     break;
14572                 case 'years':
14573                     this.minViewMode = 2;
14574                     break;
14575                 default:
14576                     this.minViewMode = 0;
14577                     break;
14578             }
14579         }
14580         
14581         if (typeof(this.viewMode === 'string')) {
14582             switch (this.viewMode) {
14583                 case 'months':
14584                     this.viewMode = 1;
14585                     break;
14586                 case 'years':
14587                     this.viewMode = 2;
14588                     break;
14589                 default:
14590                     this.viewMode = 0;
14591                     break;
14592             }
14593         }
14594                 
14595         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14596         
14597 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14598         
14599         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14600         
14601         this.picker().on('mousedown', this.onMousedown, this);
14602         this.picker().on('click', this.onClick, this);
14603         
14604         this.picker().addClass('datepicker-dropdown');
14605         
14606         this.startViewMode = this.viewMode;
14607         
14608         if(this.singleMode){
14609             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14610                 v.setVisibilityMode(Roo.Element.DISPLAY)
14611                 v.hide();
14612             })
14613             
14614             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14615                 v.setStyle('width', '189px');
14616             });
14617         }
14618         
14619         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14620             if(!this.calendarWeeks){
14621                 v.remove();
14622                 return;
14623             };
14624             
14625             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14626             v.attr('colspan', function(i, val){
14627                 return parseInt(val) + 1;
14628             });
14629         })
14630                         
14631         
14632         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14633         
14634         this.setStartDate(this.startDate);
14635         this.setEndDate(this.endDate);
14636         
14637         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14638         
14639         this.fillDow();
14640         this.fillMonths();
14641         this.update();
14642         this.showMode();
14643         
14644         if(this.isInline) {
14645             this.show();
14646         }
14647     },
14648     
14649     picker : function()
14650     {
14651         return this.pickerEl;
14652 //        return this.el.select('.datepicker', true).first();
14653     },
14654     
14655     fillDow: function()
14656     {
14657         var dowCnt = this.weekStart;
14658         
14659         var dow = {
14660             tag: 'tr',
14661             cn: [
14662                 
14663             ]
14664         };
14665         
14666         if(this.calendarWeeks){
14667             dow.cn.push({
14668                 tag: 'th',
14669                 cls: 'cw',
14670                 html: '&nbsp;'
14671             })
14672         }
14673         
14674         while (dowCnt < this.weekStart + 7) {
14675             dow.cn.push({
14676                 tag: 'th',
14677                 cls: 'dow',
14678                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14679             });
14680         }
14681         
14682         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14683     },
14684     
14685     fillMonths: function()
14686     {    
14687         var i = 0
14688         var months = this.picker().select('>.datepicker-months td', true).first();
14689         
14690         months.dom.innerHTML = '';
14691         
14692         while (i < 12) {
14693             var month = {
14694                 tag: 'span',
14695                 cls: 'month',
14696                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14697             }
14698             
14699             months.createChild(month);
14700         }
14701         
14702     },
14703     
14704     update: function()
14705     {
14706         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
14707         
14708         if (this.date < this.startDate) {
14709             this.viewDate = new Date(this.startDate);
14710         } else if (this.date > this.endDate) {
14711             this.viewDate = new Date(this.endDate);
14712         } else {
14713             this.viewDate = new Date(this.date);
14714         }
14715         
14716         this.fill();
14717     },
14718     
14719     fill: function() 
14720     {
14721         var d = new Date(this.viewDate),
14722                 year = d.getUTCFullYear(),
14723                 month = d.getUTCMonth(),
14724                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14725                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14726                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14727                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14728                 currentDate = this.date && this.date.valueOf(),
14729                 today = this.UTCToday();
14730         
14731         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14732         
14733 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14734         
14735 //        this.picker.select('>tfoot th.today').
14736 //                                              .text(dates[this.language].today)
14737 //                                              .toggle(this.todayBtn !== false);
14738     
14739         this.updateNavArrows();
14740         this.fillMonths();
14741                                                 
14742         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14743         
14744         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14745          
14746         prevMonth.setUTCDate(day);
14747         
14748         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14749         
14750         var nextMonth = new Date(prevMonth);
14751         
14752         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14753         
14754         nextMonth = nextMonth.valueOf();
14755         
14756         var fillMonths = false;
14757         
14758         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14759         
14760         while(prevMonth.valueOf() < nextMonth) {
14761             var clsName = '';
14762             
14763             if (prevMonth.getUTCDay() === this.weekStart) {
14764                 if(fillMonths){
14765                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14766                 }
14767                     
14768                 fillMonths = {
14769                     tag: 'tr',
14770                     cn: []
14771                 };
14772                 
14773                 if(this.calendarWeeks){
14774                     // ISO 8601: First week contains first thursday.
14775                     // ISO also states week starts on Monday, but we can be more abstract here.
14776                     var
14777                     // Start of current week: based on weekstart/current date
14778                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14779                     // Thursday of this week
14780                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14781                     // First Thursday of year, year from thursday
14782                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14783                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14784                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14785                     
14786                     fillMonths.cn.push({
14787                         tag: 'td',
14788                         cls: 'cw',
14789                         html: calWeek
14790                     });
14791                 }
14792             }
14793             
14794             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14795                 clsName += ' old';
14796             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14797                 clsName += ' new';
14798             }
14799             if (this.todayHighlight &&
14800                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14801                 prevMonth.getUTCMonth() == today.getMonth() &&
14802                 prevMonth.getUTCDate() == today.getDate()) {
14803                 clsName += ' today';
14804             }
14805             
14806             if (currentDate && prevMonth.valueOf() === currentDate) {
14807                 clsName += ' active';
14808             }
14809             
14810             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14811                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14812                     clsName += ' disabled';
14813             }
14814             
14815             fillMonths.cn.push({
14816                 tag: 'td',
14817                 cls: 'day ' + clsName,
14818                 html: prevMonth.getDate()
14819             })
14820             
14821             prevMonth.setDate(prevMonth.getDate()+1);
14822         }
14823           
14824         var currentYear = this.date && this.date.getUTCFullYear();
14825         var currentMonth = this.date && this.date.getUTCMonth();
14826         
14827         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14828         
14829         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14830             v.removeClass('active');
14831             
14832             if(currentYear === year && k === currentMonth){
14833                 v.addClass('active');
14834             }
14835             
14836             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14837                 v.addClass('disabled');
14838             }
14839             
14840         });
14841         
14842         
14843         year = parseInt(year/10, 10) * 10;
14844         
14845         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14846         
14847         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14848         
14849         year -= 1;
14850         for (var i = -1; i < 11; i++) {
14851             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14852                 tag: 'span',
14853                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14854                 html: year
14855             })
14856             
14857             year += 1;
14858         }
14859     },
14860     
14861     showMode: function(dir) 
14862     {
14863         if (dir) {
14864             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14865         }
14866         
14867         Roo.each(this.picker().select('>div',true).elements, function(v){
14868             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14869             v.hide();
14870         });
14871         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14872     },
14873     
14874     place: function()
14875     {
14876         if(this.isInline) return;
14877         
14878         this.picker().removeClass(['bottom', 'top']);
14879         
14880         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14881             /*
14882              * place to the top of element!
14883              *
14884              */
14885             
14886             this.picker().addClass('top');
14887             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14888             
14889             return;
14890         }
14891         
14892         this.picker().addClass('bottom');
14893         
14894         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14895     },
14896     
14897     parseDate : function(value)
14898     {
14899         if(!value || value instanceof Date){
14900             return value;
14901         }
14902         var v = Date.parseDate(value, this.format);
14903         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14904             v = Date.parseDate(value, 'Y-m-d');
14905         }
14906         if(!v && this.altFormats){
14907             if(!this.altFormatsArray){
14908                 this.altFormatsArray = this.altFormats.split("|");
14909             }
14910             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14911                 v = Date.parseDate(value, this.altFormatsArray[i]);
14912             }
14913         }
14914         return v;
14915     },
14916     
14917     formatDate : function(date, fmt)
14918     {   
14919         return (!date || !(date instanceof Date)) ?
14920         date : date.dateFormat(fmt || this.format);
14921     },
14922     
14923     onFocus : function()
14924     {
14925         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14926         this.show();
14927     },
14928     
14929     onBlur : function()
14930     {
14931         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14932         
14933         var d = this.inputEl().getValue();
14934         
14935         this.setValue(d);
14936                 
14937         this.hide();
14938     },
14939     
14940     show : function()
14941     {
14942         this.picker().show();
14943         this.update();
14944         this.place();
14945         
14946         this.fireEvent('show', this, this.date);
14947     },
14948     
14949     hide : function()
14950     {
14951         if(this.isInline) return;
14952         this.picker().hide();
14953         this.viewMode = this.startViewMode;
14954         this.showMode();
14955         
14956         this.fireEvent('hide', this, this.date);
14957         
14958     },
14959     
14960     onMousedown: function(e)
14961     {
14962         e.stopPropagation();
14963         e.preventDefault();
14964     },
14965     
14966     keyup: function(e)
14967     {
14968         Roo.bootstrap.DateField.superclass.keyup.call(this);
14969         this.update();
14970     },
14971
14972     setValue: function(v)
14973     {
14974         
14975         // v can be a string or a date..
14976         
14977         
14978         var d = new Date(this.parseDate(v) ).clearTime();
14979         
14980         if(isNaN(d.getTime())){
14981             this.date = this.viewDate = '';
14982             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14983             return;
14984         }
14985         
14986         v = this.formatDate(d);
14987         
14988         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14989         
14990         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14991      
14992         this.update();
14993
14994         this.fireEvent('select', this, this.date);
14995         
14996     },
14997     
14998     getValue: function()
14999     {
15000         return this.formatDate(this.date);
15001     },
15002     
15003     fireKey: function(e)
15004     {
15005         if (!this.picker().isVisible()){
15006             if (e.keyCode == 27) // allow escape to hide and re-show picker
15007                 this.show();
15008             return;
15009         }
15010         
15011         var dateChanged = false,
15012         dir, day, month,
15013         newDate, newViewDate;
15014         
15015         switch(e.keyCode){
15016             case 27: // escape
15017                 this.hide();
15018                 e.preventDefault();
15019                 break;
15020             case 37: // left
15021             case 39: // right
15022                 if (!this.keyboardNavigation) break;
15023                 dir = e.keyCode == 37 ? -1 : 1;
15024                 
15025                 if (e.ctrlKey){
15026                     newDate = this.moveYear(this.date, dir);
15027                     newViewDate = this.moveYear(this.viewDate, dir);
15028                 } else if (e.shiftKey){
15029                     newDate = this.moveMonth(this.date, dir);
15030                     newViewDate = this.moveMonth(this.viewDate, dir);
15031                 } else {
15032                     newDate = new Date(this.date);
15033                     newDate.setUTCDate(this.date.getUTCDate() + dir);
15034                     newViewDate = new Date(this.viewDate);
15035                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15036                 }
15037                 if (this.dateWithinRange(newDate)){
15038                     this.date = newDate;
15039                     this.viewDate = newViewDate;
15040                     this.setValue(this.formatDate(this.date));
15041 //                    this.update();
15042                     e.preventDefault();
15043                     dateChanged = true;
15044                 }
15045                 break;
15046             case 38: // up
15047             case 40: // down
15048                 if (!this.keyboardNavigation) break;
15049                 dir = e.keyCode == 38 ? -1 : 1;
15050                 if (e.ctrlKey){
15051                     newDate = this.moveYear(this.date, dir);
15052                     newViewDate = this.moveYear(this.viewDate, dir);
15053                 } else if (e.shiftKey){
15054                     newDate = this.moveMonth(this.date, dir);
15055                     newViewDate = this.moveMonth(this.viewDate, dir);
15056                 } else {
15057                     newDate = new Date(this.date);
15058                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15059                     newViewDate = new Date(this.viewDate);
15060                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15061                 }
15062                 if (this.dateWithinRange(newDate)){
15063                     this.date = newDate;
15064                     this.viewDate = newViewDate;
15065                     this.setValue(this.formatDate(this.date));
15066 //                    this.update();
15067                     e.preventDefault();
15068                     dateChanged = true;
15069                 }
15070                 break;
15071             case 13: // enter
15072                 this.setValue(this.formatDate(this.date));
15073                 this.hide();
15074                 e.preventDefault();
15075                 break;
15076             case 9: // tab
15077                 this.setValue(this.formatDate(this.date));
15078                 this.hide();
15079                 break;
15080             case 16: // shift
15081             case 17: // ctrl
15082             case 18: // alt
15083                 break;
15084             default :
15085                 this.hide();
15086                 
15087         }
15088     },
15089     
15090     
15091     onClick: function(e) 
15092     {
15093         e.stopPropagation();
15094         e.preventDefault();
15095         
15096         var target = e.getTarget();
15097         
15098         if(target.nodeName.toLowerCase() === 'i'){
15099             target = Roo.get(target).dom.parentNode;
15100         }
15101         
15102         var nodeName = target.nodeName;
15103         var className = target.className;
15104         var html = target.innerHTML;
15105         //Roo.log(nodeName);
15106         
15107         switch(nodeName.toLowerCase()) {
15108             case 'th':
15109                 switch(className) {
15110                     case 'switch':
15111                         this.showMode(1);
15112                         break;
15113                     case 'prev':
15114                     case 'next':
15115                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15116                         switch(this.viewMode){
15117                                 case 0:
15118                                         this.viewDate = this.moveMonth(this.viewDate, dir);
15119                                         break;
15120                                 case 1:
15121                                 case 2:
15122                                         this.viewDate = this.moveYear(this.viewDate, dir);
15123                                         break;
15124                         }
15125                         this.fill();
15126                         break;
15127                     case 'today':
15128                         var date = new Date();
15129                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15130 //                        this.fill()
15131                         this.setValue(this.formatDate(this.date));
15132                         
15133                         this.hide();
15134                         break;
15135                 }
15136                 break;
15137             case 'span':
15138                 if (className.indexOf('disabled') < 0) {
15139                     this.viewDate.setUTCDate(1);
15140                     if (className.indexOf('month') > -1) {
15141                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15142                     } else {
15143                         var year = parseInt(html, 10) || 0;
15144                         this.viewDate.setUTCFullYear(year);
15145                         
15146                     }
15147                     
15148                     if(this.singleMode){
15149                         this.setValue(this.formatDate(this.viewDate));
15150                         this.hide();
15151                         return;
15152                     }
15153                     
15154                     this.showMode(-1);
15155                     this.fill();
15156                 }
15157                 break;
15158                 
15159             case 'td':
15160                 //Roo.log(className);
15161                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15162                     var day = parseInt(html, 10) || 1;
15163                     var year = this.viewDate.getUTCFullYear(),
15164                         month = this.viewDate.getUTCMonth();
15165
15166                     if (className.indexOf('old') > -1) {
15167                         if(month === 0 ){
15168                             month = 11;
15169                             year -= 1;
15170                         }else{
15171                             month -= 1;
15172                         }
15173                     } else if (className.indexOf('new') > -1) {
15174                         if (month == 11) {
15175                             month = 0;
15176                             year += 1;
15177                         } else {
15178                             month += 1;
15179                         }
15180                     }
15181                     //Roo.log([year,month,day]);
15182                     this.date = this.UTCDate(year, month, day,0,0,0,0);
15183                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15184 //                    this.fill();
15185                     //Roo.log(this.formatDate(this.date));
15186                     this.setValue(this.formatDate(this.date));
15187                     this.hide();
15188                 }
15189                 break;
15190         }
15191     },
15192     
15193     setStartDate: function(startDate)
15194     {
15195         this.startDate = startDate || -Infinity;
15196         if (this.startDate !== -Infinity) {
15197             this.startDate = this.parseDate(this.startDate);
15198         }
15199         this.update();
15200         this.updateNavArrows();
15201     },
15202
15203     setEndDate: function(endDate)
15204     {
15205         this.endDate = endDate || Infinity;
15206         if (this.endDate !== Infinity) {
15207             this.endDate = this.parseDate(this.endDate);
15208         }
15209         this.update();
15210         this.updateNavArrows();
15211     },
15212     
15213     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15214     {
15215         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15216         if (typeof(this.daysOfWeekDisabled) !== 'object') {
15217             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15218         }
15219         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15220             return parseInt(d, 10);
15221         });
15222         this.update();
15223         this.updateNavArrows();
15224     },
15225     
15226     updateNavArrows: function() 
15227     {
15228         if(this.singleMode){
15229             return;
15230         }
15231         
15232         var d = new Date(this.viewDate),
15233         year = d.getUTCFullYear(),
15234         month = d.getUTCMonth();
15235         
15236         Roo.each(this.picker().select('.prev', true).elements, function(v){
15237             v.show();
15238             switch (this.viewMode) {
15239                 case 0:
15240
15241                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15242                         v.hide();
15243                     }
15244                     break;
15245                 case 1:
15246                 case 2:
15247                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15248                         v.hide();
15249                     }
15250                     break;
15251             }
15252         });
15253         
15254         Roo.each(this.picker().select('.next', true).elements, function(v){
15255             v.show();
15256             switch (this.viewMode) {
15257                 case 0:
15258
15259                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15260                         v.hide();
15261                     }
15262                     break;
15263                 case 1:
15264                 case 2:
15265                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15266                         v.hide();
15267                     }
15268                     break;
15269             }
15270         })
15271     },
15272     
15273     moveMonth: function(date, dir)
15274     {
15275         if (!dir) return date;
15276         var new_date = new Date(date.valueOf()),
15277         day = new_date.getUTCDate(),
15278         month = new_date.getUTCMonth(),
15279         mag = Math.abs(dir),
15280         new_month, test;
15281         dir = dir > 0 ? 1 : -1;
15282         if (mag == 1){
15283             test = dir == -1
15284             // If going back one month, make sure month is not current month
15285             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15286             ? function(){
15287                 return new_date.getUTCMonth() == month;
15288             }
15289             // If going forward one month, make sure month is as expected
15290             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15291             : function(){
15292                 return new_date.getUTCMonth() != new_month;
15293             };
15294             new_month = month + dir;
15295             new_date.setUTCMonth(new_month);
15296             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15297             if (new_month < 0 || new_month > 11)
15298                 new_month = (new_month + 12) % 12;
15299         } else {
15300             // For magnitudes >1, move one month at a time...
15301             for (var i=0; i<mag; i++)
15302                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15303                 new_date = this.moveMonth(new_date, dir);
15304             // ...then reset the day, keeping it in the new month
15305             new_month = new_date.getUTCMonth();
15306             new_date.setUTCDate(day);
15307             test = function(){
15308                 return new_month != new_date.getUTCMonth();
15309             };
15310         }
15311         // Common date-resetting loop -- if date is beyond end of month, make it
15312         // end of month
15313         while (test()){
15314             new_date.setUTCDate(--day);
15315             new_date.setUTCMonth(new_month);
15316         }
15317         return new_date;
15318     },
15319
15320     moveYear: function(date, dir)
15321     {
15322         return this.moveMonth(date, dir*12);
15323     },
15324
15325     dateWithinRange: function(date)
15326     {
15327         return date >= this.startDate && date <= this.endDate;
15328     },
15329
15330     
15331     remove: function() 
15332     {
15333         this.picker().remove();
15334     }
15335    
15336 });
15337
15338 Roo.apply(Roo.bootstrap.DateField,  {
15339     
15340     head : {
15341         tag: 'thead',
15342         cn: [
15343         {
15344             tag: 'tr',
15345             cn: [
15346             {
15347                 tag: 'th',
15348                 cls: 'prev',
15349                 html: '<i class="fa fa-arrow-left"/>'
15350             },
15351             {
15352                 tag: 'th',
15353                 cls: 'switch',
15354                 colspan: '5'
15355             },
15356             {
15357                 tag: 'th',
15358                 cls: 'next',
15359                 html: '<i class="fa fa-arrow-right"/>'
15360             }
15361
15362             ]
15363         }
15364         ]
15365     },
15366     
15367     content : {
15368         tag: 'tbody',
15369         cn: [
15370         {
15371             tag: 'tr',
15372             cn: [
15373             {
15374                 tag: 'td',
15375                 colspan: '7'
15376             }
15377             ]
15378         }
15379         ]
15380     },
15381     
15382     footer : {
15383         tag: 'tfoot',
15384         cn: [
15385         {
15386             tag: 'tr',
15387             cn: [
15388             {
15389                 tag: 'th',
15390                 colspan: '7',
15391                 cls: 'today'
15392             }
15393                     
15394             ]
15395         }
15396         ]
15397     },
15398     
15399     dates:{
15400         en: {
15401             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15402             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15403             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15404             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15405             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15406             today: "Today"
15407         }
15408     },
15409     
15410     modes: [
15411     {
15412         clsName: 'days',
15413         navFnc: 'Month',
15414         navStep: 1
15415     },
15416     {
15417         clsName: 'months',
15418         navFnc: 'FullYear',
15419         navStep: 1
15420     },
15421     {
15422         clsName: 'years',
15423         navFnc: 'FullYear',
15424         navStep: 10
15425     }]
15426 });
15427
15428 Roo.apply(Roo.bootstrap.DateField,  {
15429   
15430     template : {
15431         tag: 'div',
15432         cls: 'datepicker dropdown-menu roo-dynamic',
15433         cn: [
15434         {
15435             tag: 'div',
15436             cls: 'datepicker-days',
15437             cn: [
15438             {
15439                 tag: 'table',
15440                 cls: 'table-condensed',
15441                 cn:[
15442                 Roo.bootstrap.DateField.head,
15443                 {
15444                     tag: 'tbody'
15445                 },
15446                 Roo.bootstrap.DateField.footer
15447                 ]
15448             }
15449             ]
15450         },
15451         {
15452             tag: 'div',
15453             cls: 'datepicker-months',
15454             cn: [
15455             {
15456                 tag: 'table',
15457                 cls: 'table-condensed',
15458                 cn:[
15459                 Roo.bootstrap.DateField.head,
15460                 Roo.bootstrap.DateField.content,
15461                 Roo.bootstrap.DateField.footer
15462                 ]
15463             }
15464             ]
15465         },
15466         {
15467             tag: 'div',
15468             cls: 'datepicker-years',
15469             cn: [
15470             {
15471                 tag: 'table',
15472                 cls: 'table-condensed',
15473                 cn:[
15474                 Roo.bootstrap.DateField.head,
15475                 Roo.bootstrap.DateField.content,
15476                 Roo.bootstrap.DateField.footer
15477                 ]
15478             }
15479             ]
15480         }
15481         ]
15482     }
15483 });
15484
15485  
15486
15487  /*
15488  * - LGPL
15489  *
15490  * TimeField
15491  * 
15492  */
15493
15494 /**
15495  * @class Roo.bootstrap.TimeField
15496  * @extends Roo.bootstrap.Input
15497  * Bootstrap DateField class
15498  * 
15499  * 
15500  * @constructor
15501  * Create a new TimeField
15502  * @param {Object} config The config object
15503  */
15504
15505 Roo.bootstrap.TimeField = function(config){
15506     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15507     this.addEvents({
15508             /**
15509              * @event show
15510              * Fires when this field show.
15511              * @param {Roo.bootstrap.DateField} this
15512              * @param {Mixed} date The date value
15513              */
15514             show : true,
15515             /**
15516              * @event show
15517              * Fires when this field hide.
15518              * @param {Roo.bootstrap.DateField} this
15519              * @param {Mixed} date The date value
15520              */
15521             hide : true,
15522             /**
15523              * @event select
15524              * Fires when select a date.
15525              * @param {Roo.bootstrap.DateField} this
15526              * @param {Mixed} date The date value
15527              */
15528             select : true
15529         });
15530 };
15531
15532 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
15533     
15534     /**
15535      * @cfg {String} format
15536      * The default time format string which can be overriden for localization support.  The format must be
15537      * valid according to {@link Date#parseDate} (defaults to 'H:i').
15538      */
15539     format : "H:i",
15540        
15541     onRender: function(ct, position)
15542     {
15543         
15544         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15545                 
15546         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15547         
15548         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15549         
15550         this.pop = this.picker().select('>.datepicker-time',true).first();
15551         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
15552         
15553         this.picker().on('mousedown', this.onMousedown, this);
15554         this.picker().on('click', this.onClick, this);
15555         
15556         this.picker().addClass('datepicker-dropdown');
15557     
15558         this.fillTime();
15559         this.update();
15560             
15561         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15562         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15563         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15564         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15565         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15566         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15567
15568     },
15569     
15570     fireKey: function(e){
15571         if (!this.picker().isVisible()){
15572             if (e.keyCode == 27) // allow escape to hide and re-show picker
15573                 this.show();
15574             return;
15575         }
15576
15577         e.preventDefault();
15578         
15579         switch(e.keyCode){
15580             case 27: // escape
15581                 this.hide();
15582                 break;
15583             case 37: // left
15584             case 39: // right
15585                 this.onTogglePeriod();
15586                 break;
15587             case 38: // up
15588                 this.onIncrementMinutes();
15589                 break;
15590             case 40: // down
15591                 this.onDecrementMinutes();
15592                 break;
15593             case 13: // enter
15594             case 9: // tab
15595                 this.setTime();
15596                 break;
15597         }
15598     },
15599     
15600     onClick: function(e) {
15601         e.stopPropagation();
15602         e.preventDefault();
15603     },
15604     
15605     picker : function()
15606     {
15607         return this.el.select('.datepicker', true).first();
15608     },
15609     
15610     fillTime: function()
15611     {    
15612         var time = this.pop.select('tbody', true).first();
15613         
15614         time.dom.innerHTML = '';
15615         
15616         time.createChild({
15617             tag: 'tr',
15618             cn: [
15619                 {
15620                     tag: 'td',
15621                     cn: [
15622                         {
15623                             tag: 'a',
15624                             href: '#',
15625                             cls: 'btn',
15626                             cn: [
15627                                 {
15628                                     tag: 'span',
15629                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
15630                                 }
15631                             ]
15632                         } 
15633                     ]
15634                 },
15635                 {
15636                     tag: 'td',
15637                     cls: 'separator'
15638                 },
15639                 {
15640                     tag: 'td',
15641                     cn: [
15642                         {
15643                             tag: 'a',
15644                             href: '#',
15645                             cls: 'btn',
15646                             cn: [
15647                                 {
15648                                     tag: 'span',
15649                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
15650                                 }
15651                             ]
15652                         }
15653                     ]
15654                 },
15655                 {
15656                     tag: 'td',
15657                     cls: 'separator'
15658                 }
15659             ]
15660         });
15661         
15662         time.createChild({
15663             tag: 'tr',
15664             cn: [
15665                 {
15666                     tag: 'td',
15667                     cn: [
15668                         {
15669                             tag: 'span',
15670                             cls: 'timepicker-hour',
15671                             html: '00'
15672                         }  
15673                     ]
15674                 },
15675                 {
15676                     tag: 'td',
15677                     cls: 'separator',
15678                     html: ':'
15679                 },
15680                 {
15681                     tag: 'td',
15682                     cn: [
15683                         {
15684                             tag: 'span',
15685                             cls: 'timepicker-minute',
15686                             html: '00'
15687                         }  
15688                     ]
15689                 },
15690                 {
15691                     tag: 'td',
15692                     cls: 'separator'
15693                 },
15694                 {
15695                     tag: 'td',
15696                     cn: [
15697                         {
15698                             tag: 'button',
15699                             type: 'button',
15700                             cls: 'btn btn-primary period',
15701                             html: 'AM'
15702                             
15703                         }
15704                     ]
15705                 }
15706             ]
15707         });
15708         
15709         time.createChild({
15710             tag: 'tr',
15711             cn: [
15712                 {
15713                     tag: 'td',
15714                     cn: [
15715                         {
15716                             tag: 'a',
15717                             href: '#',
15718                             cls: 'btn',
15719                             cn: [
15720                                 {
15721                                     tag: 'span',
15722                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
15723                                 }
15724                             ]
15725                         }
15726                     ]
15727                 },
15728                 {
15729                     tag: 'td',
15730                     cls: 'separator'
15731                 },
15732                 {
15733                     tag: 'td',
15734                     cn: [
15735                         {
15736                             tag: 'a',
15737                             href: '#',
15738                             cls: 'btn',
15739                             cn: [
15740                                 {
15741                                     tag: 'span',
15742                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
15743                                 }
15744                             ]
15745                         }
15746                     ]
15747                 },
15748                 {
15749                     tag: 'td',
15750                     cls: 'separator'
15751                 }
15752             ]
15753         });
15754         
15755     },
15756     
15757     update: function()
15758     {
15759         
15760         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15761         
15762         this.fill();
15763     },
15764     
15765     fill: function() 
15766     {
15767         var hours = this.time.getHours();
15768         var minutes = this.time.getMinutes();
15769         var period = 'AM';
15770         
15771         if(hours > 11){
15772             period = 'PM';
15773         }
15774         
15775         if(hours == 0){
15776             hours = 12;
15777         }
15778         
15779         
15780         if(hours > 12){
15781             hours = hours - 12;
15782         }
15783         
15784         if(hours < 10){
15785             hours = '0' + hours;
15786         }
15787         
15788         if(minutes < 10){
15789             minutes = '0' + minutes;
15790         }
15791         
15792         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15793         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15794         this.pop.select('button', true).first().dom.innerHTML = period;
15795         
15796     },
15797     
15798     place: function()
15799     {   
15800         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15801         
15802         var cls = ['bottom'];
15803         
15804         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15805             cls.pop();
15806             cls.push('top');
15807         }
15808         
15809         cls.push('right');
15810         
15811         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15812             cls.pop();
15813             cls.push('left');
15814         }
15815         
15816         this.picker().addClass(cls.join('-'));
15817         
15818         var _this = this;
15819         
15820         Roo.each(cls, function(c){
15821             if(c == 'bottom'){
15822                 _this.picker().setTop(_this.inputEl().getHeight());
15823                 return;
15824             }
15825             if(c == 'top'){
15826                 _this.picker().setTop(0 - _this.picker().getHeight());
15827                 return;
15828             }
15829             
15830             if(c == 'left'){
15831                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15832                 return;
15833             }
15834             if(c == 'right'){
15835                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15836                 return;
15837             }
15838         });
15839         
15840     },
15841   
15842     onFocus : function()
15843     {
15844         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15845         this.show();
15846     },
15847     
15848     onBlur : function()
15849     {
15850         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15851         this.hide();
15852     },
15853     
15854     show : function()
15855     {
15856         this.picker().show();
15857         this.pop.show();
15858         this.update();
15859         this.place();
15860         
15861         this.fireEvent('show', this, this.date);
15862     },
15863     
15864     hide : function()
15865     {
15866         this.picker().hide();
15867         this.pop.hide();
15868         
15869         this.fireEvent('hide', this, this.date);
15870     },
15871     
15872     setTime : function()
15873     {
15874         this.hide();
15875         this.setValue(this.time.format(this.format));
15876         
15877         this.fireEvent('select', this, this.date);
15878         
15879         
15880     },
15881     
15882     onMousedown: function(e){
15883         e.stopPropagation();
15884         e.preventDefault();
15885     },
15886     
15887     onIncrementHours: function()
15888     {
15889         Roo.log('onIncrementHours');
15890         this.time = this.time.add(Date.HOUR, 1);
15891         this.update();
15892         
15893     },
15894     
15895     onDecrementHours: function()
15896     {
15897         Roo.log('onDecrementHours');
15898         this.time = this.time.add(Date.HOUR, -1);
15899         this.update();
15900     },
15901     
15902     onIncrementMinutes: function()
15903     {
15904         Roo.log('onIncrementMinutes');
15905         this.time = this.time.add(Date.MINUTE, 1);
15906         this.update();
15907     },
15908     
15909     onDecrementMinutes: function()
15910     {
15911         Roo.log('onDecrementMinutes');
15912         this.time = this.time.add(Date.MINUTE, -1);
15913         this.update();
15914     },
15915     
15916     onTogglePeriod: function()
15917     {
15918         Roo.log('onTogglePeriod');
15919         this.time = this.time.add(Date.HOUR, 12);
15920         this.update();
15921     }
15922     
15923    
15924 });
15925
15926 Roo.apply(Roo.bootstrap.TimeField,  {
15927     
15928     content : {
15929         tag: 'tbody',
15930         cn: [
15931             {
15932                 tag: 'tr',
15933                 cn: [
15934                 {
15935                     tag: 'td',
15936                     colspan: '7'
15937                 }
15938                 ]
15939             }
15940         ]
15941     },
15942     
15943     footer : {
15944         tag: 'tfoot',
15945         cn: [
15946             {
15947                 tag: 'tr',
15948                 cn: [
15949                 {
15950                     tag: 'th',
15951                     colspan: '7',
15952                     cls: '',
15953                     cn: [
15954                         {
15955                             tag: 'button',
15956                             cls: 'btn btn-info ok',
15957                             html: 'OK'
15958                         }
15959                     ]
15960                 }
15961
15962                 ]
15963             }
15964         ]
15965     }
15966 });
15967
15968 Roo.apply(Roo.bootstrap.TimeField,  {
15969   
15970     template : {
15971         tag: 'div',
15972         cls: 'datepicker dropdown-menu',
15973         cn: [
15974             {
15975                 tag: 'div',
15976                 cls: 'datepicker-time',
15977                 cn: [
15978                 {
15979                     tag: 'table',
15980                     cls: 'table-condensed',
15981                     cn:[
15982                     Roo.bootstrap.TimeField.content,
15983                     Roo.bootstrap.TimeField.footer
15984                     ]
15985                 }
15986                 ]
15987             }
15988         ]
15989     }
15990 });
15991
15992  
15993
15994  /*
15995  * - LGPL
15996  *
15997  * CheckBox
15998  * 
15999  */
16000
16001 /**
16002  * @class Roo.bootstrap.CheckBox
16003  * @extends Roo.bootstrap.Input
16004  * Bootstrap CheckBox class
16005  * 
16006  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
16007  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
16008  * @cfg {String} boxLabel The text that appears beside the checkbox
16009  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
16010  * @cfg {Boolean} checked initnal the element
16011  * 
16012  * 
16013  * @constructor
16014  * Create a new CheckBox
16015  * @param {Object} config The config object
16016  */
16017
16018 Roo.bootstrap.CheckBox = function(config){
16019     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
16020    
16021         this.addEvents({
16022             /**
16023             * @event check
16024             * Fires when the element is checked or unchecked.
16025             * @param {Roo.bootstrap.CheckBox} this This input
16026             * @param {Boolean} checked The new checked value
16027             */
16028            check : true
16029         });
16030 };
16031
16032 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
16033     
16034     inputType: 'checkbox',
16035     inputValue: 1,
16036     valueOff: 0,
16037     boxLabel: false,
16038     checked: false,
16039     weight : false,
16040     
16041     getAutoCreate : function()
16042     {
16043         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16044         
16045         var id = Roo.id();
16046         
16047         var cfg = {};
16048         
16049         cfg.cls = 'form-group checkbox' //input-group
16050         
16051         
16052         
16053         
16054         var input =  {
16055             tag: 'input',
16056             id : id,
16057             type : this.inputType,
16058             value : (!this.checked) ? this.valueOff : this.inputValue,
16059             cls : 'roo-checkbox', //'form-box',
16060             placeholder : this.placeholder || ''
16061             
16062         };
16063         
16064         if (this.weight) { // Validity check?
16065             cfg.cls += " checkbox-" + this.weight;
16066         }
16067         
16068         if (this.disabled) {
16069             input.disabled=true;
16070         }
16071         
16072         if(this.checked){
16073             input.checked = this.checked;
16074         }
16075         
16076         if (this.name) {
16077             input.name = this.name;
16078         }
16079         
16080         if (this.size) {
16081             input.cls += ' input-' + this.size;
16082         }
16083         
16084         var settings=this;
16085         ['xs','sm','md','lg'].map(function(size){
16086             if (settings[size]) {
16087                 cfg.cls += ' col-' + size + '-' + settings[size];
16088             }
16089         });
16090         
16091        
16092         
16093         var inputblock = input;
16094         
16095         
16096         
16097         
16098         if (this.before || this.after) {
16099             
16100             inputblock = {
16101                 cls : 'input-group',
16102                 cn :  [] 
16103             };
16104             if (this.before) {
16105                 inputblock.cn.push({
16106                     tag :'span',
16107                     cls : 'input-group-addon',
16108                     html : this.before
16109                 });
16110             }
16111             inputblock.cn.push(input);
16112             if (this.after) {
16113                 inputblock.cn.push({
16114                     tag :'span',
16115                     cls : 'input-group-addon',
16116                     html : this.after
16117                 });
16118             }
16119             
16120         };
16121         
16122         if (align ==='left' && this.fieldLabel.length) {
16123                 Roo.log("left and has label");
16124                 cfg.cn = [
16125                     
16126                     {
16127                         tag: 'label',
16128                         'for' :  id,
16129                         cls : 'control-label col-md-' + this.labelWidth,
16130                         html : this.fieldLabel
16131                         
16132                     },
16133                     {
16134                         cls : "col-md-" + (12 - this.labelWidth), 
16135                         cn: [
16136                             inputblock
16137                         ]
16138                     }
16139                     
16140                 ];
16141         } else if ( this.fieldLabel.length) {
16142                 Roo.log(" label");
16143                 cfg.cn = [
16144                    
16145                     {
16146                         tag: this.boxLabel ? 'span' : 'label',
16147                         'for': id,
16148                         cls: 'control-label box-input-label',
16149                         //cls : 'input-group-addon',
16150                         html : this.fieldLabel
16151                         
16152                     },
16153                     
16154                     inputblock
16155                     
16156                 ];
16157
16158         } else {
16159             
16160                 Roo.log(" no label && no align");
16161                 cfg.cn = [  inputblock ] ;
16162                 
16163                 
16164         };
16165          if(this.boxLabel){
16166             cfg.cn.push( {
16167                 tag: 'label',
16168                 'for': id,
16169                 cls: 'box-label',
16170                 html: this.boxLabel
16171                 
16172             });
16173         }
16174         
16175         
16176        
16177         return cfg;
16178         
16179     },
16180     
16181     /**
16182      * return the real input element.
16183      */
16184     inputEl: function ()
16185     {
16186         return this.el.select('input.roo-checkbox',true).first();
16187     },
16188     
16189     label: function()
16190     {
16191         return this.el.select('label.control-label',true).first();
16192     },
16193     
16194     initEvents : function()
16195     {
16196 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16197         
16198         this.inputEl().on('click', this.onClick,  this);
16199         
16200     },
16201     
16202     onClick : function()
16203     {   
16204         this.setChecked(!this.checked);
16205     },
16206     
16207     setChecked : function(state,suppressEvent)
16208     {
16209         this.checked = state;
16210         
16211         this.inputEl().dom.checked = state;
16212         
16213         if(suppressEvent !== true){
16214             this.fireEvent('check', this, state);
16215         }
16216         
16217         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16218         
16219     },
16220     
16221     setValue : function(v,suppressEvent)
16222     {
16223         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16224     }
16225     
16226 });
16227
16228  
16229 /*
16230  * - LGPL
16231  *
16232  * Radio
16233  * 
16234  */
16235
16236 /**
16237  * @class Roo.bootstrap.Radio
16238  * @extends Roo.bootstrap.CheckBox
16239  * Bootstrap Radio class
16240
16241  * @constructor
16242  * Create a new Radio
16243  * @param {Object} config The config object
16244  */
16245
16246 Roo.bootstrap.Radio = function(config){
16247     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16248    
16249 };
16250
16251 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
16252     
16253     inputType: 'radio',
16254     inputValue: '',
16255     valueOff: '',
16256     
16257     getAutoCreate : function()
16258     {
16259         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16260         
16261         var id = Roo.id();
16262         
16263         var cfg = {};
16264         
16265         cfg.cls = 'form-group radio' //input-group
16266         
16267         var input =  {
16268             tag: 'input',
16269             id : id,
16270             type : this.inputType,
16271             value : (!this.checked) ? this.valueOff : this.inputValue,
16272             cls : 'roo-radio',
16273             placeholder : this.placeholder || ''
16274             
16275         };
16276           if (this.weight) { // Validity check?
16277             cfg.cls += " radio-" + this.weight;
16278         }
16279         if (this.disabled) {
16280             input.disabled=true;
16281         }
16282         
16283         if(this.checked){
16284             input.checked = this.checked;
16285         }
16286         
16287         if (this.name) {
16288             input.name = this.name;
16289         }
16290         
16291         if (this.size) {
16292             input.cls += ' input-' + this.size;
16293         }
16294         
16295         var settings=this;
16296         ['xs','sm','md','lg'].map(function(size){
16297             if (settings[size]) {
16298                 cfg.cls += ' col-' + size + '-' + settings[size];
16299             }
16300         });
16301         
16302         var inputblock = input;
16303         
16304         if (this.before || this.after) {
16305             
16306             inputblock = {
16307                 cls : 'input-group',
16308                 cn :  [] 
16309             };
16310             if (this.before) {
16311                 inputblock.cn.push({
16312                     tag :'span',
16313                     cls : 'input-group-addon',
16314                     html : this.before
16315                 });
16316             }
16317             inputblock.cn.push(input);
16318             if (this.after) {
16319                 inputblock.cn.push({
16320                     tag :'span',
16321                     cls : 'input-group-addon',
16322                     html : this.after
16323                 });
16324             }
16325             
16326         };
16327         
16328         if (align ==='left' && this.fieldLabel.length) {
16329                 Roo.log("left and has label");
16330                 cfg.cn = [
16331                     
16332                     {
16333                         tag: 'label',
16334                         'for' :  id,
16335                         cls : 'control-label col-md-' + this.labelWidth,
16336                         html : this.fieldLabel
16337                         
16338                     },
16339                     {
16340                         cls : "col-md-" + (12 - this.labelWidth), 
16341                         cn: [
16342                             inputblock
16343                         ]
16344                     }
16345                     
16346                 ];
16347         } else if ( this.fieldLabel.length) {
16348                 Roo.log(" label");
16349                  cfg.cn = [
16350                    
16351                     {
16352                         tag: 'label',
16353                         'for': id,
16354                         cls: 'control-label box-input-label',
16355                         //cls : 'input-group-addon',
16356                         html : this.fieldLabel
16357                         
16358                     },
16359                     
16360                     inputblock
16361                     
16362                 ];
16363
16364         } else {
16365             
16366                    Roo.log(" no label && no align");
16367                 cfg.cn = [
16368                     
16369                         inputblock
16370                     
16371                 ];
16372                 
16373                 
16374         };
16375         
16376         if(this.boxLabel){
16377             cfg.cn.push({
16378                 tag: 'label',
16379                 'for': id,
16380                 cls: 'box-label',
16381                 html: this.boxLabel
16382             })
16383         }
16384         
16385         return cfg;
16386         
16387     },
16388     inputEl: function ()
16389     {
16390         return this.el.select('input.roo-radio',true).first();
16391     },
16392     onClick : function()
16393     {   
16394         this.setChecked(true);
16395     },
16396     
16397     setChecked : function(state,suppressEvent)
16398     {
16399         if(state){
16400             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16401                 v.dom.checked = false;
16402             });
16403         }
16404         
16405         this.checked = state;
16406         this.inputEl().dom.checked = state;
16407         
16408         if(suppressEvent !== true){
16409             this.fireEvent('check', this, state);
16410         }
16411         
16412         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16413         
16414     },
16415     
16416     getGroupValue : function()
16417     {
16418         var value = ''
16419         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16420             if(v.dom.checked == true){
16421                 value = v.dom.value;
16422             }
16423         });
16424         
16425         return value;
16426     },
16427     
16428     /**
16429      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16430      * @return {Mixed} value The field value
16431      */
16432     getValue : function(){
16433         return this.getGroupValue();
16434     }
16435     
16436 });
16437
16438  
16439 //<script type="text/javascript">
16440
16441 /*
16442  * Based  Ext JS Library 1.1.1
16443  * Copyright(c) 2006-2007, Ext JS, LLC.
16444  * LGPL
16445  *
16446  */
16447  
16448 /**
16449  * @class Roo.HtmlEditorCore
16450  * @extends Roo.Component
16451  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16452  *
16453  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16454  */
16455
16456 Roo.HtmlEditorCore = function(config){
16457     
16458     
16459     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16460     
16461     
16462     this.addEvents({
16463         /**
16464          * @event initialize
16465          * Fires when the editor is fully initialized (including the iframe)
16466          * @param {Roo.HtmlEditorCore} this
16467          */
16468         initialize: true,
16469         /**
16470          * @event activate
16471          * Fires when the editor is first receives the focus. Any insertion must wait
16472          * until after this event.
16473          * @param {Roo.HtmlEditorCore} this
16474          */
16475         activate: true,
16476          /**
16477          * @event beforesync
16478          * Fires before the textarea is updated with content from the editor iframe. Return false
16479          * to cancel the sync.
16480          * @param {Roo.HtmlEditorCore} this
16481          * @param {String} html
16482          */
16483         beforesync: true,
16484          /**
16485          * @event beforepush
16486          * Fires before the iframe editor is updated with content from the textarea. Return false
16487          * to cancel the push.
16488          * @param {Roo.HtmlEditorCore} this
16489          * @param {String} html
16490          */
16491         beforepush: true,
16492          /**
16493          * @event sync
16494          * Fires when the textarea is updated with content from the editor iframe.
16495          * @param {Roo.HtmlEditorCore} this
16496          * @param {String} html
16497          */
16498         sync: true,
16499          /**
16500          * @event push
16501          * Fires when the iframe editor is updated with content from the textarea.
16502          * @param {Roo.HtmlEditorCore} this
16503          * @param {String} html
16504          */
16505         push: true,
16506         
16507         /**
16508          * @event editorevent
16509          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16510          * @param {Roo.HtmlEditorCore} this
16511          */
16512         editorevent: true
16513     });
16514     
16515     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16516     
16517     // defaults : white / black...
16518     this.applyBlacklists();
16519     
16520     
16521     
16522 };
16523
16524
16525 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
16526
16527
16528      /**
16529      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
16530      */
16531     
16532     owner : false,
16533     
16534      /**
16535      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
16536      *                        Roo.resizable.
16537      */
16538     resizable : false,
16539      /**
16540      * @cfg {Number} height (in pixels)
16541      */   
16542     height: 300,
16543    /**
16544      * @cfg {Number} width (in pixels)
16545      */   
16546     width: 500,
16547     
16548     /**
16549      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16550      * 
16551      */
16552     stylesheets: false,
16553     
16554     // id of frame..
16555     frameId: false,
16556     
16557     // private properties
16558     validationEvent : false,
16559     deferHeight: true,
16560     initialized : false,
16561     activated : false,
16562     sourceEditMode : false,
16563     onFocus : Roo.emptyFn,
16564     iframePad:3,
16565     hideMode:'offsets',
16566     
16567     clearUp: true,
16568     
16569     // blacklist + whitelisted elements..
16570     black: false,
16571     white: false,
16572      
16573     
16574
16575     /**
16576      * Protected method that will not generally be called directly. It
16577      * is called when the editor initializes the iframe with HTML contents. Override this method if you
16578      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16579      */
16580     getDocMarkup : function(){
16581         // body styles..
16582         var st = '';
16583         Roo.log(this.stylesheets);
16584         
16585         // inherit styels from page...?? 
16586         if (this.stylesheets === false) {
16587             
16588             Roo.get(document.head).select('style').each(function(node) {
16589                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16590             });
16591             
16592             Roo.get(document.head).select('link').each(function(node) { 
16593                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16594             });
16595             
16596         } else if (!this.stylesheets.length) {
16597                 // simple..
16598                 st = '<style type="text/css">' +
16599                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16600                    '</style>';
16601         } else {
16602             Roo.each(this.stylesheets, function(s) {
16603                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16604             });
16605             
16606         }
16607         
16608         st +=  '<style type="text/css">' +
16609             'IMG { cursor: pointer } ' +
16610         '</style>';
16611
16612         
16613         return '<html><head>' + st  +
16614             //<style type="text/css">' +
16615             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16616             //'</style>' +
16617             ' </head><body class="roo-htmleditor-body"></body></html>';
16618     },
16619
16620     // private
16621     onRender : function(ct, position)
16622     {
16623         var _t = this;
16624         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16625         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16626         
16627         
16628         this.el.dom.style.border = '0 none';
16629         this.el.dom.setAttribute('tabIndex', -1);
16630         this.el.addClass('x-hidden hide');
16631         
16632         
16633         
16634         if(Roo.isIE){ // fix IE 1px bogus margin
16635             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16636         }
16637        
16638         
16639         this.frameId = Roo.id();
16640         
16641          
16642         
16643         var iframe = this.owner.wrap.createChild({
16644             tag: 'iframe',
16645             cls: 'form-control', // bootstrap..
16646             id: this.frameId,
16647             name: this.frameId,
16648             frameBorder : 'no',
16649             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
16650         }, this.el
16651         );
16652         
16653         
16654         this.iframe = iframe.dom;
16655
16656          this.assignDocWin();
16657         
16658         this.doc.designMode = 'on';
16659        
16660         this.doc.open();
16661         this.doc.write(this.getDocMarkup());
16662         this.doc.close();
16663
16664         
16665         var task = { // must defer to wait for browser to be ready
16666             run : function(){
16667                 //console.log("run task?" + this.doc.readyState);
16668                 this.assignDocWin();
16669                 if(this.doc.body || this.doc.readyState == 'complete'){
16670                     try {
16671                         this.doc.designMode="on";
16672                     } catch (e) {
16673                         return;
16674                     }
16675                     Roo.TaskMgr.stop(task);
16676                     this.initEditor.defer(10, this);
16677                 }
16678             },
16679             interval : 10,
16680             duration: 10000,
16681             scope: this
16682         };
16683         Roo.TaskMgr.start(task);
16684
16685         
16686          
16687     },
16688
16689     // private
16690     onResize : function(w, h)
16691     {
16692          Roo.log('resize: ' +w + ',' + h );
16693         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16694         if(!this.iframe){
16695             return;
16696         }
16697         if(typeof w == 'number'){
16698             
16699             this.iframe.style.width = w + 'px';
16700         }
16701         if(typeof h == 'number'){
16702             
16703             this.iframe.style.height = h + 'px';
16704             if(this.doc){
16705                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16706             }
16707         }
16708         
16709     },
16710
16711     /**
16712      * Toggles the editor between standard and source edit mode.
16713      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16714      */
16715     toggleSourceEdit : function(sourceEditMode){
16716         
16717         this.sourceEditMode = sourceEditMode === true;
16718         
16719         if(this.sourceEditMode){
16720  
16721             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
16722             
16723         }else{
16724             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16725             //this.iframe.className = '';
16726             this.deferFocus();
16727         }
16728         //this.setSize(this.owner.wrap.getSize());
16729         //this.fireEvent('editmodechange', this, this.sourceEditMode);
16730     },
16731
16732     
16733   
16734
16735     /**
16736      * Protected method that will not generally be called directly. If you need/want
16737      * custom HTML cleanup, this is the method you should override.
16738      * @param {String} html The HTML to be cleaned
16739      * return {String} The cleaned HTML
16740      */
16741     cleanHtml : function(html){
16742         html = String(html);
16743         if(html.length > 5){
16744             if(Roo.isSafari){ // strip safari nonsense
16745                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16746             }
16747         }
16748         if(html == '&nbsp;'){
16749             html = '';
16750         }
16751         return html;
16752     },
16753
16754     /**
16755      * HTML Editor -> Textarea
16756      * Protected method that will not generally be called directly. Syncs the contents
16757      * of the editor iframe with the textarea.
16758      */
16759     syncValue : function(){
16760         if(this.initialized){
16761             var bd = (this.doc.body || this.doc.documentElement);
16762             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16763             var html = bd.innerHTML;
16764             if(Roo.isSafari){
16765                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16766                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16767                 if(m && m[1]){
16768                     html = '<div style="'+m[0]+'">' + html + '</div>';
16769                 }
16770             }
16771             html = this.cleanHtml(html);
16772             // fix up the special chars.. normaly like back quotes in word...
16773             // however we do not want to do this with chinese..
16774             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16775                 var cc = b.charCodeAt();
16776                 if (
16777                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16778                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16779                     (cc >= 0xf900 && cc < 0xfb00 )
16780                 ) {
16781                         return b;
16782                 }
16783                 return "&#"+cc+";" 
16784             });
16785             if(this.owner.fireEvent('beforesync', this, html) !== false){
16786                 this.el.dom.value = html;
16787                 this.owner.fireEvent('sync', this, html);
16788             }
16789         }
16790     },
16791
16792     /**
16793      * Protected method that will not generally be called directly. Pushes the value of the textarea
16794      * into the iframe editor.
16795      */
16796     pushValue : function(){
16797         if(this.initialized){
16798             var v = this.el.dom.value.trim();
16799             
16800 //            if(v.length < 1){
16801 //                v = '&#160;';
16802 //            }
16803             
16804             if(this.owner.fireEvent('beforepush', this, v) !== false){
16805                 var d = (this.doc.body || this.doc.documentElement);
16806                 d.innerHTML = v;
16807                 this.cleanUpPaste();
16808                 this.el.dom.value = d.innerHTML;
16809                 this.owner.fireEvent('push', this, v);
16810             }
16811         }
16812     },
16813
16814     // private
16815     deferFocus : function(){
16816         this.focus.defer(10, this);
16817     },
16818
16819     // doc'ed in Field
16820     focus : function(){
16821         if(this.win && !this.sourceEditMode){
16822             this.win.focus();
16823         }else{
16824             this.el.focus();
16825         }
16826     },
16827     
16828     assignDocWin: function()
16829     {
16830         var iframe = this.iframe;
16831         
16832          if(Roo.isIE){
16833             this.doc = iframe.contentWindow.document;
16834             this.win = iframe.contentWindow;
16835         } else {
16836 //            if (!Roo.get(this.frameId)) {
16837 //                return;
16838 //            }
16839 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16840 //            this.win = Roo.get(this.frameId).dom.contentWindow;
16841             
16842             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16843                 return;
16844             }
16845             
16846             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16847             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16848         }
16849     },
16850     
16851     // private
16852     initEditor : function(){
16853         //console.log("INIT EDITOR");
16854         this.assignDocWin();
16855         
16856         
16857         
16858         this.doc.designMode="on";
16859         this.doc.open();
16860         this.doc.write(this.getDocMarkup());
16861         this.doc.close();
16862         
16863         var dbody = (this.doc.body || this.doc.documentElement);
16864         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16865         // this copies styles from the containing element into thsi one..
16866         // not sure why we need all of this..
16867         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16868         
16869         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16870         //ss['background-attachment'] = 'fixed'; // w3c
16871         dbody.bgProperties = 'fixed'; // ie
16872         //Roo.DomHelper.applyStyles(dbody, ss);
16873         Roo.EventManager.on(this.doc, {
16874             //'mousedown': this.onEditorEvent,
16875             'mouseup': this.onEditorEvent,
16876             'dblclick': this.onEditorEvent,
16877             'click': this.onEditorEvent,
16878             'keyup': this.onEditorEvent,
16879             buffer:100,
16880             scope: this
16881         });
16882         if(Roo.isGecko){
16883             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16884         }
16885         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16886             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16887         }
16888         this.initialized = true;
16889
16890         this.owner.fireEvent('initialize', this);
16891         this.pushValue();
16892     },
16893
16894     // private
16895     onDestroy : function(){
16896         
16897         
16898         
16899         if(this.rendered){
16900             
16901             //for (var i =0; i < this.toolbars.length;i++) {
16902             //    // fixme - ask toolbars for heights?
16903             //    this.toolbars[i].onDestroy();
16904            // }
16905             
16906             //this.wrap.dom.innerHTML = '';
16907             //this.wrap.remove();
16908         }
16909     },
16910
16911     // private
16912     onFirstFocus : function(){
16913         
16914         this.assignDocWin();
16915         
16916         
16917         this.activated = true;
16918          
16919     
16920         if(Roo.isGecko){ // prevent silly gecko errors
16921             this.win.focus();
16922             var s = this.win.getSelection();
16923             if(!s.focusNode || s.focusNode.nodeType != 3){
16924                 var r = s.getRangeAt(0);
16925                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16926                 r.collapse(true);
16927                 this.deferFocus();
16928             }
16929             try{
16930                 this.execCmd('useCSS', true);
16931                 this.execCmd('styleWithCSS', false);
16932             }catch(e){}
16933         }
16934         this.owner.fireEvent('activate', this);
16935     },
16936
16937     // private
16938     adjustFont: function(btn){
16939         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16940         //if(Roo.isSafari){ // safari
16941         //    adjust *= 2;
16942        // }
16943         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16944         if(Roo.isSafari){ // safari
16945             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16946             v =  (v < 10) ? 10 : v;
16947             v =  (v > 48) ? 48 : v;
16948             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16949             
16950         }
16951         
16952         
16953         v = Math.max(1, v+adjust);
16954         
16955         this.execCmd('FontSize', v  );
16956     },
16957
16958     onEditorEvent : function(e){
16959         this.owner.fireEvent('editorevent', this, e);
16960       //  this.updateToolbar();
16961         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16962     },
16963
16964     insertTag : function(tg)
16965     {
16966         // could be a bit smarter... -> wrap the current selected tRoo..
16967         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16968             
16969             range = this.createRange(this.getSelection());
16970             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16971             wrappingNode.appendChild(range.extractContents());
16972             range.insertNode(wrappingNode);
16973
16974             return;
16975             
16976             
16977             
16978         }
16979         this.execCmd("formatblock",   tg);
16980         
16981     },
16982     
16983     insertText : function(txt)
16984     {
16985         
16986         
16987         var range = this.createRange();
16988         range.deleteContents();
16989                //alert(Sender.getAttribute('label'));
16990                
16991         range.insertNode(this.doc.createTextNode(txt));
16992     } ,
16993     
16994      
16995
16996     /**
16997      * Executes a Midas editor command on the editor document and performs necessary focus and
16998      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16999      * @param {String} cmd The Midas command
17000      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17001      */
17002     relayCmd : function(cmd, value){
17003         this.win.focus();
17004         this.execCmd(cmd, value);
17005         this.owner.fireEvent('editorevent', this);
17006         //this.updateToolbar();
17007         this.owner.deferFocus();
17008     },
17009
17010     /**
17011      * Executes a Midas editor command directly on the editor document.
17012      * For visual commands, you should use {@link #relayCmd} instead.
17013      * <b>This should only be called after the editor is initialized.</b>
17014      * @param {String} cmd The Midas command
17015      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
17016      */
17017     execCmd : function(cmd, value){
17018         this.doc.execCommand(cmd, false, value === undefined ? null : value);
17019         this.syncValue();
17020     },
17021  
17022  
17023    
17024     /**
17025      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
17026      * to insert tRoo.
17027      * @param {String} text | dom node.. 
17028      */
17029     insertAtCursor : function(text)
17030     {
17031         
17032         
17033         
17034         if(!this.activated){
17035             return;
17036         }
17037         /*
17038         if(Roo.isIE){
17039             this.win.focus();
17040             var r = this.doc.selection.createRange();
17041             if(r){
17042                 r.collapse(true);
17043                 r.pasteHTML(text);
17044                 this.syncValue();
17045                 this.deferFocus();
17046             
17047             }
17048             return;
17049         }
17050         */
17051         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17052             this.win.focus();
17053             
17054             
17055             // from jquery ui (MIT licenced)
17056             var range, node;
17057             var win = this.win;
17058             
17059             if (win.getSelection && win.getSelection().getRangeAt) {
17060                 range = win.getSelection().getRangeAt(0);
17061                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17062                 range.insertNode(node);
17063             } else if (win.document.selection && win.document.selection.createRange) {
17064                 // no firefox support
17065                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17066                 win.document.selection.createRange().pasteHTML(txt);
17067             } else {
17068                 // no firefox support
17069                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17070                 this.execCmd('InsertHTML', txt);
17071             } 
17072             
17073             this.syncValue();
17074             
17075             this.deferFocus();
17076         }
17077     },
17078  // private
17079     mozKeyPress : function(e){
17080         if(e.ctrlKey){
17081             var c = e.getCharCode(), cmd;
17082           
17083             if(c > 0){
17084                 c = String.fromCharCode(c).toLowerCase();
17085                 switch(c){
17086                     case 'b':
17087                         cmd = 'bold';
17088                         break;
17089                     case 'i':
17090                         cmd = 'italic';
17091                         break;
17092                     
17093                     case 'u':
17094                         cmd = 'underline';
17095                         break;
17096                     
17097                     case 'v':
17098                         this.cleanUpPaste.defer(100, this);
17099                         return;
17100                         
17101                 }
17102                 if(cmd){
17103                     this.win.focus();
17104                     this.execCmd(cmd);
17105                     this.deferFocus();
17106                     e.preventDefault();
17107                 }
17108                 
17109             }
17110         }
17111     },
17112
17113     // private
17114     fixKeys : function(){ // load time branching for fastest keydown performance
17115         if(Roo.isIE){
17116             return function(e){
17117                 var k = e.getKey(), r;
17118                 if(k == e.TAB){
17119                     e.stopEvent();
17120                     r = this.doc.selection.createRange();
17121                     if(r){
17122                         r.collapse(true);
17123                         r.pasteHTML('&#160;&#160;&#160;&#160;');
17124                         this.deferFocus();
17125                     }
17126                     return;
17127                 }
17128                 
17129                 if(k == e.ENTER){
17130                     r = this.doc.selection.createRange();
17131                     if(r){
17132                         var target = r.parentElement();
17133                         if(!target || target.tagName.toLowerCase() != 'li'){
17134                             e.stopEvent();
17135                             r.pasteHTML('<br />');
17136                             r.collapse(false);
17137                             r.select();
17138                         }
17139                     }
17140                 }
17141                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17142                     this.cleanUpPaste.defer(100, this);
17143                     return;
17144                 }
17145                 
17146                 
17147             };
17148         }else if(Roo.isOpera){
17149             return function(e){
17150                 var k = e.getKey();
17151                 if(k == e.TAB){
17152                     e.stopEvent();
17153                     this.win.focus();
17154                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
17155                     this.deferFocus();
17156                 }
17157                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17158                     this.cleanUpPaste.defer(100, this);
17159                     return;
17160                 }
17161                 
17162             };
17163         }else if(Roo.isSafari){
17164             return function(e){
17165                 var k = e.getKey();
17166                 
17167                 if(k == e.TAB){
17168                     e.stopEvent();
17169                     this.execCmd('InsertText','\t');
17170                     this.deferFocus();
17171                     return;
17172                 }
17173                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17174                     this.cleanUpPaste.defer(100, this);
17175                     return;
17176                 }
17177                 
17178              };
17179         }
17180     }(),
17181     
17182     getAllAncestors: function()
17183     {
17184         var p = this.getSelectedNode();
17185         var a = [];
17186         if (!p) {
17187             a.push(p); // push blank onto stack..
17188             p = this.getParentElement();
17189         }
17190         
17191         
17192         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17193             a.push(p);
17194             p = p.parentNode;
17195         }
17196         a.push(this.doc.body);
17197         return a;
17198     },
17199     lastSel : false,
17200     lastSelNode : false,
17201     
17202     
17203     getSelection : function() 
17204     {
17205         this.assignDocWin();
17206         return Roo.isIE ? this.doc.selection : this.win.getSelection();
17207     },
17208     
17209     getSelectedNode: function() 
17210     {
17211         // this may only work on Gecko!!!
17212         
17213         // should we cache this!!!!
17214         
17215         
17216         
17217          
17218         var range = this.createRange(this.getSelection()).cloneRange();
17219         
17220         if (Roo.isIE) {
17221             var parent = range.parentElement();
17222             while (true) {
17223                 var testRange = range.duplicate();
17224                 testRange.moveToElementText(parent);
17225                 if (testRange.inRange(range)) {
17226                     break;
17227                 }
17228                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17229                     break;
17230                 }
17231                 parent = parent.parentElement;
17232             }
17233             return parent;
17234         }
17235         
17236         // is ancestor a text element.
17237         var ac =  range.commonAncestorContainer;
17238         if (ac.nodeType == 3) {
17239             ac = ac.parentNode;
17240         }
17241         
17242         var ar = ac.childNodes;
17243          
17244         var nodes = [];
17245         var other_nodes = [];
17246         var has_other_nodes = false;
17247         for (var i=0;i<ar.length;i++) {
17248             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
17249                 continue;
17250             }
17251             // fullly contained node.
17252             
17253             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17254                 nodes.push(ar[i]);
17255                 continue;
17256             }
17257             
17258             // probably selected..
17259             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17260                 other_nodes.push(ar[i]);
17261                 continue;
17262             }
17263             // outer..
17264             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
17265                 continue;
17266             }
17267             
17268             
17269             has_other_nodes = true;
17270         }
17271         if (!nodes.length && other_nodes.length) {
17272             nodes= other_nodes;
17273         }
17274         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17275             return false;
17276         }
17277         
17278         return nodes[0];
17279     },
17280     createRange: function(sel)
17281     {
17282         // this has strange effects when using with 
17283         // top toolbar - not sure if it's a great idea.
17284         //this.editor.contentWindow.focus();
17285         if (typeof sel != "undefined") {
17286             try {
17287                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17288             } catch(e) {
17289                 return this.doc.createRange();
17290             }
17291         } else {
17292             return this.doc.createRange();
17293         }
17294     },
17295     getParentElement: function()
17296     {
17297         
17298         this.assignDocWin();
17299         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17300         
17301         var range = this.createRange(sel);
17302          
17303         try {
17304             var p = range.commonAncestorContainer;
17305             while (p.nodeType == 3) { // text node
17306                 p = p.parentNode;
17307             }
17308             return p;
17309         } catch (e) {
17310             return null;
17311         }
17312     
17313     },
17314     /***
17315      *
17316      * Range intersection.. the hard stuff...
17317      *  '-1' = before
17318      *  '0' = hits..
17319      *  '1' = after.
17320      *         [ -- selected range --- ]
17321      *   [fail]                        [fail]
17322      *
17323      *    basically..
17324      *      if end is before start or  hits it. fail.
17325      *      if start is after end or hits it fail.
17326      *
17327      *   if either hits (but other is outside. - then it's not 
17328      *   
17329      *    
17330      **/
17331     
17332     
17333     // @see http://www.thismuchiknow.co.uk/?p=64.
17334     rangeIntersectsNode : function(range, node)
17335     {
17336         var nodeRange = node.ownerDocument.createRange();
17337         try {
17338             nodeRange.selectNode(node);
17339         } catch (e) {
17340             nodeRange.selectNodeContents(node);
17341         }
17342     
17343         var rangeStartRange = range.cloneRange();
17344         rangeStartRange.collapse(true);
17345     
17346         var rangeEndRange = range.cloneRange();
17347         rangeEndRange.collapse(false);
17348     
17349         var nodeStartRange = nodeRange.cloneRange();
17350         nodeStartRange.collapse(true);
17351     
17352         var nodeEndRange = nodeRange.cloneRange();
17353         nodeEndRange.collapse(false);
17354     
17355         return rangeStartRange.compareBoundaryPoints(
17356                  Range.START_TO_START, nodeEndRange) == -1 &&
17357                rangeEndRange.compareBoundaryPoints(
17358                  Range.START_TO_START, nodeStartRange) == 1;
17359         
17360          
17361     },
17362     rangeCompareNode : function(range, node)
17363     {
17364         var nodeRange = node.ownerDocument.createRange();
17365         try {
17366             nodeRange.selectNode(node);
17367         } catch (e) {
17368             nodeRange.selectNodeContents(node);
17369         }
17370         
17371         
17372         range.collapse(true);
17373     
17374         nodeRange.collapse(true);
17375      
17376         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17377         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
17378          
17379         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17380         
17381         var nodeIsBefore   =  ss == 1;
17382         var nodeIsAfter    = ee == -1;
17383         
17384         if (nodeIsBefore && nodeIsAfter)
17385             return 0; // outer
17386         if (!nodeIsBefore && nodeIsAfter)
17387             return 1; //right trailed.
17388         
17389         if (nodeIsBefore && !nodeIsAfter)
17390             return 2;  // left trailed.
17391         // fully contined.
17392         return 3;
17393     },
17394
17395     // private? - in a new class?
17396     cleanUpPaste :  function()
17397     {
17398         // cleans up the whole document..
17399         Roo.log('cleanuppaste');
17400         
17401         this.cleanUpChildren(this.doc.body);
17402         var clean = this.cleanWordChars(this.doc.body.innerHTML);
17403         if (clean != this.doc.body.innerHTML) {
17404             this.doc.body.innerHTML = clean;
17405         }
17406         
17407     },
17408     
17409     cleanWordChars : function(input) {// change the chars to hex code
17410         var he = Roo.HtmlEditorCore;
17411         
17412         var output = input;
17413         Roo.each(he.swapCodes, function(sw) { 
17414             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17415             
17416             output = output.replace(swapper, sw[1]);
17417         });
17418         
17419         return output;
17420     },
17421     
17422     
17423     cleanUpChildren : function (n)
17424     {
17425         if (!n.childNodes.length) {
17426             return;
17427         }
17428         for (var i = n.childNodes.length-1; i > -1 ; i--) {
17429            this.cleanUpChild(n.childNodes[i]);
17430         }
17431     },
17432     
17433     
17434         
17435     
17436     cleanUpChild : function (node)
17437     {
17438         var ed = this;
17439         //console.log(node);
17440         if (node.nodeName == "#text") {
17441             // clean up silly Windows -- stuff?
17442             return; 
17443         }
17444         if (node.nodeName == "#comment") {
17445             node.parentNode.removeChild(node);
17446             // clean up silly Windows -- stuff?
17447             return; 
17448         }
17449         var lcname = node.tagName.toLowerCase();
17450         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17451         // whitelist of tags..
17452         
17453         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17454             // remove node.
17455             node.parentNode.removeChild(node);
17456             return;
17457             
17458         }
17459         
17460         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17461         
17462         // remove <a name=....> as rendering on yahoo mailer is borked with this.
17463         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17464         
17465         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17466         //    remove_keep_children = true;
17467         //}
17468         
17469         if (remove_keep_children) {
17470             this.cleanUpChildren(node);
17471             // inserts everything just before this node...
17472             while (node.childNodes.length) {
17473                 var cn = node.childNodes[0];
17474                 node.removeChild(cn);
17475                 node.parentNode.insertBefore(cn, node);
17476             }
17477             node.parentNode.removeChild(node);
17478             return;
17479         }
17480         
17481         if (!node.attributes || !node.attributes.length) {
17482             this.cleanUpChildren(node);
17483             return;
17484         }
17485         
17486         function cleanAttr(n,v)
17487         {
17488             
17489             if (v.match(/^\./) || v.match(/^\//)) {
17490                 return;
17491             }
17492             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17493                 return;
17494             }
17495             if (v.match(/^#/)) {
17496                 return;
17497             }
17498 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17499             node.removeAttribute(n);
17500             
17501         }
17502         
17503         var cwhite = this.cwhite;
17504         var cblack = this.cblack;
17505             
17506         function cleanStyle(n,v)
17507         {
17508             if (v.match(/expression/)) { //XSS?? should we even bother..
17509                 node.removeAttribute(n);
17510                 return;
17511             }
17512             
17513             var parts = v.split(/;/);
17514             var clean = [];
17515             
17516             Roo.each(parts, function(p) {
17517                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17518                 if (!p.length) {
17519                     return true;
17520                 }
17521                 var l = p.split(':').shift().replace(/\s+/g,'');
17522                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17523                 
17524                 if ( cwhite.length && cblack.indexOf(l) > -1) {
17525 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17526                     //node.removeAttribute(n);
17527                     return true;
17528                 }
17529                 //Roo.log()
17530                 // only allow 'c whitelisted system attributes'
17531                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
17532 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17533                     //node.removeAttribute(n);
17534                     return true;
17535                 }
17536                 
17537                 
17538                  
17539                 
17540                 clean.push(p);
17541                 return true;
17542             });
17543             if (clean.length) { 
17544                 node.setAttribute(n, clean.join(';'));
17545             } else {
17546                 node.removeAttribute(n);
17547             }
17548             
17549         }
17550         
17551         
17552         for (var i = node.attributes.length-1; i > -1 ; i--) {
17553             var a = node.attributes[i];
17554             //console.log(a);
17555             
17556             if (a.name.toLowerCase().substr(0,2)=='on')  {
17557                 node.removeAttribute(a.name);
17558                 continue;
17559             }
17560             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17561                 node.removeAttribute(a.name);
17562                 continue;
17563             }
17564             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17565                 cleanAttr(a.name,a.value); // fixme..
17566                 continue;
17567             }
17568             if (a.name == 'style') {
17569                 cleanStyle(a.name,a.value);
17570                 continue;
17571             }
17572             /// clean up MS crap..
17573             // tecnically this should be a list of valid class'es..
17574             
17575             
17576             if (a.name == 'class') {
17577                 if (a.value.match(/^Mso/)) {
17578                     node.className = '';
17579                 }
17580                 
17581                 if (a.value.match(/body/)) {
17582                     node.className = '';
17583                 }
17584                 continue;
17585             }
17586             
17587             // style cleanup!?
17588             // class cleanup?
17589             
17590         }
17591         
17592         
17593         this.cleanUpChildren(node);
17594         
17595         
17596     },
17597     /**
17598      * Clean up MS wordisms...
17599      */
17600     cleanWord : function(node)
17601     {
17602         var _t = this;
17603         var cleanWordChildren = function()
17604         {
17605             if (!node.childNodes.length) {
17606                 return;
17607             }
17608             for (var i = node.childNodes.length-1; i > -1 ; i--) {
17609                _t.cleanWord(node.childNodes[i]);
17610             }
17611         }
17612         
17613         
17614         if (!node) {
17615             this.cleanWord(this.doc.body);
17616             return;
17617         }
17618         if (node.nodeName == "#text") {
17619             // clean up silly Windows -- stuff?
17620             return; 
17621         }
17622         if (node.nodeName == "#comment") {
17623             node.parentNode.removeChild(node);
17624             // clean up silly Windows -- stuff?
17625             return; 
17626         }
17627         
17628         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17629             node.parentNode.removeChild(node);
17630             return;
17631         }
17632         
17633         // remove - but keep children..
17634         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17635             while (node.childNodes.length) {
17636                 var cn = node.childNodes[0];
17637                 node.removeChild(cn);
17638                 node.parentNode.insertBefore(cn, node);
17639             }
17640             node.parentNode.removeChild(node);
17641             cleanWordChildren();
17642             return;
17643         }
17644         // clean styles
17645         if (node.className.length) {
17646             
17647             var cn = node.className.split(/\W+/);
17648             var cna = [];
17649             Roo.each(cn, function(cls) {
17650                 if (cls.match(/Mso[a-zA-Z]+/)) {
17651                     return;
17652                 }
17653                 cna.push(cls);
17654             });
17655             node.className = cna.length ? cna.join(' ') : '';
17656             if (!cna.length) {
17657                 node.removeAttribute("class");
17658             }
17659         }
17660         
17661         if (node.hasAttribute("lang")) {
17662             node.removeAttribute("lang");
17663         }
17664         
17665         if (node.hasAttribute("style")) {
17666             
17667             var styles = node.getAttribute("style").split(";");
17668             var nstyle = [];
17669             Roo.each(styles, function(s) {
17670                 if (!s.match(/:/)) {
17671                     return;
17672                 }
17673                 var kv = s.split(":");
17674                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17675                     return;
17676                 }
17677                 // what ever is left... we allow.
17678                 nstyle.push(s);
17679             });
17680             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17681             if (!nstyle.length) {
17682                 node.removeAttribute('style');
17683             }
17684         }
17685         
17686         cleanWordChildren();
17687         
17688         
17689     },
17690     domToHTML : function(currentElement, depth, nopadtext) {
17691         
17692         depth = depth || 0;
17693         nopadtext = nopadtext || false;
17694     
17695         if (!currentElement) {
17696             return this.domToHTML(this.doc.body);
17697         }
17698         
17699         //Roo.log(currentElement);
17700         var j;
17701         var allText = false;
17702         var nodeName = currentElement.nodeName;
17703         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17704         
17705         if  (nodeName == '#text') {
17706             return currentElement.nodeValue;
17707         }
17708         
17709         
17710         var ret = '';
17711         if (nodeName != 'BODY') {
17712              
17713             var i = 0;
17714             // Prints the node tagName, such as <A>, <IMG>, etc
17715             if (tagName) {
17716                 var attr = [];
17717                 for(i = 0; i < currentElement.attributes.length;i++) {
17718                     // quoting?
17719                     var aname = currentElement.attributes.item(i).name;
17720                     if (!currentElement.attributes.item(i).value.length) {
17721                         continue;
17722                     }
17723                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17724                 }
17725                 
17726                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17727             } 
17728             else {
17729                 
17730                 // eack
17731             }
17732         } else {
17733             tagName = false;
17734         }
17735         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17736             return ret;
17737         }
17738         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17739             nopadtext = true;
17740         }
17741         
17742         
17743         // Traverse the tree
17744         i = 0;
17745         var currentElementChild = currentElement.childNodes.item(i);
17746         var allText = true;
17747         var innerHTML  = '';
17748         lastnode = '';
17749         while (currentElementChild) {
17750             // Formatting code (indent the tree so it looks nice on the screen)
17751             var nopad = nopadtext;
17752             if (lastnode == 'SPAN') {
17753                 nopad  = true;
17754             }
17755             // text
17756             if  (currentElementChild.nodeName == '#text') {
17757                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17758                 if (!nopad && toadd.length > 80) {
17759                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
17760                 }
17761                 innerHTML  += toadd;
17762                 
17763                 i++;
17764                 currentElementChild = currentElement.childNodes.item(i);
17765                 lastNode = '';
17766                 continue;
17767             }
17768             allText = false;
17769             
17770             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17771                 
17772             // Recursively traverse the tree structure of the child node
17773             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17774             lastnode = currentElementChild.nodeName;
17775             i++;
17776             currentElementChild=currentElement.childNodes.item(i);
17777         }
17778         
17779         ret += innerHTML;
17780         
17781         if (!allText) {
17782                 // The remaining code is mostly for formatting the tree
17783             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17784         }
17785         
17786         
17787         if (tagName) {
17788             ret+= "</"+tagName+">";
17789         }
17790         return ret;
17791         
17792     },
17793         
17794     applyBlacklists : function()
17795     {
17796         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
17797         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
17798         
17799         this.white = [];
17800         this.black = [];
17801         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17802             if (b.indexOf(tag) > -1) {
17803                 return;
17804             }
17805             this.white.push(tag);
17806             
17807         }, this);
17808         
17809         Roo.each(w, function(tag) {
17810             if (b.indexOf(tag) > -1) {
17811                 return;
17812             }
17813             if (this.white.indexOf(tag) > -1) {
17814                 return;
17815             }
17816             this.white.push(tag);
17817             
17818         }, this);
17819         
17820         
17821         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17822             if (w.indexOf(tag) > -1) {
17823                 return;
17824             }
17825             this.black.push(tag);
17826             
17827         }, this);
17828         
17829         Roo.each(b, function(tag) {
17830             if (w.indexOf(tag) > -1) {
17831                 return;
17832             }
17833             if (this.black.indexOf(tag) > -1) {
17834                 return;
17835             }
17836             this.black.push(tag);
17837             
17838         }, this);
17839         
17840         
17841         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
17842         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
17843         
17844         this.cwhite = [];
17845         this.cblack = [];
17846         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17847             if (b.indexOf(tag) > -1) {
17848                 return;
17849             }
17850             this.cwhite.push(tag);
17851             
17852         }, this);
17853         
17854         Roo.each(w, function(tag) {
17855             if (b.indexOf(tag) > -1) {
17856                 return;
17857             }
17858             if (this.cwhite.indexOf(tag) > -1) {
17859                 return;
17860             }
17861             this.cwhite.push(tag);
17862             
17863         }, this);
17864         
17865         
17866         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17867             if (w.indexOf(tag) > -1) {
17868                 return;
17869             }
17870             this.cblack.push(tag);
17871             
17872         }, this);
17873         
17874         Roo.each(b, function(tag) {
17875             if (w.indexOf(tag) > -1) {
17876                 return;
17877             }
17878             if (this.cblack.indexOf(tag) > -1) {
17879                 return;
17880             }
17881             this.cblack.push(tag);
17882             
17883         }, this);
17884     }
17885     
17886     // hide stuff that is not compatible
17887     /**
17888      * @event blur
17889      * @hide
17890      */
17891     /**
17892      * @event change
17893      * @hide
17894      */
17895     /**
17896      * @event focus
17897      * @hide
17898      */
17899     /**
17900      * @event specialkey
17901      * @hide
17902      */
17903     /**
17904      * @cfg {String} fieldClass @hide
17905      */
17906     /**
17907      * @cfg {String} focusClass @hide
17908      */
17909     /**
17910      * @cfg {String} autoCreate @hide
17911      */
17912     /**
17913      * @cfg {String} inputType @hide
17914      */
17915     /**
17916      * @cfg {String} invalidClass @hide
17917      */
17918     /**
17919      * @cfg {String} invalidText @hide
17920      */
17921     /**
17922      * @cfg {String} msgFx @hide
17923      */
17924     /**
17925      * @cfg {String} validateOnBlur @hide
17926      */
17927 });
17928
17929 Roo.HtmlEditorCore.white = [
17930         'area', 'br', 'img', 'input', 'hr', 'wbr',
17931         
17932        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17933        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17934        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17935        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17936        'table',   'ul',         'xmp', 
17937        
17938        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17939       'thead',   'tr', 
17940      
17941       'dir', 'menu', 'ol', 'ul', 'dl',
17942        
17943       'embed',  'object'
17944 ];
17945
17946
17947 Roo.HtmlEditorCore.black = [
17948     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17949         'applet', // 
17950         'base',   'basefont', 'bgsound', 'blink',  'body', 
17951         'frame',  'frameset', 'head',    'html',   'ilayer', 
17952         'iframe', 'layer',  'link',     'meta',    'object',   
17953         'script', 'style' ,'title',  'xml' // clean later..
17954 ];
17955 Roo.HtmlEditorCore.clean = [
17956     'script', 'style', 'title', 'xml'
17957 ];
17958 Roo.HtmlEditorCore.remove = [
17959     'font'
17960 ];
17961 // attributes..
17962
17963 Roo.HtmlEditorCore.ablack = [
17964     'on'
17965 ];
17966     
17967 Roo.HtmlEditorCore.aclean = [ 
17968     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17969 ];
17970
17971 // protocols..
17972 Roo.HtmlEditorCore.pwhite= [
17973         'http',  'https',  'mailto'
17974 ];
17975
17976 // white listed style attributes.
17977 Roo.HtmlEditorCore.cwhite= [
17978       //  'text-align', /// default is to allow most things..
17979       
17980          
17981 //        'font-size'//??
17982 ];
17983
17984 // black listed style attributes.
17985 Roo.HtmlEditorCore.cblack= [
17986       //  'font-size' -- this can be set by the project 
17987 ];
17988
17989
17990 Roo.HtmlEditorCore.swapCodes   =[ 
17991     [    8211, "--" ], 
17992     [    8212, "--" ], 
17993     [    8216,  "'" ],  
17994     [    8217, "'" ],  
17995     [    8220, '"' ],  
17996     [    8221, '"' ],  
17997     [    8226, "*" ],  
17998     [    8230, "..." ]
17999 ]; 
18000
18001     /*
18002  * - LGPL
18003  *
18004  * HtmlEditor
18005  * 
18006  */
18007
18008 /**
18009  * @class Roo.bootstrap.HtmlEditor
18010  * @extends Roo.bootstrap.TextArea
18011  * Bootstrap HtmlEditor class
18012
18013  * @constructor
18014  * Create a new HtmlEditor
18015  * @param {Object} config The config object
18016  */
18017
18018 Roo.bootstrap.HtmlEditor = function(config){
18019     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
18020     if (!this.toolbars) {
18021         this.toolbars = [];
18022     }
18023     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
18024     this.addEvents({
18025             /**
18026              * @event initialize
18027              * Fires when the editor is fully initialized (including the iframe)
18028              * @param {HtmlEditor} this
18029              */
18030             initialize: true,
18031             /**
18032              * @event activate
18033              * Fires when the editor is first receives the focus. Any insertion must wait
18034              * until after this event.
18035              * @param {HtmlEditor} this
18036              */
18037             activate: true,
18038              /**
18039              * @event beforesync
18040              * Fires before the textarea is updated with content from the editor iframe. Return false
18041              * to cancel the sync.
18042              * @param {HtmlEditor} this
18043              * @param {String} html
18044              */
18045             beforesync: true,
18046              /**
18047              * @event beforepush
18048              * Fires before the iframe editor is updated with content from the textarea. Return false
18049              * to cancel the push.
18050              * @param {HtmlEditor} this
18051              * @param {String} html
18052              */
18053             beforepush: true,
18054              /**
18055              * @event sync
18056              * Fires when the textarea is updated with content from the editor iframe.
18057              * @param {HtmlEditor} this
18058              * @param {String} html
18059              */
18060             sync: true,
18061              /**
18062              * @event push
18063              * Fires when the iframe editor is updated with content from the textarea.
18064              * @param {HtmlEditor} this
18065              * @param {String} html
18066              */
18067             push: true,
18068              /**
18069              * @event editmodechange
18070              * Fires when the editor switches edit modes
18071              * @param {HtmlEditor} this
18072              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18073              */
18074             editmodechange: true,
18075             /**
18076              * @event editorevent
18077              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18078              * @param {HtmlEditor} this
18079              */
18080             editorevent: true,
18081             /**
18082              * @event firstfocus
18083              * Fires when on first focus - needed by toolbars..
18084              * @param {HtmlEditor} this
18085              */
18086             firstfocus: true,
18087             /**
18088              * @event autosave
18089              * Auto save the htmlEditor value as a file into Events
18090              * @param {HtmlEditor} this
18091              */
18092             autosave: true,
18093             /**
18094              * @event savedpreview
18095              * preview the saved version of htmlEditor
18096              * @param {HtmlEditor} this
18097              */
18098             savedpreview: true
18099         });
18100 };
18101
18102
18103 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
18104     
18105     
18106       /**
18107      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18108      */
18109     toolbars : false,
18110    
18111      /**
18112      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18113      *                        Roo.resizable.
18114      */
18115     resizable : false,
18116      /**
18117      * @cfg {Number} height (in pixels)
18118      */   
18119     height: 300,
18120    /**
18121      * @cfg {Number} width (in pixels)
18122      */   
18123     width: false,
18124     
18125     /**
18126      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18127      * 
18128      */
18129     stylesheets: false,
18130     
18131     // id of frame..
18132     frameId: false,
18133     
18134     // private properties
18135     validationEvent : false,
18136     deferHeight: true,
18137     initialized : false,
18138     activated : false,
18139     
18140     onFocus : Roo.emptyFn,
18141     iframePad:3,
18142     hideMode:'offsets',
18143     
18144     
18145     tbContainer : false,
18146     
18147     toolbarContainer :function() {
18148         return this.wrap.select('.x-html-editor-tb',true).first();
18149     },
18150
18151     /**
18152      * Protected method that will not generally be called directly. It
18153      * is called when the editor creates its toolbar. Override this method if you need to
18154      * add custom toolbar buttons.
18155      * @param {HtmlEditor} editor
18156      */
18157     createToolbar : function(){
18158         
18159         Roo.log("create toolbars");
18160         
18161         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18162         this.toolbars[0].render(this.toolbarContainer());
18163         
18164         return;
18165         
18166 //        if (!editor.toolbars || !editor.toolbars.length) {
18167 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18168 //        }
18169 //        
18170 //        for (var i =0 ; i < editor.toolbars.length;i++) {
18171 //            editor.toolbars[i] = Roo.factory(
18172 //                    typeof(editor.toolbars[i]) == 'string' ?
18173 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
18174 //                Roo.bootstrap.HtmlEditor);
18175 //            editor.toolbars[i].init(editor);
18176 //        }
18177     },
18178
18179      
18180     // private
18181     onRender : function(ct, position)
18182     {
18183        // Roo.log("Call onRender: " + this.xtype);
18184         var _t = this;
18185         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18186       
18187         this.wrap = this.inputEl().wrap({
18188             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18189         });
18190         
18191         this.editorcore.onRender(ct, position);
18192          
18193         if (this.resizable) {
18194             this.resizeEl = new Roo.Resizable(this.wrap, {
18195                 pinned : true,
18196                 wrap: true,
18197                 dynamic : true,
18198                 minHeight : this.height,
18199                 height: this.height,
18200                 handles : this.resizable,
18201                 width: this.width,
18202                 listeners : {
18203                     resize : function(r, w, h) {
18204                         _t.onResize(w,h); // -something
18205                     }
18206                 }
18207             });
18208             
18209         }
18210         this.createToolbar(this);
18211        
18212         
18213         if(!this.width && this.resizable){
18214             this.setSize(this.wrap.getSize());
18215         }
18216         if (this.resizeEl) {
18217             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18218             // should trigger onReize..
18219         }
18220         
18221     },
18222
18223     // private
18224     onResize : function(w, h)
18225     {
18226         Roo.log('resize: ' +w + ',' + h );
18227         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18228         var ew = false;
18229         var eh = false;
18230         
18231         if(this.inputEl() ){
18232             if(typeof w == 'number'){
18233                 var aw = w - this.wrap.getFrameWidth('lr');
18234                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18235                 ew = aw;
18236             }
18237             if(typeof h == 'number'){
18238                  var tbh = -11;  // fixme it needs to tool bar size!
18239                 for (var i =0; i < this.toolbars.length;i++) {
18240                     // fixme - ask toolbars for heights?
18241                     tbh += this.toolbars[i].el.getHeight();
18242                     //if (this.toolbars[i].footer) {
18243                     //    tbh += this.toolbars[i].footer.el.getHeight();
18244                     //}
18245                 }
18246               
18247                 
18248                 
18249                 
18250                 
18251                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18252                 ah -= 5; // knock a few pixes off for look..
18253                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18254                 var eh = ah;
18255             }
18256         }
18257         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18258         this.editorcore.onResize(ew,eh);
18259         
18260     },
18261
18262     /**
18263      * Toggles the editor between standard and source edit mode.
18264      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18265      */
18266     toggleSourceEdit : function(sourceEditMode)
18267     {
18268         this.editorcore.toggleSourceEdit(sourceEditMode);
18269         
18270         if(this.editorcore.sourceEditMode){
18271             Roo.log('editor - showing textarea');
18272             
18273 //            Roo.log('in');
18274 //            Roo.log(this.syncValue());
18275             this.syncValue();
18276             this.inputEl().removeClass(['hide', 'x-hidden']);
18277             this.inputEl().dom.removeAttribute('tabIndex');
18278             this.inputEl().focus();
18279         }else{
18280             Roo.log('editor - hiding textarea');
18281 //            Roo.log('out')
18282 //            Roo.log(this.pushValue()); 
18283             this.pushValue();
18284             
18285             this.inputEl().addClass(['hide', 'x-hidden']);
18286             this.inputEl().dom.setAttribute('tabIndex', -1);
18287             //this.deferFocus();
18288         }
18289          
18290         if(this.resizable){
18291             this.setSize(this.wrap.getSize());
18292         }
18293         
18294         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18295     },
18296  
18297     // private (for BoxComponent)
18298     adjustSize : Roo.BoxComponent.prototype.adjustSize,
18299
18300     // private (for BoxComponent)
18301     getResizeEl : function(){
18302         return this.wrap;
18303     },
18304
18305     // private (for BoxComponent)
18306     getPositionEl : function(){
18307         return this.wrap;
18308     },
18309
18310     // private
18311     initEvents : function(){
18312         this.originalValue = this.getValue();
18313     },
18314
18315 //    /**
18316 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18317 //     * @method
18318 //     */
18319 //    markInvalid : Roo.emptyFn,
18320 //    /**
18321 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18322 //     * @method
18323 //     */
18324 //    clearInvalid : Roo.emptyFn,
18325
18326     setValue : function(v){
18327         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18328         this.editorcore.pushValue();
18329     },
18330
18331      
18332     // private
18333     deferFocus : function(){
18334         this.focus.defer(10, this);
18335     },
18336
18337     // doc'ed in Field
18338     focus : function(){
18339         this.editorcore.focus();
18340         
18341     },
18342       
18343
18344     // private
18345     onDestroy : function(){
18346         
18347         
18348         
18349         if(this.rendered){
18350             
18351             for (var i =0; i < this.toolbars.length;i++) {
18352                 // fixme - ask toolbars for heights?
18353                 this.toolbars[i].onDestroy();
18354             }
18355             
18356             this.wrap.dom.innerHTML = '';
18357             this.wrap.remove();
18358         }
18359     },
18360
18361     // private
18362     onFirstFocus : function(){
18363         //Roo.log("onFirstFocus");
18364         this.editorcore.onFirstFocus();
18365          for (var i =0; i < this.toolbars.length;i++) {
18366             this.toolbars[i].onFirstFocus();
18367         }
18368         
18369     },
18370     
18371     // private
18372     syncValue : function()
18373     {   
18374         this.editorcore.syncValue();
18375     },
18376     
18377     pushValue : function()
18378     {   
18379         this.editorcore.pushValue();
18380     }
18381      
18382     
18383     // hide stuff that is not compatible
18384     /**
18385      * @event blur
18386      * @hide
18387      */
18388     /**
18389      * @event change
18390      * @hide
18391      */
18392     /**
18393      * @event focus
18394      * @hide
18395      */
18396     /**
18397      * @event specialkey
18398      * @hide
18399      */
18400     /**
18401      * @cfg {String} fieldClass @hide
18402      */
18403     /**
18404      * @cfg {String} focusClass @hide
18405      */
18406     /**
18407      * @cfg {String} autoCreate @hide
18408      */
18409     /**
18410      * @cfg {String} inputType @hide
18411      */
18412     /**
18413      * @cfg {String} invalidClass @hide
18414      */
18415     /**
18416      * @cfg {String} invalidText @hide
18417      */
18418     /**
18419      * @cfg {String} msgFx @hide
18420      */
18421     /**
18422      * @cfg {String} validateOnBlur @hide
18423      */
18424 });
18425  
18426     
18427    
18428    
18429    
18430       
18431 Roo.namespace('Roo.bootstrap.htmleditor');
18432 /**
18433  * @class Roo.bootstrap.HtmlEditorToolbar1
18434  * Basic Toolbar
18435  * 
18436  * Usage:
18437  *
18438  new Roo.bootstrap.HtmlEditor({
18439     ....
18440     toolbars : [
18441         new Roo.bootstrap.HtmlEditorToolbar1({
18442             disable : { fonts: 1 , format: 1, ..., ... , ...],
18443             btns : [ .... ]
18444         })
18445     }
18446      
18447  * 
18448  * @cfg {Object} disable List of elements to disable..
18449  * @cfg {Array} btns List of additional buttons.
18450  * 
18451  * 
18452  * NEEDS Extra CSS? 
18453  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18454  */
18455  
18456 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18457 {
18458     
18459     Roo.apply(this, config);
18460     
18461     // default disabled, based on 'good practice'..
18462     this.disable = this.disable || {};
18463     Roo.applyIf(this.disable, {
18464         fontSize : true,
18465         colors : true,
18466         specialElements : true
18467     });
18468     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18469     
18470     this.editor = config.editor;
18471     this.editorcore = config.editor.editorcore;
18472     
18473     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18474     
18475     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18476     // dont call parent... till later.
18477 }
18478 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
18479      
18480     bar : true,
18481     
18482     editor : false,
18483     editorcore : false,
18484     
18485     
18486     formats : [
18487         "p" ,  
18488         "h1","h2","h3","h4","h5","h6", 
18489         "pre", "code", 
18490         "abbr", "acronym", "address", "cite", "samp", "var",
18491         'div','span'
18492     ],
18493     
18494     onRender : function(ct, position)
18495     {
18496        // Roo.log("Call onRender: " + this.xtype);
18497         
18498        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18499        Roo.log(this.el);
18500        this.el.dom.style.marginBottom = '0';
18501        var _this = this;
18502        var editorcore = this.editorcore;
18503        var editor= this.editor;
18504        
18505        var children = [];
18506        var btn = function(id,cmd , toggle, handler){
18507        
18508             var  event = toggle ? 'toggle' : 'click';
18509        
18510             var a = {
18511                 size : 'sm',
18512                 xtype: 'Button',
18513                 xns: Roo.bootstrap,
18514                 glyphicon : id,
18515                 cmd : id || cmd,
18516                 enableToggle:toggle !== false,
18517                 //html : 'submit'
18518                 pressed : toggle ? false : null,
18519                 listeners : {}
18520             }
18521             a.listeners[toggle ? 'toggle' : 'click'] = function() {
18522                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
18523             }
18524             children.push(a);
18525             return a;
18526        }
18527         
18528         var style = {
18529                 xtype: 'Button',
18530                 size : 'sm',
18531                 xns: Roo.bootstrap,
18532                 glyphicon : 'font',
18533                 //html : 'submit'
18534                 menu : {
18535                     xtype: 'Menu',
18536                     xns: Roo.bootstrap,
18537                     items:  []
18538                 }
18539         };
18540         Roo.each(this.formats, function(f) {
18541             style.menu.items.push({
18542                 xtype :'MenuItem',
18543                 xns: Roo.bootstrap,
18544                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18545                 tagname : f,
18546                 listeners : {
18547                     click : function()
18548                     {
18549                         editorcore.insertTag(this.tagname);
18550                         editor.focus();
18551                     }
18552                 }
18553                 
18554             });
18555         });
18556          children.push(style);   
18557             
18558             
18559         btn('bold',false,true);
18560         btn('italic',false,true);
18561         btn('align-left', 'justifyleft',true);
18562         btn('align-center', 'justifycenter',true);
18563         btn('align-right' , 'justifyright',true);
18564         btn('link', false, false, function(btn) {
18565             //Roo.log("create link?");
18566             var url = prompt(this.createLinkText, this.defaultLinkValue);
18567             if(url && url != 'http:/'+'/'){
18568                 this.editorcore.relayCmd('createlink', url);
18569             }
18570         }),
18571         btn('list','insertunorderedlist',true);
18572         btn('pencil', false,true, function(btn){
18573                 Roo.log(this);
18574                 
18575                 this.toggleSourceEdit(btn.pressed);
18576         });
18577         /*
18578         var cog = {
18579                 xtype: 'Button',
18580                 size : 'sm',
18581                 xns: Roo.bootstrap,
18582                 glyphicon : 'cog',
18583                 //html : 'submit'
18584                 menu : {
18585                     xtype: 'Menu',
18586                     xns: Roo.bootstrap,
18587                     items:  []
18588                 }
18589         };
18590         
18591         cog.menu.items.push({
18592             xtype :'MenuItem',
18593             xns: Roo.bootstrap,
18594             html : Clean styles,
18595             tagname : f,
18596             listeners : {
18597                 click : function()
18598                 {
18599                     editorcore.insertTag(this.tagname);
18600                     editor.focus();
18601                 }
18602             }
18603             
18604         });
18605        */
18606         
18607          
18608        this.xtype = 'NavSimplebar';
18609         
18610         for(var i=0;i< children.length;i++) {
18611             
18612             this.buttons.add(this.addxtypeChild(children[i]));
18613             
18614         }
18615         
18616         editor.on('editorevent', this.updateToolbar, this);
18617     },
18618     onBtnClick : function(id)
18619     {
18620        this.editorcore.relayCmd(id);
18621        this.editorcore.focus();
18622     },
18623     
18624     /**
18625      * Protected method that will not generally be called directly. It triggers
18626      * a toolbar update by reading the markup state of the current selection in the editor.
18627      */
18628     updateToolbar: function(){
18629
18630         if(!this.editorcore.activated){
18631             this.editor.onFirstFocus(); // is this neeed?
18632             return;
18633         }
18634
18635         var btns = this.buttons; 
18636         var doc = this.editorcore.doc;
18637         btns.get('bold').setActive(doc.queryCommandState('bold'));
18638         btns.get('italic').setActive(doc.queryCommandState('italic'));
18639         //btns.get('underline').setActive(doc.queryCommandState('underline'));
18640         
18641         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18642         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18643         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18644         
18645         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18646         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18647          /*
18648         
18649         var ans = this.editorcore.getAllAncestors();
18650         if (this.formatCombo) {
18651             
18652             
18653             var store = this.formatCombo.store;
18654             this.formatCombo.setValue("");
18655             for (var i =0; i < ans.length;i++) {
18656                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18657                     // select it..
18658                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18659                     break;
18660                 }
18661             }
18662         }
18663         
18664         
18665         
18666         // hides menus... - so this cant be on a menu...
18667         Roo.bootstrap.MenuMgr.hideAll();
18668         */
18669         Roo.bootstrap.MenuMgr.hideAll();
18670         //this.editorsyncValue();
18671     },
18672     onFirstFocus: function() {
18673         this.buttons.each(function(item){
18674            item.enable();
18675         });
18676     },
18677     toggleSourceEdit : function(sourceEditMode){
18678         
18679           
18680         if(sourceEditMode){
18681             Roo.log("disabling buttons");
18682            this.buttons.each( function(item){
18683                 if(item.cmd != 'pencil'){
18684                     item.disable();
18685                 }
18686             });
18687           
18688         }else{
18689             Roo.log("enabling buttons");
18690             if(this.editorcore.initialized){
18691                 this.buttons.each( function(item){
18692                     item.enable();
18693                 });
18694             }
18695             
18696         }
18697         Roo.log("calling toggole on editor");
18698         // tell the editor that it's been pressed..
18699         this.editor.toggleSourceEdit(sourceEditMode);
18700        
18701     }
18702 });
18703
18704
18705
18706
18707
18708 /**
18709  * @class Roo.bootstrap.Table.AbstractSelectionModel
18710  * @extends Roo.util.Observable
18711  * Abstract base class for grid SelectionModels.  It provides the interface that should be
18712  * implemented by descendant classes.  This class should not be directly instantiated.
18713  * @constructor
18714  */
18715 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18716     this.locked = false;
18717     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18718 };
18719
18720
18721 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
18722     /** @ignore Called by the grid automatically. Do not call directly. */
18723     init : function(grid){
18724         this.grid = grid;
18725         this.initEvents();
18726     },
18727
18728     /**
18729      * Locks the selections.
18730      */
18731     lock : function(){
18732         this.locked = true;
18733     },
18734
18735     /**
18736      * Unlocks the selections.
18737      */
18738     unlock : function(){
18739         this.locked = false;
18740     },
18741
18742     /**
18743      * Returns true if the selections are locked.
18744      * @return {Boolean}
18745      */
18746     isLocked : function(){
18747         return this.locked;
18748     }
18749 });
18750 /**
18751  * @extends Roo.bootstrap.Table.AbstractSelectionModel
18752  * @class Roo.bootstrap.Table.RowSelectionModel
18753  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18754  * It supports multiple selections and keyboard selection/navigation. 
18755  * @constructor
18756  * @param {Object} config
18757  */
18758
18759 Roo.bootstrap.Table.RowSelectionModel = function(config){
18760     Roo.apply(this, config);
18761     this.selections = new Roo.util.MixedCollection(false, function(o){
18762         return o.id;
18763     });
18764
18765     this.last = false;
18766     this.lastActive = false;
18767
18768     this.addEvents({
18769         /**
18770              * @event selectionchange
18771              * Fires when the selection changes
18772              * @param {SelectionModel} this
18773              */
18774             "selectionchange" : true,
18775         /**
18776              * @event afterselectionchange
18777              * Fires after the selection changes (eg. by key press or clicking)
18778              * @param {SelectionModel} this
18779              */
18780             "afterselectionchange" : true,
18781         /**
18782              * @event beforerowselect
18783              * Fires when a row is selected being selected, return false to cancel.
18784              * @param {SelectionModel} this
18785              * @param {Number} rowIndex The selected index
18786              * @param {Boolean} keepExisting False if other selections will be cleared
18787              */
18788             "beforerowselect" : true,
18789         /**
18790              * @event rowselect
18791              * Fires when a row is selected.
18792              * @param {SelectionModel} this
18793              * @param {Number} rowIndex The selected index
18794              * @param {Roo.data.Record} r The record
18795              */
18796             "rowselect" : true,
18797         /**
18798              * @event rowdeselect
18799              * Fires when a row is deselected.
18800              * @param {SelectionModel} this
18801              * @param {Number} rowIndex The selected index
18802              */
18803         "rowdeselect" : true
18804     });
18805     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18806     this.locked = false;
18807 };
18808
18809 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
18810     /**
18811      * @cfg {Boolean} singleSelect
18812      * True to allow selection of only one row at a time (defaults to false)
18813      */
18814     singleSelect : false,
18815
18816     // private
18817     initEvents : function(){
18818
18819         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18820             this.grid.on("mousedown", this.handleMouseDown, this);
18821         }else{ // allow click to work like normal
18822             this.grid.on("rowclick", this.handleDragableRowClick, this);
18823         }
18824
18825         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18826             "up" : function(e){
18827                 if(!e.shiftKey){
18828                     this.selectPrevious(e.shiftKey);
18829                 }else if(this.last !== false && this.lastActive !== false){
18830                     var last = this.last;
18831                     this.selectRange(this.last,  this.lastActive-1);
18832                     this.grid.getView().focusRow(this.lastActive);
18833                     if(last !== false){
18834                         this.last = last;
18835                     }
18836                 }else{
18837                     this.selectFirstRow();
18838                 }
18839                 this.fireEvent("afterselectionchange", this);
18840             },
18841             "down" : function(e){
18842                 if(!e.shiftKey){
18843                     this.selectNext(e.shiftKey);
18844                 }else if(this.last !== false && this.lastActive !== false){
18845                     var last = this.last;
18846                     this.selectRange(this.last,  this.lastActive+1);
18847                     this.grid.getView().focusRow(this.lastActive);
18848                     if(last !== false){
18849                         this.last = last;
18850                     }
18851                 }else{
18852                     this.selectFirstRow();
18853                 }
18854                 this.fireEvent("afterselectionchange", this);
18855             },
18856             scope: this
18857         });
18858
18859         var view = this.grid.view;
18860         view.on("refresh", this.onRefresh, this);
18861         view.on("rowupdated", this.onRowUpdated, this);
18862         view.on("rowremoved", this.onRemove, this);
18863     },
18864
18865     // private
18866     onRefresh : function(){
18867         var ds = this.grid.dataSource, i, v = this.grid.view;
18868         var s = this.selections;
18869         s.each(function(r){
18870             if((i = ds.indexOfId(r.id)) != -1){
18871                 v.onRowSelect(i);
18872             }else{
18873                 s.remove(r);
18874             }
18875         });
18876     },
18877
18878     // private
18879     onRemove : function(v, index, r){
18880         this.selections.remove(r);
18881     },
18882
18883     // private
18884     onRowUpdated : function(v, index, r){
18885         if(this.isSelected(r)){
18886             v.onRowSelect(index);
18887         }
18888     },
18889
18890     /**
18891      * Select records.
18892      * @param {Array} records The records to select
18893      * @param {Boolean} keepExisting (optional) True to keep existing selections
18894      */
18895     selectRecords : function(records, keepExisting){
18896         if(!keepExisting){
18897             this.clearSelections();
18898         }
18899         var ds = this.grid.dataSource;
18900         for(var i = 0, len = records.length; i < len; i++){
18901             this.selectRow(ds.indexOf(records[i]), true);
18902         }
18903     },
18904
18905     /**
18906      * Gets the number of selected rows.
18907      * @return {Number}
18908      */
18909     getCount : function(){
18910         return this.selections.length;
18911     },
18912
18913     /**
18914      * Selects the first row in the grid.
18915      */
18916     selectFirstRow : function(){
18917         this.selectRow(0);
18918     },
18919
18920     /**
18921      * Select the last row.
18922      * @param {Boolean} keepExisting (optional) True to keep existing selections
18923      */
18924     selectLastRow : function(keepExisting){
18925         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18926     },
18927
18928     /**
18929      * Selects the row immediately following the last selected row.
18930      * @param {Boolean} keepExisting (optional) True to keep existing selections
18931      */
18932     selectNext : function(keepExisting){
18933         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18934             this.selectRow(this.last+1, keepExisting);
18935             this.grid.getView().focusRow(this.last);
18936         }
18937     },
18938
18939     /**
18940      * Selects the row that precedes the last selected row.
18941      * @param {Boolean} keepExisting (optional) True to keep existing selections
18942      */
18943     selectPrevious : function(keepExisting){
18944         if(this.last){
18945             this.selectRow(this.last-1, keepExisting);
18946             this.grid.getView().focusRow(this.last);
18947         }
18948     },
18949
18950     /**
18951      * Returns the selected records
18952      * @return {Array} Array of selected records
18953      */
18954     getSelections : function(){
18955         return [].concat(this.selections.items);
18956     },
18957
18958     /**
18959      * Returns the first selected record.
18960      * @return {Record}
18961      */
18962     getSelected : function(){
18963         return this.selections.itemAt(0);
18964     },
18965
18966
18967     /**
18968      * Clears all selections.
18969      */
18970     clearSelections : function(fast){
18971         if(this.locked) return;
18972         if(fast !== true){
18973             var ds = this.grid.dataSource;
18974             var s = this.selections;
18975             s.each(function(r){
18976                 this.deselectRow(ds.indexOfId(r.id));
18977             }, this);
18978             s.clear();
18979         }else{
18980             this.selections.clear();
18981         }
18982         this.last = false;
18983     },
18984
18985
18986     /**
18987      * Selects all rows.
18988      */
18989     selectAll : function(){
18990         if(this.locked) return;
18991         this.selections.clear();
18992         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18993             this.selectRow(i, true);
18994         }
18995     },
18996
18997     /**
18998      * Returns True if there is a selection.
18999      * @return {Boolean}
19000      */
19001     hasSelection : function(){
19002         return this.selections.length > 0;
19003     },
19004
19005     /**
19006      * Returns True if the specified row is selected.
19007      * @param {Number/Record} record The record or index of the record to check
19008      * @return {Boolean}
19009      */
19010     isSelected : function(index){
19011         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
19012         return (r && this.selections.key(r.id) ? true : false);
19013     },
19014
19015     /**
19016      * Returns True if the specified record id is selected.
19017      * @param {String} id The id of record to check
19018      * @return {Boolean}
19019      */
19020     isIdSelected : function(id){
19021         return (this.selections.key(id) ? true : false);
19022     },
19023
19024     // private
19025     handleMouseDown : function(e, t){
19026         var view = this.grid.getView(), rowIndex;
19027         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
19028             return;
19029         };
19030         if(e.shiftKey && this.last !== false){
19031             var last = this.last;
19032             this.selectRange(last, rowIndex, e.ctrlKey);
19033             this.last = last; // reset the last
19034             view.focusRow(rowIndex);
19035         }else{
19036             var isSelected = this.isSelected(rowIndex);
19037             if(e.button !== 0 && isSelected){
19038                 view.focusRow(rowIndex);
19039             }else if(e.ctrlKey && isSelected){
19040                 this.deselectRow(rowIndex);
19041             }else if(!isSelected){
19042                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19043                 view.focusRow(rowIndex);
19044             }
19045         }
19046         this.fireEvent("afterselectionchange", this);
19047     },
19048     // private
19049     handleDragableRowClick :  function(grid, rowIndex, e) 
19050     {
19051         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19052             this.selectRow(rowIndex, false);
19053             grid.view.focusRow(rowIndex);
19054              this.fireEvent("afterselectionchange", this);
19055         }
19056     },
19057     
19058     /**
19059      * Selects multiple rows.
19060      * @param {Array} rows Array of the indexes of the row to select
19061      * @param {Boolean} keepExisting (optional) True to keep existing selections
19062      */
19063     selectRows : function(rows, keepExisting){
19064         if(!keepExisting){
19065             this.clearSelections();
19066         }
19067         for(var i = 0, len = rows.length; i < len; i++){
19068             this.selectRow(rows[i], true);
19069         }
19070     },
19071
19072     /**
19073      * Selects a range of rows. All rows in between startRow and endRow are also selected.
19074      * @param {Number} startRow The index of the first row in the range
19075      * @param {Number} endRow The index of the last row in the range
19076      * @param {Boolean} keepExisting (optional) True to retain existing selections
19077      */
19078     selectRange : function(startRow, endRow, keepExisting){
19079         if(this.locked) return;
19080         if(!keepExisting){
19081             this.clearSelections();
19082         }
19083         if(startRow <= endRow){
19084             for(var i = startRow; i <= endRow; i++){
19085                 this.selectRow(i, true);
19086             }
19087         }else{
19088             for(var i = startRow; i >= endRow; i--){
19089                 this.selectRow(i, true);
19090             }
19091         }
19092     },
19093
19094     /**
19095      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19096      * @param {Number} startRow The index of the first row in the range
19097      * @param {Number} endRow The index of the last row in the range
19098      */
19099     deselectRange : function(startRow, endRow, preventViewNotify){
19100         if(this.locked) return;
19101         for(var i = startRow; i <= endRow; i++){
19102             this.deselectRow(i, preventViewNotify);
19103         }
19104     },
19105
19106     /**
19107      * Selects a row.
19108      * @param {Number} row The index of the row to select
19109      * @param {Boolean} keepExisting (optional) True to keep existing selections
19110      */
19111     selectRow : function(index, keepExisting, preventViewNotify){
19112         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19113         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19114             if(!keepExisting || this.singleSelect){
19115                 this.clearSelections();
19116             }
19117             var r = this.grid.dataSource.getAt(index);
19118             this.selections.add(r);
19119             this.last = this.lastActive = index;
19120             if(!preventViewNotify){
19121                 this.grid.getView().onRowSelect(index);
19122             }
19123             this.fireEvent("rowselect", this, index, r);
19124             this.fireEvent("selectionchange", this);
19125         }
19126     },
19127
19128     /**
19129      * Deselects a row.
19130      * @param {Number} row The index of the row to deselect
19131      */
19132     deselectRow : function(index, preventViewNotify){
19133         if(this.locked) return;
19134         if(this.last == index){
19135             this.last = false;
19136         }
19137         if(this.lastActive == index){
19138             this.lastActive = false;
19139         }
19140         var r = this.grid.dataSource.getAt(index);
19141         this.selections.remove(r);
19142         if(!preventViewNotify){
19143             this.grid.getView().onRowDeselect(index);
19144         }
19145         this.fireEvent("rowdeselect", this, index);
19146         this.fireEvent("selectionchange", this);
19147     },
19148
19149     // private
19150     restoreLast : function(){
19151         if(this._last){
19152             this.last = this._last;
19153         }
19154     },
19155
19156     // private
19157     acceptsNav : function(row, col, cm){
19158         return !cm.isHidden(col) && cm.isCellEditable(col, row);
19159     },
19160
19161     // private
19162     onEditorKey : function(field, e){
19163         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19164         if(k == e.TAB){
19165             e.stopEvent();
19166             ed.completeEdit();
19167             if(e.shiftKey){
19168                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19169             }else{
19170                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19171             }
19172         }else if(k == e.ENTER && !e.ctrlKey){
19173             e.stopEvent();
19174             ed.completeEdit();
19175             if(e.shiftKey){
19176                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19177             }else{
19178                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19179             }
19180         }else if(k == e.ESC){
19181             ed.cancelEdit();
19182         }
19183         if(newCell){
19184             g.startEditing(newCell[0], newCell[1]);
19185         }
19186     }
19187 });/*
19188  * Based on:
19189  * Ext JS Library 1.1.1
19190  * Copyright(c) 2006-2007, Ext JS, LLC.
19191  *
19192  * Originally Released Under LGPL - original licence link has changed is not relivant.
19193  *
19194  * Fork - LGPL
19195  * <script type="text/javascript">
19196  */
19197  
19198 /**
19199  * @class Roo.bootstrap.PagingToolbar
19200  * @extends Roo.Row
19201  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19202  * @constructor
19203  * Create a new PagingToolbar
19204  * @param {Object} config The config object
19205  */
19206 Roo.bootstrap.PagingToolbar = function(config)
19207 {
19208     // old args format still supported... - xtype is prefered..
19209         // created from xtype...
19210     var ds = config.dataSource;
19211     this.toolbarItems = [];
19212     if (config.items) {
19213         this.toolbarItems = config.items;
19214 //        config.items = [];
19215     }
19216     
19217     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19218     this.ds = ds;
19219     this.cursor = 0;
19220     if (ds) { 
19221         this.bind(ds);
19222     }
19223     
19224     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19225     
19226 };
19227
19228 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19229     /**
19230      * @cfg {Roo.data.Store} dataSource
19231      * The underlying data store providing the paged data
19232      */
19233     /**
19234      * @cfg {String/HTMLElement/Element} container
19235      * container The id or element that will contain the toolbar
19236      */
19237     /**
19238      * @cfg {Boolean} displayInfo
19239      * True to display the displayMsg (defaults to false)
19240      */
19241     /**
19242      * @cfg {Number} pageSize
19243      * The number of records to display per page (defaults to 20)
19244      */
19245     pageSize: 20,
19246     /**
19247      * @cfg {String} displayMsg
19248      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19249      */
19250     displayMsg : 'Displaying {0} - {1} of {2}',
19251     /**
19252      * @cfg {String} emptyMsg
19253      * The message to display when no records are found (defaults to "No data to display")
19254      */
19255     emptyMsg : 'No data to display',
19256     /**
19257      * Customizable piece of the default paging text (defaults to "Page")
19258      * @type String
19259      */
19260     beforePageText : "Page",
19261     /**
19262      * Customizable piece of the default paging text (defaults to "of %0")
19263      * @type String
19264      */
19265     afterPageText : "of {0}",
19266     /**
19267      * Customizable piece of the default paging text (defaults to "First Page")
19268      * @type String
19269      */
19270     firstText : "First Page",
19271     /**
19272      * Customizable piece of the default paging text (defaults to "Previous Page")
19273      * @type String
19274      */
19275     prevText : "Previous Page",
19276     /**
19277      * Customizable piece of the default paging text (defaults to "Next Page")
19278      * @type String
19279      */
19280     nextText : "Next Page",
19281     /**
19282      * Customizable piece of the default paging text (defaults to "Last Page")
19283      * @type String
19284      */
19285     lastText : "Last Page",
19286     /**
19287      * Customizable piece of the default paging text (defaults to "Refresh")
19288      * @type String
19289      */
19290     refreshText : "Refresh",
19291
19292     buttons : false,
19293     // private
19294     onRender : function(ct, position) 
19295     {
19296         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19297         this.navgroup.parentId = this.id;
19298         this.navgroup.onRender(this.el, null);
19299         // add the buttons to the navgroup
19300         
19301         if(this.displayInfo){
19302             Roo.log(this.el.select('ul.navbar-nav',true).first());
19303             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19304             this.displayEl = this.el.select('.x-paging-info', true).first();
19305 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19306 //            this.displayEl = navel.el.select('span',true).first();
19307         }
19308         
19309         var _this = this;
19310         
19311         if(this.buttons){
19312             Roo.each(_this.buttons, function(e){
19313                Roo.factory(e).onRender(_this.el, null);
19314             });
19315         }
19316             
19317         Roo.each(_this.toolbarItems, function(e) {
19318             _this.navgroup.addItem(e);
19319         });
19320         
19321         this.first = this.navgroup.addItem({
19322             tooltip: this.firstText,
19323             cls: "prev",
19324             icon : 'fa fa-backward',
19325             disabled: true,
19326             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19327         });
19328         
19329         this.prev =  this.navgroup.addItem({
19330             tooltip: this.prevText,
19331             cls: "prev",
19332             icon : 'fa fa-step-backward',
19333             disabled: true,
19334             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
19335         });
19336     //this.addSeparator();
19337         
19338         
19339         var field = this.navgroup.addItem( {
19340             tagtype : 'span',
19341             cls : 'x-paging-position',
19342             
19343             html : this.beforePageText  +
19344                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19345                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
19346          } ); //?? escaped?
19347         
19348         this.field = field.el.select('input', true).first();
19349         this.field.on("keydown", this.onPagingKeydown, this);
19350         this.field.on("focus", function(){this.dom.select();});
19351     
19352     
19353         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
19354         //this.field.setHeight(18);
19355         //this.addSeparator();
19356         this.next = this.navgroup.addItem({
19357             tooltip: this.nextText,
19358             cls: "next",
19359             html : ' <i class="fa fa-step-forward">',
19360             disabled: true,
19361             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
19362         });
19363         this.last = this.navgroup.addItem({
19364             tooltip: this.lastText,
19365             icon : 'fa fa-forward',
19366             cls: "next",
19367             disabled: true,
19368             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
19369         });
19370     //this.addSeparator();
19371         this.loading = this.navgroup.addItem({
19372             tooltip: this.refreshText,
19373             icon: 'fa fa-refresh',
19374             
19375             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19376         });
19377
19378     },
19379
19380     // private
19381     updateInfo : function(){
19382         if(this.displayEl){
19383             var count = this.ds.getCount();
19384             var msg = count == 0 ?
19385                 this.emptyMsg :
19386                 String.format(
19387                     this.displayMsg,
19388                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
19389                 );
19390             this.displayEl.update(msg);
19391         }
19392     },
19393
19394     // private
19395     onLoad : function(ds, r, o){
19396        this.cursor = o.params ? o.params.start : 0;
19397        var d = this.getPageData(),
19398             ap = d.activePage,
19399             ps = d.pages;
19400         
19401        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19402        this.field.dom.value = ap;
19403        this.first.setDisabled(ap == 1);
19404        this.prev.setDisabled(ap == 1);
19405        this.next.setDisabled(ap == ps);
19406        this.last.setDisabled(ap == ps);
19407        this.loading.enable();
19408        this.updateInfo();
19409     },
19410
19411     // private
19412     getPageData : function(){
19413         var total = this.ds.getTotalCount();
19414         return {
19415             total : total,
19416             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19417             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19418         };
19419     },
19420
19421     // private
19422     onLoadError : function(){
19423         this.loading.enable();
19424     },
19425
19426     // private
19427     onPagingKeydown : function(e){
19428         var k = e.getKey();
19429         var d = this.getPageData();
19430         if(k == e.RETURN){
19431             var v = this.field.dom.value, pageNum;
19432             if(!v || isNaN(pageNum = parseInt(v, 10))){
19433                 this.field.dom.value = d.activePage;
19434                 return;
19435             }
19436             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19437             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19438             e.stopEvent();
19439         }
19440         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))
19441         {
19442           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19443           this.field.dom.value = pageNum;
19444           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19445           e.stopEvent();
19446         }
19447         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19448         {
19449           var v = this.field.dom.value, pageNum; 
19450           var increment = (e.shiftKey) ? 10 : 1;
19451           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19452             increment *= -1;
19453           if(!v || isNaN(pageNum = parseInt(v, 10))) {
19454             this.field.dom.value = d.activePage;
19455             return;
19456           }
19457           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19458           {
19459             this.field.dom.value = parseInt(v, 10) + increment;
19460             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19461             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19462           }
19463           e.stopEvent();
19464         }
19465     },
19466
19467     // private
19468     beforeLoad : function(){
19469         if(this.loading){
19470             this.loading.disable();
19471         }
19472     },
19473
19474     // private
19475     onClick : function(which){
19476         var ds = this.ds;
19477         if (!ds) {
19478             return;
19479         }
19480         switch(which){
19481             case "first":
19482                 ds.load({params:{start: 0, limit: this.pageSize}});
19483             break;
19484             case "prev":
19485                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19486             break;
19487             case "next":
19488                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19489             break;
19490             case "last":
19491                 var total = ds.getTotalCount();
19492                 var extra = total % this.pageSize;
19493                 var lastStart = extra ? (total - extra) : total-this.pageSize;
19494                 ds.load({params:{start: lastStart, limit: this.pageSize}});
19495             break;
19496             case "refresh":
19497                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19498             break;
19499         }
19500     },
19501
19502     /**
19503      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19504      * @param {Roo.data.Store} store The data store to unbind
19505      */
19506     unbind : function(ds){
19507         ds.un("beforeload", this.beforeLoad, this);
19508         ds.un("load", this.onLoad, this);
19509         ds.un("loadexception", this.onLoadError, this);
19510         ds.un("remove", this.updateInfo, this);
19511         ds.un("add", this.updateInfo, this);
19512         this.ds = undefined;
19513     },
19514
19515     /**
19516      * Binds the paging toolbar to the specified {@link Roo.data.Store}
19517      * @param {Roo.data.Store} store The data store to bind
19518      */
19519     bind : function(ds){
19520         ds.on("beforeload", this.beforeLoad, this);
19521         ds.on("load", this.onLoad, this);
19522         ds.on("loadexception", this.onLoadError, this);
19523         ds.on("remove", this.updateInfo, this);
19524         ds.on("add", this.updateInfo, this);
19525         this.ds = ds;
19526     }
19527 });/*
19528  * - LGPL
19529  *
19530  * element
19531  * 
19532  */
19533
19534 /**
19535  * @class Roo.bootstrap.MessageBar
19536  * @extends Roo.bootstrap.Component
19537  * Bootstrap MessageBar class
19538  * @cfg {String} html contents of the MessageBar
19539  * @cfg {String} weight (info | success | warning | danger) default info
19540  * @cfg {String} beforeClass insert the bar before the given class
19541  * @cfg {Boolean} closable (true | false) default false
19542  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19543  * 
19544  * @constructor
19545  * Create a new Element
19546  * @param {Object} config The config object
19547  */
19548
19549 Roo.bootstrap.MessageBar = function(config){
19550     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19551 };
19552
19553 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
19554     
19555     html: '',
19556     weight: 'info',
19557     closable: false,
19558     fixed: false,
19559     beforeClass: 'bootstrap-sticky-wrap',
19560     
19561     getAutoCreate : function(){
19562         
19563         var cfg = {
19564             tag: 'div',
19565             cls: 'alert alert-dismissable alert-' + this.weight,
19566             cn: [
19567                 {
19568                     tag: 'span',
19569                     cls: 'message',
19570                     html: this.html || ''
19571                 }
19572             ]
19573         }
19574         
19575         if(this.fixed){
19576             cfg.cls += ' alert-messages-fixed';
19577         }
19578         
19579         if(this.closable){
19580             cfg.cn.push({
19581                 tag: 'button',
19582                 cls: 'close',
19583                 html: 'x'
19584             });
19585         }
19586         
19587         return cfg;
19588     },
19589     
19590     onRender : function(ct, position)
19591     {
19592         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19593         
19594         if(!this.el){
19595             var cfg = Roo.apply({},  this.getAutoCreate());
19596             cfg.id = Roo.id();
19597             
19598             if (this.cls) {
19599                 cfg.cls += ' ' + this.cls;
19600             }
19601             if (this.style) {
19602                 cfg.style = this.style;
19603             }
19604             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19605             
19606             this.el.setVisibilityMode(Roo.Element.DISPLAY);
19607         }
19608         
19609         this.el.select('>button.close').on('click', this.hide, this);
19610         
19611     },
19612     
19613     show : function()
19614     {
19615         if (!this.rendered) {
19616             this.render();
19617         }
19618         
19619         this.el.show();
19620         
19621         this.fireEvent('show', this);
19622         
19623     },
19624     
19625     hide : function()
19626     {
19627         if (!this.rendered) {
19628             this.render();
19629         }
19630         
19631         this.el.hide();
19632         
19633         this.fireEvent('hide', this);
19634     },
19635     
19636     update : function()
19637     {
19638 //        var e = this.el.dom.firstChild;
19639 //        
19640 //        if(this.closable){
19641 //            e = e.nextSibling;
19642 //        }
19643 //        
19644 //        e.data = this.html || '';
19645
19646         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19647     }
19648    
19649 });
19650
19651  
19652
19653      /*
19654  * - LGPL
19655  *
19656  * Graph
19657  * 
19658  */
19659
19660
19661 /**
19662  * @class Roo.bootstrap.Graph
19663  * @extends Roo.bootstrap.Component
19664  * Bootstrap Graph class
19665 > Prameters
19666  -sm {number} sm 4
19667  -md {number} md 5
19668  @cfg {String} graphtype  bar | vbar | pie
19669  @cfg {number} g_x coodinator | centre x (pie)
19670  @cfg {number} g_y coodinator | centre y (pie)
19671  @cfg {number} g_r radius (pie)
19672  @cfg {number} g_height height of the chart (respected by all elements in the set)
19673  @cfg {number} g_width width of the chart (respected by all elements in the set)
19674  @cfg {Object} title The title of the chart
19675     
19676  -{Array}  values
19677  -opts (object) options for the chart 
19678      o {
19679      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19680      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19681      o vgutter (number)
19682      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
19683      o stacked (boolean) whether or not to tread values as in a stacked bar chart
19684      o to
19685      o stretch (boolean)
19686      o }
19687  -opts (object) options for the pie
19688      o{
19689      o cut
19690      o startAngle (number)
19691      o endAngle (number)
19692      } 
19693  *
19694  * @constructor
19695  * Create a new Input
19696  * @param {Object} config The config object
19697  */
19698
19699 Roo.bootstrap.Graph = function(config){
19700     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19701     
19702     this.addEvents({
19703         // img events
19704         /**
19705          * @event click
19706          * The img click event for the img.
19707          * @param {Roo.EventObject} e
19708          */
19709         "click" : true
19710     });
19711 };
19712
19713 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
19714     
19715     sm: 4,
19716     md: 5,
19717     graphtype: 'bar',
19718     g_height: 250,
19719     g_width: 400,
19720     g_x: 50,
19721     g_y: 50,
19722     g_r: 30,
19723     opts:{
19724         //g_colors: this.colors,
19725         g_type: 'soft',
19726         g_gutter: '20%'
19727
19728     },
19729     title : false,
19730
19731     getAutoCreate : function(){
19732         
19733         var cfg = {
19734             tag: 'div',
19735             html : null
19736         }
19737         
19738         
19739         return  cfg;
19740     },
19741
19742     onRender : function(ct,position){
19743         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19744         this.raphael = Raphael(this.el.dom);
19745         
19746                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19747                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19748                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19749                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19750                 /*
19751                 r.text(160, 10, "Single Series Chart").attr(txtattr);
19752                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19753                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19754                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19755                 
19756                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19757                 r.barchart(330, 10, 300, 220, data1);
19758                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19759                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19760                 */
19761                 
19762                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19763                 // r.barchart(30, 30, 560, 250,  xdata, {
19764                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19765                 //     axis : "0 0 1 1",
19766                 //     axisxlabels :  xdata
19767                 //     //yvalues : cols,
19768                    
19769                 // });
19770 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19771 //        
19772 //        this.load(null,xdata,{
19773 //                axis : "0 0 1 1",
19774 //                axisxlabels :  xdata
19775 //                });
19776
19777     },
19778
19779     load : function(graphtype,xdata,opts){
19780         this.raphael.clear();
19781         if(!graphtype) {
19782             graphtype = this.graphtype;
19783         }
19784         if(!opts){
19785             opts = this.opts;
19786         }
19787         var r = this.raphael,
19788             fin = function () {
19789                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19790             },
19791             fout = function () {
19792                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19793             },
19794             pfin = function() {
19795                 this.sector.stop();
19796                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19797
19798                 if (this.label) {
19799                     this.label[0].stop();
19800                     this.label[0].attr({ r: 7.5 });
19801                     this.label[1].attr({ "font-weight": 800 });
19802                 }
19803             },
19804             pfout = function() {
19805                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19806
19807                 if (this.label) {
19808                     this.label[0].animate({ r: 5 }, 500, "bounce");
19809                     this.label[1].attr({ "font-weight": 400 });
19810                 }
19811             };
19812
19813         switch(graphtype){
19814             case 'bar':
19815                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19816                 break;
19817             case 'hbar':
19818                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19819                 break;
19820             case 'pie':
19821 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
19822 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19823 //            
19824                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19825                 
19826                 break;
19827
19828         }
19829         
19830         if(this.title){
19831             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19832         }
19833         
19834     },
19835     
19836     setTitle: function(o)
19837     {
19838         this.title = o;
19839     },
19840     
19841     initEvents: function() {
19842         
19843         if(!this.href){
19844             this.el.on('click', this.onClick, this);
19845         }
19846     },
19847     
19848     onClick : function(e)
19849     {
19850         Roo.log('img onclick');
19851         this.fireEvent('click', this, e);
19852     }
19853    
19854 });
19855
19856  
19857 /*
19858  * - LGPL
19859  *
19860  * numberBox
19861  * 
19862  */
19863 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19864
19865 /**
19866  * @class Roo.bootstrap.dash.NumberBox
19867  * @extends Roo.bootstrap.Component
19868  * Bootstrap NumberBox class
19869  * @cfg {String} headline Box headline
19870  * @cfg {String} content Box content
19871  * @cfg {String} icon Box icon
19872  * @cfg {String} footer Footer text
19873  * @cfg {String} fhref Footer href
19874  * 
19875  * @constructor
19876  * Create a new NumberBox
19877  * @param {Object} config The config object
19878  */
19879
19880
19881 Roo.bootstrap.dash.NumberBox = function(config){
19882     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19883     
19884 };
19885
19886 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19887     
19888     headline : '',
19889     content : '',
19890     icon : '',
19891     footer : '',
19892     fhref : '',
19893     ficon : '',
19894     
19895     getAutoCreate : function(){
19896         
19897         var cfg = {
19898             tag : 'div',
19899             cls : 'small-box ',
19900             cn : [
19901                 {
19902                     tag : 'div',
19903                     cls : 'inner',
19904                     cn :[
19905                         {
19906                             tag : 'h3',
19907                             cls : 'roo-headline',
19908                             html : this.headline
19909                         },
19910                         {
19911                             tag : 'p',
19912                             cls : 'roo-content',
19913                             html : this.content
19914                         }
19915                     ]
19916                 }
19917             ]
19918         }
19919         
19920         if(this.icon){
19921             cfg.cn.push({
19922                 tag : 'div',
19923                 cls : 'icon',
19924                 cn :[
19925                     {
19926                         tag : 'i',
19927                         cls : 'ion ' + this.icon
19928                     }
19929                 ]
19930             });
19931         }
19932         
19933         if(this.footer){
19934             var footer = {
19935                 tag : 'a',
19936                 cls : 'small-box-footer',
19937                 href : this.fhref || '#',
19938                 html : this.footer
19939             };
19940             
19941             cfg.cn.push(footer);
19942             
19943         }
19944         
19945         return  cfg;
19946     },
19947
19948     onRender : function(ct,position){
19949         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19950
19951
19952        
19953                 
19954     },
19955
19956     setHeadline: function (value)
19957     {
19958         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19959     },
19960     
19961     setFooter: function (value, href)
19962     {
19963         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19964         
19965         if(href){
19966             this.el.select('a.small-box-footer',true).first().attr('href', href);
19967         }
19968         
19969     },
19970
19971     setContent: function (value)
19972     {
19973         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19974     },
19975
19976     initEvents: function() 
19977     {   
19978         
19979     }
19980     
19981 });
19982
19983  
19984 /*
19985  * - LGPL
19986  *
19987  * TabBox
19988  * 
19989  */
19990 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19991
19992 /**
19993  * @class Roo.bootstrap.dash.TabBox
19994  * @extends Roo.bootstrap.Component
19995  * Bootstrap TabBox class
19996  * @cfg {String} title Title of the TabBox
19997  * @cfg {String} icon Icon of the TabBox
19998  * @cfg {Boolean} showtabs (true|false) show the tabs default true
19999  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
20000  * 
20001  * @constructor
20002  * Create a new TabBox
20003  * @param {Object} config The config object
20004  */
20005
20006
20007 Roo.bootstrap.dash.TabBox = function(config){
20008     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
20009     this.addEvents({
20010         // raw events
20011         /**
20012          * @event addpane
20013          * When a pane is added
20014          * @param {Roo.bootstrap.dash.TabPane} pane
20015          */
20016         "addpane" : true,
20017         /**
20018          * @event activatepane
20019          * When a pane is activated
20020          * @param {Roo.bootstrap.dash.TabPane} pane
20021          */
20022         "activatepane" : true
20023         
20024          
20025     });
20026     
20027     this.panes = [];
20028 };
20029
20030 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
20031
20032     title : '',
20033     icon : false,
20034     showtabs : true,
20035     tabScrollable : false,
20036     
20037     getChildContainer : function()
20038     {
20039         return this.el.select('.tab-content', true).first();
20040     },
20041     
20042     getAutoCreate : function(){
20043         
20044         var header = {
20045             tag: 'li',
20046             cls: 'pull-left header',
20047             html: this.title,
20048             cn : []
20049         };
20050         
20051         if(this.icon){
20052             header.cn.push({
20053                 tag: 'i',
20054                 cls: 'fa ' + this.icon
20055             });
20056         }
20057         
20058         var h = {
20059             tag: 'ul',
20060             cls: 'nav nav-tabs pull-right',
20061             cn: [
20062                 header
20063             ]
20064         };
20065         
20066         if(this.tabScrollable){
20067             h = {
20068                 tag: 'div',
20069                 cls: 'tab-header',
20070                 cn: [
20071                     {
20072                         tag: 'ul',
20073                         cls: 'nav nav-tabs pull-right',
20074                         cn: [
20075                             header
20076                         ]
20077                     }
20078                 ]
20079             }
20080         }
20081         
20082         var cfg = {
20083             tag: 'div',
20084             cls: 'nav-tabs-custom',
20085             cn: [
20086                 h,
20087                 {
20088                     tag: 'div',
20089                     cls: 'tab-content no-padding',
20090                     cn: []
20091                 }
20092             ]
20093         }
20094
20095         return  cfg;
20096     },
20097     initEvents : function()
20098     {
20099         //Roo.log('add add pane handler');
20100         this.on('addpane', this.onAddPane, this);
20101     },
20102      /**
20103      * Updates the box title
20104      * @param {String} html to set the title to.
20105      */
20106     setTitle : function(value)
20107     {
20108         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20109     },
20110     onAddPane : function(pane)
20111     {
20112         this.panes.push(pane);
20113         //Roo.log('addpane');
20114         //Roo.log(pane);
20115         // tabs are rendere left to right..
20116         if(!this.showtabs){
20117             return;
20118         }
20119         
20120         var ctr = this.el.select('.nav-tabs', true).first();
20121          
20122          
20123         var existing = ctr.select('.nav-tab',true);
20124         var qty = existing.getCount();;
20125         
20126         
20127         var tab = ctr.createChild({
20128             tag : 'li',
20129             cls : 'nav-tab' + (qty ? '' : ' active'),
20130             cn : [
20131                 {
20132                     tag : 'a',
20133                     href:'#',
20134                     html : pane.title
20135                 }
20136             ]
20137         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20138         pane.tab = tab;
20139         
20140         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20141         if (!qty) {
20142             pane.el.addClass('active');
20143         }
20144         
20145                 
20146     },
20147     onTabClick : function(ev,un,ob,pane)
20148     {
20149         //Roo.log('tab - prev default');
20150         ev.preventDefault();
20151         
20152         
20153         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20154         pane.tab.addClass('active');
20155         //Roo.log(pane.title);
20156         this.getChildContainer().select('.tab-pane',true).removeClass('active');
20157         // technically we should have a deactivate event.. but maybe add later.
20158         // and it should not de-activate the selected tab...
20159         this.fireEvent('activatepane', pane);
20160         pane.el.addClass('active');
20161         pane.fireEvent('activate');
20162         
20163         
20164     },
20165     
20166     getActivePane : function()
20167     {
20168         var r = false;
20169         Roo.each(this.panes, function(p) {
20170             if(p.el.hasClass('active')){
20171                 r = p;
20172                 return false;
20173             }
20174             
20175             return;
20176         });
20177         
20178         return r;
20179     }
20180     
20181     
20182 });
20183
20184  
20185 /*
20186  * - LGPL
20187  *
20188  * Tab pane
20189  * 
20190  */
20191 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20192 /**
20193  * @class Roo.bootstrap.TabPane
20194  * @extends Roo.bootstrap.Component
20195  * Bootstrap TabPane class
20196  * @cfg {Boolean} active (false | true) Default false
20197  * @cfg {String} title title of panel
20198
20199  * 
20200  * @constructor
20201  * Create a new TabPane
20202  * @param {Object} config The config object
20203  */
20204
20205 Roo.bootstrap.dash.TabPane = function(config){
20206     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20207     
20208     this.addEvents({
20209         // raw events
20210         /**
20211          * @event activate
20212          * When a pane is activated
20213          * @param {Roo.bootstrap.dash.TabPane} pane
20214          */
20215         "activate" : true
20216          
20217     });
20218 };
20219
20220 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
20221     
20222     active : false,
20223     title : '',
20224     
20225     // the tabBox that this is attached to.
20226     tab : false,
20227      
20228     getAutoCreate : function() 
20229     {
20230         var cfg = {
20231             tag: 'div',
20232             cls: 'tab-pane'
20233         }
20234         
20235         if(this.active){
20236             cfg.cls += ' active';
20237         }
20238         
20239         return cfg;
20240     },
20241     initEvents  : function()
20242     {
20243         //Roo.log('trigger add pane handler');
20244         this.parent().fireEvent('addpane', this)
20245     },
20246     
20247      /**
20248      * Updates the tab title 
20249      * @param {String} html to set the title to.
20250      */
20251     setTitle: function(str)
20252     {
20253         if (!this.tab) {
20254             return;
20255         }
20256         this.title = str;
20257         this.tab.select('a', true).first().dom.innerHTML = str;
20258         
20259     }
20260     
20261     
20262     
20263 });
20264
20265  
20266
20267
20268  /*
20269  * - LGPL
20270  *
20271  * menu
20272  * 
20273  */
20274 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20275
20276 /**
20277  * @class Roo.bootstrap.menu.Menu
20278  * @extends Roo.bootstrap.Component
20279  * Bootstrap Menu class - container for Menu
20280  * @cfg {String} html Text of the menu
20281  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20282  * @cfg {String} icon Font awesome icon
20283  * @cfg {String} pos Menu align to (top | bottom) default bottom
20284  * 
20285  * 
20286  * @constructor
20287  * Create a new Menu
20288  * @param {Object} config The config object
20289  */
20290
20291
20292 Roo.bootstrap.menu.Menu = function(config){
20293     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20294     
20295     this.addEvents({
20296         /**
20297          * @event beforeshow
20298          * Fires before this menu is displayed
20299          * @param {Roo.bootstrap.menu.Menu} this
20300          */
20301         beforeshow : true,
20302         /**
20303          * @event beforehide
20304          * Fires before this menu is hidden
20305          * @param {Roo.bootstrap.menu.Menu} this
20306          */
20307         beforehide : true,
20308         /**
20309          * @event show
20310          * Fires after this menu is displayed
20311          * @param {Roo.bootstrap.menu.Menu} this
20312          */
20313         show : true,
20314         /**
20315          * @event hide
20316          * Fires after this menu is hidden
20317          * @param {Roo.bootstrap.menu.Menu} this
20318          */
20319         hide : true,
20320         /**
20321          * @event click
20322          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20323          * @param {Roo.bootstrap.menu.Menu} this
20324          * @param {Roo.EventObject} e
20325          */
20326         click : true
20327     });
20328     
20329 };
20330
20331 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
20332     
20333     submenu : false,
20334     html : '',
20335     weight : 'default',
20336     icon : false,
20337     pos : 'bottom',
20338     
20339     
20340     getChildContainer : function() {
20341         if(this.isSubMenu){
20342             return this.el;
20343         }
20344         
20345         return this.el.select('ul.dropdown-menu', true).first();  
20346     },
20347     
20348     getAutoCreate : function()
20349     {
20350         var text = [
20351             {
20352                 tag : 'span',
20353                 cls : 'roo-menu-text',
20354                 html : this.html
20355             }
20356         ];
20357         
20358         if(this.icon){
20359             text.unshift({
20360                 tag : 'i',
20361                 cls : 'fa ' + this.icon
20362             })
20363         }
20364         
20365         
20366         var cfg = {
20367             tag : 'div',
20368             cls : 'btn-group',
20369             cn : [
20370                 {
20371                     tag : 'button',
20372                     cls : 'dropdown-button btn btn-' + this.weight,
20373                     cn : text
20374                 },
20375                 {
20376                     tag : 'button',
20377                     cls : 'dropdown-toggle btn btn-' + this.weight,
20378                     cn : [
20379                         {
20380                             tag : 'span',
20381                             cls : 'caret'
20382                         }
20383                     ]
20384                 },
20385                 {
20386                     tag : 'ul',
20387                     cls : 'dropdown-menu'
20388                 }
20389             ]
20390             
20391         };
20392         
20393         if(this.pos == 'top'){
20394             cfg.cls += ' dropup';
20395         }
20396         
20397         if(this.isSubMenu){
20398             cfg = {
20399                 tag : 'ul',
20400                 cls : 'dropdown-menu'
20401             }
20402         }
20403         
20404         return cfg;
20405     },
20406     
20407     onRender : function(ct, position)
20408     {
20409         this.isSubMenu = ct.hasClass('dropdown-submenu');
20410         
20411         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20412     },
20413     
20414     initEvents : function() 
20415     {
20416         if(this.isSubMenu){
20417             return;
20418         }
20419         
20420         this.hidden = true;
20421         
20422         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20423         this.triggerEl.on('click', this.onTriggerPress, this);
20424         
20425         this.buttonEl = this.el.select('button.dropdown-button', true).first();
20426         this.buttonEl.on('click', this.onClick, this);
20427         
20428     },
20429     
20430     list : function()
20431     {
20432         if(this.isSubMenu){
20433             return this.el;
20434         }
20435         
20436         return this.el.select('ul.dropdown-menu', true).first();
20437     },
20438     
20439     onClick : function(e)
20440     {
20441         this.fireEvent("click", this, e);
20442     },
20443     
20444     onTriggerPress  : function(e)
20445     {   
20446         if (this.isVisible()) {
20447             this.hide();
20448         } else {
20449             this.show();
20450         }
20451     },
20452     
20453     isVisible : function(){
20454         return !this.hidden;
20455     },
20456     
20457     show : function()
20458     {
20459         this.fireEvent("beforeshow", this);
20460         
20461         this.hidden = false;
20462         this.el.addClass('open');
20463         
20464         Roo.get(document).on("mouseup", this.onMouseUp, this);
20465         
20466         this.fireEvent("show", this);
20467         
20468         
20469     },
20470     
20471     hide : function()
20472     {
20473         this.fireEvent("beforehide", this);
20474         
20475         this.hidden = true;
20476         this.el.removeClass('open');
20477         
20478         Roo.get(document).un("mouseup", this.onMouseUp);
20479         
20480         this.fireEvent("hide", this);
20481     },
20482     
20483     onMouseUp : function()
20484     {
20485         this.hide();
20486     }
20487     
20488 });
20489
20490  
20491  /*
20492  * - LGPL
20493  *
20494  * menu item
20495  * 
20496  */
20497 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20498
20499 /**
20500  * @class Roo.bootstrap.menu.Item
20501  * @extends Roo.bootstrap.Component
20502  * Bootstrap MenuItem class
20503  * @cfg {Boolean} submenu (true | false) default false
20504  * @cfg {String} html text of the item
20505  * @cfg {String} href the link
20506  * @cfg {Boolean} disable (true | false) default false
20507  * @cfg {Boolean} preventDefault (true | false) default true
20508  * @cfg {String} icon Font awesome icon
20509  * @cfg {String} pos Submenu align to (left | right) default right 
20510  * 
20511  * 
20512  * @constructor
20513  * Create a new Item
20514  * @param {Object} config The config object
20515  */
20516
20517
20518 Roo.bootstrap.menu.Item = function(config){
20519     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20520     this.addEvents({
20521         /**
20522          * @event mouseover
20523          * Fires when the mouse is hovering over this menu
20524          * @param {Roo.bootstrap.menu.Item} this
20525          * @param {Roo.EventObject} e
20526          */
20527         mouseover : true,
20528         /**
20529          * @event mouseout
20530          * Fires when the mouse exits this menu
20531          * @param {Roo.bootstrap.menu.Item} this
20532          * @param {Roo.EventObject} e
20533          */
20534         mouseout : true,
20535         // raw events
20536         /**
20537          * @event click
20538          * The raw click event for the entire grid.
20539          * @param {Roo.EventObject} e
20540          */
20541         click : true
20542     });
20543 };
20544
20545 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
20546     
20547     submenu : false,
20548     href : '',
20549     html : '',
20550     preventDefault: true,
20551     disable : false,
20552     icon : false,
20553     pos : 'right',
20554     
20555     getAutoCreate : function()
20556     {
20557         var text = [
20558             {
20559                 tag : 'span',
20560                 cls : 'roo-menu-item-text',
20561                 html : this.html
20562             }
20563         ];
20564         
20565         if(this.icon){
20566             text.unshift({
20567                 tag : 'i',
20568                 cls : 'fa ' + this.icon
20569             })
20570         }
20571         
20572         var cfg = {
20573             tag : 'li',
20574             cn : [
20575                 {
20576                     tag : 'a',
20577                     href : this.href || '#',
20578                     cn : text
20579                 }
20580             ]
20581         };
20582         
20583         if(this.disable){
20584             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20585         }
20586         
20587         if(this.submenu){
20588             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20589             
20590             if(this.pos == 'left'){
20591                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20592             }
20593         }
20594         
20595         return cfg;
20596     },
20597     
20598     initEvents : function() 
20599     {
20600         this.el.on('mouseover', this.onMouseOver, this);
20601         this.el.on('mouseout', this.onMouseOut, this);
20602         
20603         this.el.select('a', true).first().on('click', this.onClick, this);
20604         
20605     },
20606     
20607     onClick : function(e)
20608     {
20609         if(this.preventDefault){
20610             e.preventDefault();
20611         }
20612         
20613         this.fireEvent("click", this, e);
20614     },
20615     
20616     onMouseOver : function(e)
20617     {
20618         if(this.submenu && this.pos == 'left'){
20619             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20620         }
20621         
20622         this.fireEvent("mouseover", this, e);
20623     },
20624     
20625     onMouseOut : function(e)
20626     {
20627         this.fireEvent("mouseout", this, e);
20628     }
20629 });
20630
20631  
20632
20633  /*
20634  * - LGPL
20635  *
20636  * menu separator
20637  * 
20638  */
20639 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20640
20641 /**
20642  * @class Roo.bootstrap.menu.Separator
20643  * @extends Roo.bootstrap.Component
20644  * Bootstrap Separator class
20645  * 
20646  * @constructor
20647  * Create a new Separator
20648  * @param {Object} config The config object
20649  */
20650
20651
20652 Roo.bootstrap.menu.Separator = function(config){
20653     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20654 };
20655
20656 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
20657     
20658     getAutoCreate : function(){
20659         var cfg = {
20660             tag : 'li',
20661             cls: 'divider'
20662         };
20663         
20664         return cfg;
20665     }
20666    
20667 });
20668
20669  
20670
20671  /*
20672  * - LGPL
20673  *
20674  * Tooltip
20675  * 
20676  */
20677
20678 /**
20679  * @class Roo.bootstrap.Tooltip
20680  * Bootstrap Tooltip class
20681  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20682  * to determine which dom element triggers the tooltip.
20683  * 
20684  * It needs to add support for additional attributes like tooltip-position
20685  * 
20686  * @constructor
20687  * Create a new Toolti
20688  * @param {Object} config The config object
20689  */
20690
20691 Roo.bootstrap.Tooltip = function(config){
20692     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20693 };
20694
20695 Roo.apply(Roo.bootstrap.Tooltip, {
20696     /**
20697      * @function init initialize tooltip monitoring.
20698      * @static
20699      */
20700     currentEl : false,
20701     currentTip : false,
20702     currentRegion : false,
20703     
20704     //  init : delay?
20705     
20706     init : function()
20707     {
20708         Roo.get(document).on('mouseover', this.enter ,this);
20709         Roo.get(document).on('mouseout', this.leave, this);
20710          
20711         
20712         this.currentTip = new Roo.bootstrap.Tooltip();
20713     },
20714     
20715     enter : function(ev)
20716     {
20717         var dom = ev.getTarget();
20718         //Roo.log(['enter',dom]);
20719         var el = Roo.fly(dom);
20720         if (this.currentEl) {
20721             //Roo.log(dom);
20722             //Roo.log(this.currentEl);
20723             //Roo.log(this.currentEl.contains(dom));
20724             if (this.currentEl == el) {
20725                 return;
20726             }
20727             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20728                 return;
20729             }
20730
20731         }
20732         
20733         
20734         
20735         if (this.currentTip.el) {
20736             this.currentTip.el.hide(); // force hiding...
20737         }    
20738         //Roo.log(el);
20739         if (!el.attr('tooltip')) { // parents who have tip?
20740             return;
20741         }
20742         this.currentEl = el;
20743         this.currentTip.bind(el);
20744         this.currentRegion = Roo.lib.Region.getRegion(dom);
20745         this.currentTip.enter();
20746         
20747     },
20748     leave : function(ev)
20749     {
20750         var dom = ev.getTarget();
20751         //Roo.log(['leave',dom]);
20752         if (!this.currentEl) {
20753             return;
20754         }
20755         
20756         
20757         if (dom != this.currentEl.dom) {
20758             return;
20759         }
20760         var xy = ev.getXY();
20761         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
20762             return;
20763         }
20764         // only activate leave if mouse cursor is outside... bounding box..
20765         
20766         
20767         
20768         
20769         if (this.currentTip) {
20770             this.currentTip.leave();
20771         }
20772         //Roo.log('clear currentEl');
20773         this.currentEl = false;
20774         
20775         
20776     },
20777     alignment : {
20778         'left' : ['r-l', [-2,0], 'right'],
20779         'right' : ['l-r', [2,0], 'left'],
20780         'bottom' : ['t-b', [0,2], 'top'],
20781         'top' : [ 'b-t', [0,-2], 'bottom']
20782     }
20783     
20784 });
20785
20786
20787 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
20788     
20789     
20790     bindEl : false,
20791     
20792     delay : null, // can be { show : 300 , hide: 500}
20793     
20794     timeout : null,
20795     
20796     hoverState : null, //???
20797     
20798     placement : 'bottom', 
20799     
20800     getAutoCreate : function(){
20801     
20802         var cfg = {
20803            cls : 'tooltip',
20804            role : 'tooltip',
20805            cn : [
20806                 {
20807                     cls : 'tooltip-arrow'
20808                 },
20809                 {
20810                     cls : 'tooltip-inner'
20811                 }
20812            ]
20813         };
20814         
20815         return cfg;
20816     },
20817     bind : function(el)
20818     {
20819         this.bindEl = el;
20820     },
20821       
20822     
20823     enter : function () {
20824        
20825         if (this.timeout != null) {
20826             clearTimeout(this.timeout);
20827         }
20828         
20829         this.hoverState = 'in'
20830          //Roo.log("enter - show");
20831         if (!this.delay || !this.delay.show) {
20832             this.show();
20833             return;
20834         }
20835         var _t = this;
20836         this.timeout = setTimeout(function () {
20837             if (_t.hoverState == 'in') {
20838                 _t.show();
20839             }
20840         }, this.delay.show);
20841     },
20842     leave : function()
20843     {
20844         clearTimeout(this.timeout);
20845     
20846         this.hoverState = 'out'
20847          if (!this.delay || !this.delay.hide) {
20848             this.hide();
20849             return 
20850         }
20851        
20852         var _t = this;
20853         this.timeout = setTimeout(function () {
20854             //Roo.log("leave - timeout");
20855             
20856             if (_t.hoverState == 'out') {
20857                 _t.hide();
20858                 Roo.bootstrap.Tooltip.currentEl = false;
20859             }
20860         }, delay)
20861     },
20862     
20863     show : function ()
20864     {
20865         if (!this.el) {
20866             this.render(document.body);
20867         }
20868         // set content.
20869         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20870         this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20871         
20872         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20873         
20874         var placement = typeof this.placement == 'function' ?
20875             this.placement.call(this, this.el, on_el) :
20876             this.placement;
20877             
20878         var autoToken = /\s?auto?\s?/i;
20879         var autoPlace = autoToken.test(placement);
20880         if (autoPlace) {
20881             placement = placement.replace(autoToken, '') || 'top';
20882         }
20883         
20884         //this.el.detach()
20885         //this.el.setXY([0,0]);
20886         this.el.show();
20887         //this.el.dom.style.display='block';
20888         this.el.addClass(placement);
20889         
20890         //this.el.appendTo(on_el);
20891         
20892         var p = this.getPosition();
20893         var box = this.el.getBox();
20894         
20895         if (autoPlace) {
20896             // fixme..
20897         }
20898         var align = Roo.bootstrap.Tooltip.alignment[placement]
20899         this.el.alignTo(this.bindEl, align[0],align[1]);
20900         //var arrow = this.el.select('.arrow',true).first();
20901         //arrow.set(align[2], 
20902         
20903         this.el.addClass('in fade');
20904         this.hoverState = null;
20905         
20906         if (this.el.hasClass('fade')) {
20907             // fade it?
20908         }
20909         
20910     },
20911     hide : function()
20912     {
20913          
20914         if (!this.el) {
20915             return;
20916         }
20917         //this.el.setXY([0,0]);
20918         this.el.removeClass('in');
20919         //this.el.hide();
20920         
20921     }
20922     
20923 });
20924  
20925
20926  /*
20927  * - LGPL
20928  *
20929  * Location Picker
20930  * 
20931  */
20932
20933 /**
20934  * @class Roo.bootstrap.LocationPicker
20935  * @extends Roo.bootstrap.Component
20936  * Bootstrap LocationPicker class
20937  * @cfg {Number} latitude Position when init default 0
20938  * @cfg {Number} longitude Position when init default 0
20939  * @cfg {Number} zoom default 15
20940  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
20941  * @cfg {Boolean} mapTypeControl default false
20942  * @cfg {Boolean} disableDoubleClickZoom default false
20943  * @cfg {Boolean} scrollwheel default true
20944  * @cfg {Boolean} streetViewControl default false
20945  * @cfg {Number} radius default 0
20946  * @cfg {String} locationName
20947  * @cfg {Boolean} draggable default true
20948  * @cfg {Boolean} enableAutocomplete default false
20949  * @cfg {Boolean} enableReverseGeocode default true
20950  * @cfg {String} markerTitle
20951  * 
20952  * @constructor
20953  * Create a new LocationPicker
20954  * @param {Object} config The config object
20955  */
20956
20957
20958 Roo.bootstrap.LocationPicker = function(config){
20959     
20960     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
20961     
20962      this.addEvents({
20963             /**
20964              * @event initial
20965              * Fires when the picker initialized.
20966              * @param {Roo.bootstrap.LocationPicker} this
20967              * @param {Google Location} location
20968              */
20969             initial : true,
20970             /**
20971              * @event positionchanged
20972              * Fires when the picker position changed.
20973              * @param {Roo.bootstrap.LocationPicker} this
20974              * @param {Google Location} location
20975              */
20976             positionchanged : true,
20977             /**
20978              * @event resize
20979              * Fires when the map resize.
20980              * @param {Roo.bootstrap.LocationPicker} this
20981              */
20982             resize : true,
20983             /**
20984              * @event show
20985              * Fires when the map show.
20986              * @param {Roo.bootstrap.LocationPicker} this
20987              */
20988             show : true,
20989             /**
20990              * @event hide
20991              * Fires when the map hide.
20992              * @param {Roo.bootstrap.LocationPicker} this
20993              */
20994             hide : true
20995         });
20996         
20997 };
20998
20999 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
21000     
21001     gMapContext: false,
21002     
21003     latitude: 0,
21004     longitude: 0,
21005     zoom: 15,
21006     mapTypeId: false,
21007     mapTypeControl: false,
21008     disableDoubleClickZoom: false,
21009     scrollwheel: true,
21010     streetViewControl: false,
21011     radius: 0,
21012     locationName: '',
21013     draggable: true,
21014     enableAutocomplete: false,
21015     enableReverseGeocode: true,
21016     markerTitle: '',
21017     
21018     getAutoCreate: function()
21019     {
21020
21021         var cfg = {
21022             tag: 'div',
21023             cls: 'roo-location-picker'
21024         };
21025         
21026         return cfg
21027     },
21028     
21029     initEvents: function(ct, position)
21030     {   
21031         if(!this.mapTypeId){
21032             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
21033         }
21034             
21035         if(!this.el.getWidth() || this.isApplied()){
21036             return;
21037         }
21038         
21039         this.el.setVisibilityMode(Roo.Element.DISPLAY);
21040         
21041         this.initial();
21042     },
21043     
21044     initial: function()
21045     {
21046         this.gMapContext = this.GMapContext();
21047         
21048         var _this = this;
21049         
21050         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
21051             _this.setPosition(_this.gMapContext.marker.position);
21052         });
21053         
21054         this.setPosition(this.gMapContext.location);
21055         
21056         this.fireEvent('initial', this, this.gMapContext.location);
21057     },
21058     
21059     isApplied: function() 
21060     {
21061         return this.getGmapContext() == false ? false : true;
21062     },
21063     
21064     getGmapContext: function() 
21065     {
21066         return this.gMapContext
21067     },
21068     
21069     GMapContext: function() 
21070     {
21071         var _map = new google.maps.Map(this.el.dom, this);
21072         var _marker = new google.maps.Marker({
21073             position: new google.maps.LatLng(this.latitude, this.longitude),
21074             map: _map,
21075             title: this.markerTitle,
21076             draggable: this.draggable
21077         });
21078         
21079         return {
21080             map: _map,
21081             marker: _marker,
21082             circle: null,
21083             location: _marker.position,
21084             radius: this.radius,
21085             locationName: this.locationName,
21086             addressComponents: {
21087                 formatted_address: null,
21088                 addressLine1: null,
21089                 addressLine2: null,
21090                 streetName: null,
21091                 streetNumber: null,
21092                 city: null,
21093                 district: null,
21094                 state: null,
21095                 stateOrProvince: null
21096             },
21097             settings: this,
21098             domContainer: this.el.dom,
21099             geodecoder: new google.maps.Geocoder()
21100         };
21101     },
21102     
21103     drawCircle: function(center, radius, options) 
21104     {
21105         if (this.gMapContext.circle != null) {
21106             this.gMapContext.circle.setMap(null);
21107         }
21108         if (radius > 0) {
21109             radius *= 1;
21110             options = Roo.apply({}, options, {
21111                 strokeColor: "#0000FF",
21112                 strokeOpacity: .35,
21113                 strokeWeight: 2,
21114                 fillColor: "#0000FF",
21115                 fillOpacity: .2
21116             });
21117             
21118             options.map = this.gMapContext.map;
21119             options.radius = radius;
21120             options.center = center;
21121             this.gMapContext.circle = new google.maps.Circle(options);
21122             return this.gMapContext.circle;
21123         }
21124         
21125         return null;
21126     },
21127     
21128     setPosition: function(location) 
21129     {
21130         this.gMapContext.location = location;
21131         this.gMapContext.marker.setPosition(location);
21132         this.gMapContext.map.panTo(location);
21133         this.drawCircle(location, this.gMapContext.radius, {});
21134         
21135         var _this = this;
21136         
21137         if (this.gMapContext.settings.enableReverseGeocode) {
21138             this.gMapContext.geodecoder.geocode({
21139                 latLng: this.gMapContext.location
21140             }, function(results, status) {
21141                 
21142                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
21143                     _this.gMapContext.locationName = results[0].formatted_address;
21144                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
21145                     
21146                     _this.fireEvent('positionchanged', this, location);
21147                 }
21148             });
21149             
21150             return;
21151         }
21152         
21153         this.fireEvent('positionchanged', this, location);
21154     },
21155     
21156     resize: function()
21157     {
21158         google.maps.event.trigger(this.gMapContext.map, "resize");
21159         
21160         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
21161         
21162         this.fireEvent('resize', this);
21163     },
21164     
21165     setPositionByLatLng: function(latitude, longitude)
21166     {
21167         this.setPosition(new google.maps.LatLng(latitude, longitude));
21168     },
21169     
21170     getCurrentPosition: function() 
21171     {
21172         return {
21173             latitude: this.gMapContext.location.lat(),
21174             longitude: this.gMapContext.location.lng()
21175         };
21176     },
21177     
21178     getAddressName: function() 
21179     {
21180         return this.gMapContext.locationName;
21181     },
21182     
21183     getAddressComponents: function() 
21184     {
21185         return this.gMapContext.addressComponents;
21186     },
21187     
21188     address_component_from_google_geocode: function(address_components) 
21189     {
21190         var result = {};
21191         
21192         for (var i = 0; i < address_components.length; i++) {
21193             var component = address_components[i];
21194             if (component.types.indexOf("postal_code") >= 0) {
21195                 result.postalCode = component.short_name;
21196             } else if (component.types.indexOf("street_number") >= 0) {
21197                 result.streetNumber = component.short_name;
21198             } else if (component.types.indexOf("route") >= 0) {
21199                 result.streetName = component.short_name;
21200             } else if (component.types.indexOf("neighborhood") >= 0) {
21201                 result.city = component.short_name;
21202             } else if (component.types.indexOf("locality") >= 0) {
21203                 result.city = component.short_name;
21204             } else if (component.types.indexOf("sublocality") >= 0) {
21205                 result.district = component.short_name;
21206             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
21207                 result.stateOrProvince = component.short_name;
21208             } else if (component.types.indexOf("country") >= 0) {
21209                 result.country = component.short_name;
21210             }
21211         }
21212         
21213         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
21214         result.addressLine2 = "";
21215         return result;
21216     },
21217     
21218     setZoomLevel: function(zoom)
21219     {
21220         this.gMapContext.map.setZoom(zoom);
21221     },
21222     
21223     show: function()
21224     {
21225         if(!this.el){
21226             return;
21227         }
21228         
21229         this.el.show();
21230         
21231         this.resize();
21232         
21233         this.fireEvent('show', this);
21234     },
21235     
21236     hide: function()
21237     {
21238         if(!this.el){
21239             return;
21240         }
21241         
21242         this.el.hide();
21243         
21244         this.fireEvent('hide', this);
21245     }
21246     
21247 });